From 89bc1902ed3db2932d007b68d65eb5ba78fe0385 Mon Sep 17 00:00:00 2001 From: danidfra Date: Fri, 28 Feb 2025 07:24:14 -0300 Subject: [PATCH] Improve filter tags --- src/features/explorer/components/filters.tsx | 62 ++++++++++++-------- src/locales/en.json | 2 +- src/reducers/search-filter.ts | 20 ++++--- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/features/explorer/components/filters.tsx b/src/features/explorer/components/filters.tsx index fb5deda32..fe9b3e9fd 100644 --- a/src/features/explorer/components/filters.tsx +++ b/src/features/explorer/components/filters.tsx @@ -1,7 +1,7 @@ import searchIcon from '@tabler/icons/outline/search.svg'; import xIcon from '@tabler/icons/outline/x.svg'; import clsx from 'clsx'; -import { useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import Button from 'soapbox/components/ui/button.tsx'; @@ -17,7 +17,7 @@ import { IGenerateFilter } from 'soapbox/features/explorer/components/explorerFi import { SelectDropdown } from 'soapbox/features/forms/index.tsx'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; -import { changeLanguage, changeMedia, createFilter, handleToggleReplies, removeFilter, selectProtocol } from 'soapbox/reducers/search-filter.ts'; +import { changeStatus, changeLanguage, changeMedia, createFilter, removeFilter, selectProtocol } from 'soapbox/reducers/search-filter.ts'; import { AppDispatch, RootState } from 'soapbox/store.ts'; import toast from 'soapbox/toast.tsx'; @@ -36,7 +36,7 @@ const messages = defineMessages({ cancel: { id: 'column.explorer.filters.cancel', defaultMessage: 'Cancel' }, addFilter: { id: 'column.explorer.filters.add_filter', defaultMessage: 'Add Filter' }, all: { id: 'column.explorer.media_filters.all', defaultMessage: 'All' }, - textOnly: { id: 'column.explorer.media_filters.text', defaultMessage: 'Text only' }, + imageOnly: { id: 'column.explorer.media_filters.image', defaultMessage: 'Image only' }, videoOnly: { id: 'column.explorer.media_filters.video', defaultMessage: 'Video only' }, none: { id: 'column.explorer.media_filters.none', defaultMessage: 'No media' }, }); @@ -107,7 +107,7 @@ const PlatformFilters = () => { const filterList = useAppSelector((state: RootState) => state.search_filter); const handleProtocolFilter = (e: React.ChangeEvent) => { - const protocol = e.target.name; + const protocol = e.target.name.toLowerCase(); dispatch(selectProtocol(protocol)); }; @@ -270,15 +270,22 @@ const MediaFilter = () => { const dispatch = useAppDispatch(); const filters = useAppSelector((state) => state.search_filter).slice(4, 8); - const mediaFilters = { + const mediaFilters = useMemo(() => ({ all: intl.formatMessage(messages.all), - text: intl.formatMessage(messages.textOnly), + image: intl.formatMessage(messages.imageOnly), video: intl.formatMessage(messages.videoOnly), none: intl.formatMessage(messages.none), - }; + }), [intl]); + const [selectedMedia, setSelectedMedia] = useState(mediaFilters.all); - const defaultValue = (Object.keys(mediaFilters) as Array).find((key) => mediaFilters[key] === filters.find((filter) => filter.status === true)?.name) || mediaFilters.all; + useEffect(() => { + const newMediaValue = (Object.keys(mediaFilters) as Array) + .find((key) => mediaFilters[key] === filters.find((filter) => filter.status === true)?.name) + || mediaFilters.all; + + setSelectedMedia(newMediaValue); + }, [filters, mediaFilters]); const handleSelectChange: React.ChangeEventHandler = e => { const filter = e.target.value; @@ -292,9 +299,10 @@ const MediaFilter = () => { @@ -307,8 +315,6 @@ const LanguageFilter = () => { const dispatch = useAppDispatch(); const languageFilter = useAppSelector((state) => state.search_filter)[0]; - const defaultValue = languageFilter.name.toLowerCase(); - const handleSelectChange: React.ChangeEventHandler = e => { const language = e.target.value; dispatch(changeLanguage(language)); @@ -321,9 +327,10 @@ const LanguageFilter = () => { @@ -341,7 +348,7 @@ const ToggleRepliesFilter = () => { const checked = repliesFilter?.status; const handleToggle = () => { - dispatch(handleToggleReplies({ checked: !checked })); + dispatch(changeStatus({ type: 'no replies', status: !checked })); }; return ( @@ -358,14 +365,10 @@ const ToggleRepliesFilter = () => { }; const generateFilter = (dispatch: AppDispatch, { name, status }: IGenerateFilter) => { - let borderColor = ''; - let textColor = ''; - let hasButton = false; const nameLowCase = name.toLowerCase(); - const handleChangeFilters = () => { - dispatch(removeFilter(name)); - }; + let borderColor = ''; + let textColor = ''; if (Object.keys(languages).some((lang) => lang.toLowerCase() === nameLowCase)) { borderColor = 'border-gray-500'; @@ -373,7 +376,7 @@ const generateFilter = (dispatch: AppDispatch, { name, status }: IGenerateFilter } else { switch (nameLowCase) { case 'no replies': - case 'text only': + case 'image only': case 'video only': case 'no media': borderColor = 'border-gray-500'; @@ -394,21 +397,32 @@ const generateFilter = (dispatch: AppDispatch, { name, status }: IGenerateFilter default: borderColor = status ? 'border-green-500' : 'border-red-500'; textColor = status ? 'text-green-500' : 'text-red-500'; - hasButton = true; } } + const handleChangeFilters = () => { + if (['nostr', 'bluesky', 'fediverse'].includes(nameLowCase)) { + dispatch(selectProtocol(nameLowCase)); + } else if (Object.keys(languages).some((lang) => lang.toLowerCase() === nameLowCase)) { + dispatch(changeLanguage('default')); + } else if (['no replies', 'image only', 'video only', 'no media'].includes(nameLowCase)) { + dispatch(changeStatus({ type: nameLowCase, status: false })); + } else { + dispatch(removeFilter(nameLowCase)); + } + }; + return (
{name.toLowerCase() !== 'default' ? name : } - {hasButton && } + />
); }; diff --git a/src/locales/en.json b/src/locales/en.json index 935ea6499..8692ce597 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -386,8 +386,8 @@ "column.explorer.filters.nostr": "Nostr", "column.explorer.filters.platforms": "Platforms:", "column.explorer.media_filters.all": "All", + "column.explorer.media_filters.image": "Image only", "column.explorer.media_filters.none": "No media", - "column.explorer.media_filters.text": "Text only", "column.explorer.media_filters.video": "Video only", "column.explorer.nostr": "Nostr", "column.explorer.popular_accounts": "Popular Accounts", diff --git a/src/reducers/search-filter.ts b/src/reducers/search-filter.ts index 67f30beda..7208b6234 100644 --- a/src/reducers/search-filter.ts +++ b/src/reducers/search-filter.ts @@ -7,7 +7,8 @@ interface IFilters { } interface IToggle { - checked: boolean; + type: string; + status: boolean; } interface INewFilter { @@ -21,8 +22,8 @@ const initialState: IFilters[] = [ { name: 'Bluesky', status: true, value: 'protocol:atproto' }, { name: 'Fediverse', status: true, value: 'protocol:activitypub' }, { name: 'No Replies', status: false, value: 'reply:false' }, - { name: 'Video Only', status: false, value: 'video:true' }, - { name: 'Image Only', status: false, value: 'media:true -video:true' }, + { name: 'Video only', status: false, value: 'video:true' }, + { name: 'Image only', status: false, value: 'media:true -video:true' }, { name: 'No media', status: false, value: '-media:true' }, ]; @@ -33,13 +34,14 @@ const search_filter = createSlice({ /** * Toggles the status of reply filter. */ - handleToggleReplies: (state, action: PayloadAction) => { + changeStatus: (state, action: PayloadAction) => { return state.map((currentState) => { - const checked = action.payload.checked; - return currentState.value.toLowerCase().includes('reply') + const status = action.payload.status; + const type = action.payload.type; + return currentState.name.toLowerCase().includes(type) ? { ...currentState, - status: checked, + status: status, } : currentState; }); @@ -103,7 +105,7 @@ const search_filter = createSlice({ * Toggles the status of a protocol filter. */ selectProtocol: (state, action: PayloadAction) => { - const protocol = action.payload.toLowerCase(); + const protocol = action.payload; return state.map((currentState) => { const newStatus = !currentState.status; if (currentState.name.toLowerCase() !== protocol) return currentState; @@ -142,5 +144,5 @@ const search_filter = createSlice({ }); export type { IFilters }; -export const { handleToggleReplies, changeMedia, changeLanguage, selectProtocol, createFilter, removeFilter, resetFilters } = search_filter.actions; +export const { changeStatus, changeMedia, changeLanguage, selectProtocol, createFilter, removeFilter, resetFilters } = search_filter.actions; export default search_filter.reducer; \ No newline at end of file