From f8f1292c390cea68189cbb94e5e1fb813fa64338 Mon Sep 17 00:00:00 2001 From: danidfra Date: Sun, 2 Mar 2025 23:21:24 -0300 Subject: [PATCH] Update explorer with some of recent comments --- src/components/sidebar-navigation-link.tsx | 11 +- .../compose/components/search-results.tsx | 76 +--------- src/features/explorer/index.tsx | 141 +++++++++++++++++- src/features/ui/index.tsx | 1 + src/pages/search-page.tsx | 9 +- src/store.ts | 9 +- 6 files changed, 166 insertions(+), 81 deletions(-) diff --git a/src/components/sidebar-navigation-link.tsx b/src/components/sidebar-navigation-link.tsx index 8d1b26ca2..df07e101c 100644 --- a/src/components/sidebar-navigation-link.tsx +++ b/src/components/sidebar-navigation-link.tsx @@ -27,7 +27,16 @@ const SidebarNavigationLink = forwardRef((props: ISidebarNavigationLink, ref: Re const { icon, activeIcon, text, to = '', count, countMax, onClick } = props; const { pathname } = useLocation(); - const isActive = pathname === to; + const isDefault = to === '' || to === '/'; + + let isActive; + + if (isDefault) { + isActive = pathname === to; + } else { + isActive = pathname.includes(to); + } + const handleClick: React.EventHandler = (e) => { if (onClick) { diff --git a/src/features/compose/components/search-results.tsx b/src/features/compose/components/search-results.tsx index fe42197e6..cf9626a22 100644 --- a/src/features/compose/components/search-results.tsx +++ b/src/features/compose/components/search-results.tsx @@ -1,18 +1,12 @@ -import globeIcon from '@tabler/icons/outline/globe.svg'; -import trendIcon from '@tabler/icons/outline/trending-up.svg'; -import userIcon from '@tabler/icons/outline/user.svg'; import xIcon from '@tabler/icons/outline/x.svg'; import clsx from 'clsx'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef } from 'react'; import { FormattedMessage, - defineMessages, - useIntl, } from 'react-intl'; import { expandSearch, - setFilter, setSearchAccount, } from 'soapbox/actions/search.ts'; import { expandTrendingStatuses, fetchTrendingStatuses } from 'soapbox/actions/trending-statuses.ts'; @@ -20,7 +14,6 @@ import { useAccount } from 'soapbox/api/hooks/index.ts'; import Hashtag from 'soapbox/components/hashtag.tsx'; import IconButton from 'soapbox/components/icon-button.tsx'; import ScrollableList from 'soapbox/components/scrollable-list.tsx'; -import ExplorerTabs from 'soapbox/components/ui/explorer-tabs.tsx'; import HStack from 'soapbox/components/ui/hstack.tsx'; import Spinner from 'soapbox/components/ui/spinner.tsx'; import Text from 'soapbox/components/ui/text.tsx'; @@ -29,33 +22,19 @@ import StatusContainer from 'soapbox/containers/status-container.tsx'; import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder-account.tsx'; import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder-hashtag.tsx'; import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder-status.tsx'; -import PublicTimeline from 'soapbox/features/public-timeline/index.tsx'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; import { useSuggestions } from 'soapbox/queries/suggestions.ts'; -import { initialState as filterInitialState } from 'soapbox/reducers/search-filter.ts'; -import { SearchFilter } from 'soapbox/reducers/search.ts'; import type { OrderedSet as ImmutableOrderedSet } from 'immutable'; import type { VirtuosoHandle } from 'react-virtuoso'; -const messages = defineMessages({ - accounts: { id: 'search_results.accounts', defaultMessage: 'Accounts' }, - statuses: { id: 'search_results.posts', defaultMessage: 'Posts' }, - trends: { id: 'search_results.trends', defaultMessage: 'Trends' }, - search: { id: 'common.search', defaultMessage: 'Search' }, -}); - const SearchResults = () => { const node = useRef(null); - const filters = useAppSelector((state) => state.search_filter); - const intl = useIntl(); const dispatch = useAppDispatch(); const { data: suggestions } = useSuggestions(); - const [withFilter, setWithFilter] = useState(false); - const [tab, setTab] = useState('global'); const value = useAppSelector((state) => state.search.submittedValue); const results = useAppSelector((state) => state.search.results); @@ -76,39 +55,6 @@ const SearchResults = () => { }; const handleUnsetAccount = () => dispatch(setSearchAccount(null)); - const handleAction = (filter: SearchFilter, tab: string) =>{ - selectFilter(filter); - setTab(tab); - }; - - const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter)); - - const renderFilterBar = () => { - const items = []; - items.push( - { - label: intl.formatMessage(messages.statuses), - action: () => handleAction('statuses', 'global'), - name: 'global', - icon: globeIcon, - }, - { - label: intl.formatMessage(messages.trends), - action: () => handleAction('statuses', 'statuses'), - name: 'statuses', - icon: trendIcon, - }, - // TODO : limit search accounts to only be able use include - { - label: intl.formatMessage(messages.accounts), - action: () => handleAction('accounts', 'accounts'), - name: 'accounts', - icon: userIcon, - }, - ); - - return ; - }; const getCurrentIndex = (id: string): number => { return resultsIds?.keySeq().findIndex(key => key === id); @@ -143,16 +89,6 @@ const SearchResults = () => { dispatch(fetchTrendingStatuses()); }, []); - useEffect(() => { - setWithFilter(filters.length !== filterInitialState.length || - !filters.every((filter, index) => - filter.name === filterInitialState[index].name && - filter.status === filterInitialState[index].status && - filter.value === filterInitialState[index].value, - ), - ); - }, [filters]); - let searchResults; let hasMore = false; let loaded; @@ -247,7 +183,7 @@ const SearchResults = () => { return ( <> - {filterByAccount ? ( + {filterByAccount && ( @@ -258,13 +194,9 @@ const SearchResults = () => { /> - ) : ( -
- {renderFilterBar()} -
)} - {tab === 'global' && !withFilter ? : (noResultsMessage || ( + {noResultsMessage || ( { > {searchResults || []} - ))} + )} ); }; diff --git a/src/features/explorer/index.tsx b/src/features/explorer/index.tsx index 2bc93a28b..4d3102486 100644 --- a/src/features/explorer/index.tsx +++ b/src/features/explorer/index.tsx @@ -1,23 +1,58 @@ +import globeIcon from '@tabler/icons/outline/globe.svg'; +import trendIcon from '@tabler/icons/outline/trending-up.svg'; +import userIcon from '@tabler/icons/outline/user.svg'; +import { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; +import { Route, Switch, useLocation } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom-v5-compat'; +import { clearSearch, setFilter } from 'soapbox/actions/search.ts'; import { Column } from 'soapbox/components/ui/column.tsx'; import Divider from 'soapbox/components/ui/divider.tsx'; import Stack from 'soapbox/components/ui/stack.tsx'; +import Tabs from 'soapbox/components/ui/tabs.tsx'; import SearchResults from 'soapbox/features/compose/components/search-results.tsx'; +import Search from 'soapbox/features/compose/components/search.tsx'; import ExplorerCards from 'soapbox/features/explorer/components/explorer-cards.tsx'; import ExplorerFilter from 'soapbox/features/explorer/components/explorerFilter.tsx'; +import AccountsCarousel from 'soapbox/features/explorer/components/popular-accounts.tsx'; +import { PublicTimeline } from 'soapbox/features/ui/util/async-components.ts'; +import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; +import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; +import { IFilters, initialState as filterInitialState } from 'soapbox/reducers/search-filter.ts'; +import { SearchFilter } from 'soapbox/reducers/search.ts'; const messages = defineMessages({ heading: { id: 'column.explorer', defaultMessage: 'Explorer' }, + accounts: { id: 'search_results.accounts', defaultMessage: 'Accounts' }, + statuses: { id: 'search_results.posts', defaultMessage: 'Posts' }, + trends: { id: 'search_results.trends', defaultMessage: 'Trends' }, }); -const SearchPage = () => { - const intl = useIntl(); +const checkFilters = (filters: IFilters[]) => { + return filters.length !== filterInitialState.length || + !filters.every((filter, index) => + filter.name === filterInitialState[index].name && + filter.status === filterInitialState[index].status && + filter.value === filterInitialState[index].value, + ); +}; + +const PostsTab = () => { + const path = useLocation().pathname; + const inPosts = path === '/explorer'; + const filters = useAppSelector((state) => state.search_filter); + + const [withFilter, setWithFilter] = useState(checkFilters(filters)); + + useEffect(() => { + setWithFilter(checkFilters(filters)); + }, [filters]); return ( - + + {inPosts && <> - @@ -26,9 +61,107 @@ const SearchPage = () => { + {!withFilter ? : } + + } + + + ); +}; + +const TrendsTab = () => { + return ( + + + + ); +}; + +const AccountsTab = () => { + return ( + + + + + + +
+ +
+ +
+
+ ); +}; + + +const SearchPage = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const navigate = useNavigate(); + + const [selectedFilter, setSelectedFilter] = useState('global'); + + const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter)); + + const renderFilterBar = () => { + const items = []; + + const handleTabs = (path: string, filter?: SearchFilter) => { + if (filter) { + selectFilter(filter); + dispatch(clearSearch()); + dispatch(setFilter(filter)); + } else { + dispatch(setFilter('statuses')); + } + setSelectedFilter(filter ?? 'global'); + navigate(`/explorer${path}`); + }; + + items.push( + { + text: intl.formatMessage(messages.statuses), + action: () => handleTabs(''), + name: 'global', + icon: globeIcon, + }, + { + text: intl.formatMessage(messages.trends), + action: () => handleTabs('/trends', 'statuses'), + name: 'statuses', + icon: trendIcon, + }, + { + text: intl.formatMessage(messages.accounts), + action: () => handleTabs('/accounts', 'accounts'), + name: 'accounts', + icon: userIcon, + }, + ); + + return ; + }; + + return ( + + + + +
+ {renderFilterBar()} +
+ + + + + + +
+
); }; diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index 9931797ed..2474e04a6 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -48,6 +48,7 @@ import Navbar from './components/navbar.tsx'; import { Status, RemoteTimeline, + PublicTimeline, AccountTimeline, AccountGallery, HomeTimeline, diff --git a/src/pages/search-page.tsx b/src/pages/search-page.tsx index 550e26eea..b441998eb 100644 --- a/src/pages/search-page.tsx +++ b/src/pages/search-page.tsx @@ -1,3 +1,5 @@ +import { useLocation } from 'react-router-dom'; + import Layout from 'soapbox/components/ui/layout.tsx'; import LinkFooter from 'soapbox/features/ui/components/link-footer.tsx'; import { @@ -5,6 +7,7 @@ import { TrendsPanel, SignUpPanel, CtaBanner, + LatestAccountsPanel, SuggestedGroupsPanel, } from 'soapbox/features/ui/util/async-components.ts'; import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; @@ -17,6 +20,7 @@ interface ISearchPage { const SearchPage: React.FC = ({ children }) => { const me = useAppSelector(state => state.me); const features = useFeatures(); + const accountsPath = useLocation().pathname === '/explorer/accounts'; return ( <> @@ -37,8 +41,9 @@ const SearchPage: React.FC = ({ children }) => { )} - {features.suggestions && ( - + {features.suggestions && (accountsPath + ? + : )} {features.groups && ( diff --git a/src/store.ts b/src/store.ts index 52df650f1..5b4b589d0 100644 --- a/src/store.ts +++ b/src/store.ts @@ -7,8 +7,10 @@ import appReducer from './reducers/index.ts'; import type { AnyAction } from 'redux'; -const loadState = () => { +const loadState = (pathname: string) => { try { + if (pathname !== '/explorer') return undefined; + const savedState = localStorage.getItem('soapbox:explorer:filters'); return savedState ? JSON.parse(savedState) : undefined; } catch (error) { @@ -17,7 +19,10 @@ const loadState = () => { } }; -const preloadedState = { ...loadState() ? { search_filter: loadState() } : {} }; +const preloadedState = (() => { + const storedFilter = loadState(window.location.pathname); + return storedFilter ? { search_filter: storedFilter } : {}; +})(); export const store = configureStore({ reducer: appReducer,