kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Remove search bar and "improve" search in "posts"
rodzic
7d778e40d2
commit
0962a018c1
|
@ -4,8 +4,7 @@ import { useState } from 'react';
|
||||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||||
import Text from 'soapbox/components/ui/text.tsx';
|
import Text from 'soapbox/components/ui/text.tsx';
|
||||||
import Search from 'soapbox/features/compose/components/search.tsx';
|
|
||||||
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
|
|
||||||
|
|
||||||
interface TabItem {
|
interface TabItem {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -24,11 +23,8 @@ interface TabsProps {
|
||||||
*/
|
*/
|
||||||
const ExplorerTabs: React.FC<TabsProps> = ({ items, activeItem }) => {
|
const ExplorerTabs: React.FC<TabsProps> = ({ items, activeItem }) => {
|
||||||
const [activeTab, setActiveTab] = useState(activeItem || items[0].name);
|
const [activeTab, setActiveTab] = useState(activeItem || items[0].name);
|
||||||
const [lastSelected, setLastSelected] = useState('');
|
|
||||||
const isMobile = useIsMobile();
|
|
||||||
|
|
||||||
const handleTabClick = (name: string) => {
|
const handleTabClick = (name: string) => {
|
||||||
setLastSelected(activeTab);
|
|
||||||
setActiveTab(name);
|
setActiveTab(name);
|
||||||
const activeItem = items.find(item => item.name === name);
|
const activeItem = items.find(item => item.name === name);
|
||||||
if (activeItem && typeof activeItem.action === 'function') {
|
if (activeItem && typeof activeItem.action === 'function') {
|
||||||
|
@ -39,16 +35,12 @@ const ExplorerTabs: React.FC<TabsProps> = ({ items, activeItem }) => {
|
||||||
return (
|
return (
|
||||||
<HStack className='inset-x-0 bottom-4 z-[999] w-full p-2' alignItems='center' justifyContent='center'>
|
<HStack className='inset-x-0 bottom-4 z-[999] w-full p-2' alignItems='center' justifyContent='center'>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<HStack space={1} className='w-full rounded-full bg-gray-200/60 p-1.5 dark:bg-gray-800' justifyContent='around'>
|
<HStack space={1} className='w-full rounded-full bg-gray-200/60 p-1 dark:bg-gray-800' justifyContent='around'>
|
||||||
{items.map(({ name, label, icon }) => {
|
{items.map(({ name, label, icon }) => {
|
||||||
const isSelected = activeTab === name;
|
const isSelected = activeTab === name;
|
||||||
const shouldKeepBg = lastSelected === name && activeTab === 'search';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack key={name} alignItems='center' justifyContent='center'>
|
<HStack key={name} alignItems='center' justifyContent='center'>
|
||||||
{name === 'search' && isSelected ? (
|
|
||||||
<Search autoSubmit />
|
|
||||||
) : (
|
|
||||||
<HStack
|
<HStack
|
||||||
space={1}
|
space={1}
|
||||||
alignItems='center'
|
alignItems='center'
|
||||||
|
@ -57,29 +49,24 @@ const ExplorerTabs: React.FC<TabsProps> = ({ items, activeItem }) => {
|
||||||
/* eslint-disable-next-line tailwindcss/no-custom-classname */
|
/* eslint-disable-next-line tailwindcss/no-custom-classname */
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'group cursor-pointer rounded-full px-5 py-3 text-sm font-medium transition-all duration-300',
|
'group cursor-pointer rounded-full px-5 py-3 text-sm font-medium transition-all duration-300',
|
||||||
isSelected || shouldKeepBg
|
isSelected
|
||||||
? 'border-gray-500 bg-gray-500 text-white shadow-md dark:border-gray-700 dark:bg-gray-700'
|
? 'border-gray-500 bg-gray-500 text-white shadow-md dark:border-gray-700 dark:bg-gray-700'
|
||||||
: 'dark:hover:bg-gray-800/200 text-gray-500 hover:bg-gray-400/60 hover:!text-white',
|
: 'dark:hover:bg-gray-800/200 text-gray-500 hover:bg-gray-400/60 hover:!text-white',
|
||||||
{ '!p-2': shouldKeepBg },
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<SvgIcon
|
<SvgIcon
|
||||||
src={icon}
|
src={icon}
|
||||||
className='size-5 '
|
className='size-5 '
|
||||||
/>
|
/>
|
||||||
{(activeTab !== 'search' || isSelected) &&
|
|
||||||
(isMobile ? isSelected : !isMobile) && (
|
|
||||||
<Text
|
<Text
|
||||||
className={clsx('transition-all duration-300', {
|
className={clsx('transition-all duration-300', {
|
||||||
'!text-gray-500 group-hover:!text-white': !isSelected,
|
'!text-gray-500 group-hover:!text-white': !isSelected,
|
||||||
'!text-white': isSelected || shouldKeepBg,
|
'!text-white': isSelected,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
|
||||||
</HStack>
|
</HStack>
|
||||||
)}
|
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import globeIcon from '@tabler/icons/outline/globe.svg';
|
import globeIcon from '@tabler/icons/outline/globe.svg';
|
||||||
import searchIcon from '@tabler/icons/outline/search.svg';
|
|
||||||
import trendIcon from '@tabler/icons/outline/trending-up.svg';
|
import trendIcon from '@tabler/icons/outline/trending-up.svg';
|
||||||
import userIcon from '@tabler/icons/outline/user.svg';
|
import userIcon from '@tabler/icons/outline/user.svg';
|
||||||
import xIcon from '@tabler/icons/outline/x.svg';
|
import xIcon from '@tabler/icons/outline/x.svg';
|
||||||
|
@ -34,26 +33,29 @@ import PublicTimeline from 'soapbox/features/public-timeline/index.tsx';
|
||||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||||
import { useSuggestions } from 'soapbox/queries/suggestions.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 { SearchFilter } from 'soapbox/reducers/search.ts';
|
||||||
|
|
||||||
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||||
import type { VirtuosoHandle } from 'react-virtuoso';
|
import type { VirtuosoHandle } from 'react-virtuoso';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
accounts: { id: 'search_results.posts', defaultMessage: 'Accounts' },
|
accounts: { id: 'search_results.accounts', defaultMessage: 'Accounts' },
|
||||||
statuses: { id: 'search_results.accounts', defaultMessage: 'Posts' },
|
statuses: { id: 'search_results.posts', defaultMessage: 'Posts' },
|
||||||
trends: { id: 'search_results.trends', defaultMessage: 'Trends' },
|
trends: { id: 'search_results.trends', defaultMessage: 'Trends' },
|
||||||
search: { id: 'common.search', defaultMessage: 'Search' },
|
search: { id: 'common.search', defaultMessage: 'Search' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const SearchResults = () => {
|
const SearchResults = () => {
|
||||||
const node = useRef<VirtuosoHandle>(null);
|
const node = useRef<VirtuosoHandle>(null);
|
||||||
|
const filters = useAppSelector((state) => state.search_filter);
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { data: suggestions } = useSuggestions();
|
const { data: suggestions } = useSuggestions();
|
||||||
const [globalTimeline, setGlobalTimeline] = useState(true);
|
const [withFilter, setWithFilter] = useState(false);
|
||||||
|
const [tab, setTab] = useState('global');
|
||||||
|
|
||||||
const value = useAppSelector((state) => state.search.submittedValue);
|
const value = useAppSelector((state) => state.search.submittedValue);
|
||||||
const results = useAppSelector((state) => state.search.results);
|
const results = useAppSelector((state) => state.search.results);
|
||||||
|
@ -74,9 +76,9 @@ const SearchResults = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUnsetAccount = () => dispatch(setSearchAccount(null));
|
const handleUnsetAccount = () => dispatch(setSearchAccount(null));
|
||||||
const handleAction = (filter: SearchFilter) =>{
|
const handleAction = (filter: SearchFilter, tab: string) =>{
|
||||||
setGlobalTimeline(false);
|
|
||||||
selectFilter(filter);
|
selectFilter(filter);
|
||||||
|
setTab(tab);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter));
|
const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter));
|
||||||
|
@ -86,31 +88,26 @@ const SearchResults = () => {
|
||||||
items.push(
|
items.push(
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(messages.statuses),
|
label: intl.formatMessage(messages.statuses),
|
||||||
action: () => setGlobalTimeline(true),
|
action: () => handleAction('statuses', 'global'),
|
||||||
name: 'statuses',
|
name: 'global',
|
||||||
icon: globeIcon,
|
icon: globeIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(messages.trends),
|
label: intl.formatMessage(messages.trends),
|
||||||
action: () => handleAction('statuses'),
|
action: () => handleAction('statuses', 'statuses'),
|
||||||
name: 'trends',
|
name: 'statuses',
|
||||||
icon: trendIcon,
|
icon: trendIcon,
|
||||||
},
|
},
|
||||||
|
// TODO : limit search accounts to only be able use include
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(messages.accounts),
|
label: intl.formatMessage(messages.accounts),
|
||||||
action: () => handleAction('accounts'),
|
action: () => handleAction('accounts', 'accounts'),
|
||||||
name: 'accounts',
|
name: 'accounts',
|
||||||
icon: userIcon,
|
icon: userIcon,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: intl.formatMessage(messages.search),
|
|
||||||
action: () => null,
|
|
||||||
name: 'search',
|
|
||||||
icon: searchIcon,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return <ExplorerTabs items={items} activeItem={selectedFilter} />;
|
return <ExplorerTabs items={items} activeItem={tab} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentIndex = (id: string): number => {
|
const getCurrentIndex = (id: string): number => {
|
||||||
|
@ -146,6 +143,16 @@ const SearchResults = () => {
|
||||||
dispatch(fetchTrendingStatuses());
|
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 searchResults;
|
||||||
let hasMore = false;
|
let hasMore = false;
|
||||||
let loaded;
|
let loaded;
|
||||||
|
@ -257,7 +264,7 @@ const SearchResults = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{globalTimeline ? <PublicTimeline /> : (noResultsMessage || (
|
{tab === 'global' && !withFilter ? <PublicTimeline /> : (noResultsMessage || (
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
id='search-results'
|
id='search-results'
|
||||||
ref={node}
|
ref={node}
|
||||||
|
|
|
@ -1447,9 +1447,9 @@
|
||||||
"scheduled_status.cancel": "Cancel",
|
"scheduled_status.cancel": "Cancel",
|
||||||
"search.action": "Search for “{query}”",
|
"search.action": "Search for “{query}”",
|
||||||
"search.placeholder": "Search",
|
"search.placeholder": "Search",
|
||||||
"search_results.accounts": "Posts",
|
"search_results.accounts": "Accounts",
|
||||||
"search_results.filter_message": "You are searching for posts from @{acct}.",
|
"search_results.filter_message": "You are searching for posts from @{acct}.",
|
||||||
"search_results.posts": "Accounts",
|
"search_results.posts": "Posts",
|
||||||
"search_results.trends": "Trends",
|
"search_results.trends": "Trends",
|
||||||
"security.codes.fail": "Failed to fetch backup codes",
|
"security.codes.fail": "Failed to fetch backup codes",
|
||||||
"security.confirm.fail": "Incorrect code or password. Try again.",
|
"security.confirm.fail": "Incorrect code or password. Try again.",
|
||||||
|
|
|
@ -144,5 +144,6 @@ const search_filter = createSlice({
|
||||||
});
|
});
|
||||||
|
|
||||||
export type { IFilters };
|
export type { IFilters };
|
||||||
|
export { initialState };
|
||||||
export const { changeStatus, 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;
|
export default search_filter.reducer;
|
Ładowanie…
Reference in New Issue