Merge branch 'implement-explorer' into change-to-redux

merge-requests/3337/head
danidfra 2025-02-24 18:21:28 -03:00
commit 781aaefbe1
19 zmienionych plików z 208 dodań i 116 usunięć

Wyświetl plik

@ -280,15 +280,6 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
text={features.federating ? instance.domain : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />}
onClick={onClose}
/>
{features.federating && (
<SidebarLink
to='/timeline/global'
icon={worldIcon}
text={<FormattedMessage id='tabs_bar.global' defaultMessage='Global' />}
onClick={onClose}
/>
)}
</>}
<Divider />

Wyświetl plik

@ -6,6 +6,7 @@ import userFilledIcon from '@tabler/icons/filled/user.svg';
import atIcon from '@tabler/icons/outline/at.svg';
import bellIcon from '@tabler/icons/outline/bell.svg';
import bookmarkIcon from '@tabler/icons/outline/bookmark.svg';
import compassIcon from '@tabler/icons/outline/brand-safari.svg';
import calendarEventIcon from '@tabler/icons/outline/calendar-event.svg';
import circlesIcon from '@tabler/icons/outline/circles.svg';
import codeIcon from '@tabler/icons/outline/code.svg';
@ -15,7 +16,6 @@ import homeIcon from '@tabler/icons/outline/home.svg';
import listIcon from '@tabler/icons/outline/list.svg';
import mailIcon from '@tabler/icons/outline/mail.svg';
import messagesIcon from '@tabler/icons/outline/messages.svg';
import rocketIcon from '@tabler/icons/outline/rocket.svg';
import settingsIcon from '@tabler/icons/outline/settings.svg';
import userPlusIcon from '@tabler/icons/outline/user-plus.svg';
import userIcon from '@tabler/icons/outline/user.svg';
@ -162,8 +162,8 @@ const SidebarNavigation = () => {
/>
<SidebarNavigationLink
to='/search'
icon={rocketIcon}
to='/explorer'
icon={compassIcon}
text={<FormattedMessage id='tabs_bar.search' defaultMessage='Explorer' />}
/>
@ -223,14 +223,6 @@ const SidebarNavigation = () => {
text={features.federating ? instance.domain : <FormattedMessage id='tabs_bar.global' defaultMessage='Global' />}
/>
)}
{(features.federating && (account || !restrictUnauth.timelines.federated)) && (
<SidebarNavigationLink
to='/timeline/global'
icon={worldIcon}
text={<FormattedMessage id='tabs_bar.global' defaultMessage='Global' />}
/>
)}
</>
)}

Wyświetl plik

@ -3,12 +3,12 @@ import circlesFilledIcon from '@tabler/icons/filled/circles.svg';
import homeFilledIcon from '@tabler/icons/filled/home.svg';
import mailFilledIcon from '@tabler/icons/filled/mail.svg';
import bellIcon from '@tabler/icons/outline/bell.svg';
import compassIcon from '@tabler/icons/outline/brand-safari.svg';
import circlesIcon from '@tabler/icons/outline/circles.svg';
import dashboardIcon from '@tabler/icons/outline/dashboard.svg';
import homeIcon from '@tabler/icons/outline/home.svg';
import mailIcon from '@tabler/icons/outline/mail.svg';
import messagesIcon from '@tabler/icons/outline/messages.svg';
import rocketIcon from '@tabler/icons/outline/rocket.svg';
import { FormattedMessage } from 'react-intl';
import ThumbNavigationLink from 'soapbox/components/thumb-navigation-link.tsx';
@ -84,9 +84,9 @@ const ThumbNavigation: React.FC = (): JSX.Element => {
)}
<ThumbNavigationLink
src={rocketIcon}
src={compassIcon}
text={<FormattedMessage id='navigation.search' defaultMessage='Explorer' />}
to='/search'
to='/explorer'
exact
/>

Wyświetl plik

@ -0,0 +1,91 @@
import clsx from 'clsx';
import { useState } from 'react';
import HStack from 'soapbox/components/ui/hstack.tsx';
import SvgIcon from 'soapbox/components/ui/svg-icon.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 {
name: string;
label: string;
action: () => void;
icon: string;
}
interface TabsProps {
items: TabItem[];
activeItem?: string;
}
/**
*
*/
const ExplorerTabs: React.FC<TabsProps> = ({ items, activeItem }) => {
const [activeTab, setActiveTab] = useState(activeItem || items[0].name);
const [lastSelected, setLastSelected] = useState('');
const isMobile = useIsMobile();
const handleTabClick = (name: string) => {
setLastSelected(activeTab);
setActiveTab(name);
const activeItem = items.find(item => item.name === name);
if (activeItem && typeof activeItem.action === 'function') {
activeItem.action();
}
};
return (
<HStack className='inset-x-0 bottom-4 z-[999] w-full p-2' alignItems='center' justifyContent='center'>
{/* Header */}
<HStack space={1} className='w-full rounded-full bg-gray-200/60 p-1.5 dark:bg-gray-800' justifyContent='around'>
{items.map(({ name, label, icon }) => {
const isSelected = activeTab === name;
const shouldKeepBg = lastSelected === name && activeTab === 'search';
return (
<HStack key={name} alignItems='center' justifyContent='center'>
{name === 'search' && isSelected ? (
<Search autoSubmit />
) : (
<HStack
space={1}
alignItems='center'
justifyContent='center'
onClick={() => handleTabClick(name)}
/* eslint-disable-next-line tailwindcss/no-custom-classname */
className={clsx(
'group cursor-pointer rounded-full px-5 py-3 text-sm font-medium transition-all duration-300',
isSelected || shouldKeepBg
? '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',
{ '!p-2': shouldKeepBg },
)}
>
<SvgIcon
src={icon}
className='size-5 '
/>
{(activeTab !== 'search' || isSelected) &&
(isMobile ? isSelected : !isMobile) && (
<Text
className={clsx('transition-all duration-300', {
'!text-gray-500 group-hover:!text-white': !isSelected,
'!text-white': isSelected || shouldKeepBg,
})}
>
{label}
</Text>
)}
</HStack>
)}
</HStack>
);
})}
</HStack>
</HStack>
);
};
export default ExplorerTabs;

Wyświetl plik

@ -68,7 +68,6 @@ const Input = forwardRef<HTMLInputElement, IInput>(
clsx('relative', {
'rounded-md': theme !== 'search',
'rounded-full': theme === 'search',
'mt-1': !String(outerClassName).includes('mt-'),
[String(outerClassName)]: typeof outerClassName !== 'undefined',
})
}

Wyświetl plik

@ -259,7 +259,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
const onSearch = () => {
dispatch(setSearchAccount(account.id));
history.push('/search');
history.push('/explorer');
};
const onAvatarClick = () => {

Wyświetl plik

@ -1,48 +1,59 @@
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 userIcon from '@tabler/icons/outline/user.svg';
import xIcon from '@tabler/icons/outline/x.svg';
import clsx from 'clsx';
import { useEffect, useRef } from 'react';
import {
FormattedMessage,
// defineMessages,
// useIntl
import { useEffect, useRef, useState } from 'react';
import {
FormattedMessage,
defineMessages,
useIntl,
} from 'react-intl';
import {
import {
expandSearch,
// setFilter,
// setSearchAccount
setFilter,
setSearchAccount,
} from 'soapbox/actions/search.ts';
import { expandTrendingStatuses, fetchTrendingStatuses } from 'soapbox/actions/trending-statuses.ts';
// import { useAccount } from 'soapbox/api/hooks/index.ts';
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 Tabs from 'soapbox/components/ui/tabs.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import AccountContainer from 'soapbox/containers/account-container.tsx';
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 { SearchFilter } from 'soapbox/reducers/search.ts';
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
import type { VirtuosoHandle } from 'react-virtuoso';
// import type { SearchFilter } from 'soapbox/reducers/search.ts';
// const messages = defineMessages({
// accounts: { id: 'search_results.accounts', defaultMessage: 'People' },
// statuses: { id: 'search_results.statuses', defaultMessage: 'Posts' },
// hashtags: { id: 'search_results.hashtags', defaultMessage: 'Hashtags' },
// });
const messages = defineMessages({
accounts: { id: 'search_results.posts', defaultMessage: 'Accounts' },
statuses: { id: 'search_results.accounts', defaultMessage: 'Posts' },
trends: { id: 'search_results.trends', defaultMessage: 'Trends' },
search: { id: 'common.search', defaultMessage: 'Search' },
});
const SearchResults = () => {
const node = useRef<VirtuosoHandle>(null);
// const intl = useIntl();
const intl = useIntl();
const dispatch = useAppDispatch();
const { data: suggestions } = useSuggestions();
const [globalTimeline, setGlobalTimeline] = useState(true);
const value = useAppSelector((state) => state.search.submittedValue);
const results = useAppSelector((state) => state.search.results);
@ -51,8 +62,8 @@ const SearchResults = () => {
const trends = useAppSelector((state) => state.trends.items);
const submitted = useAppSelector((state) => state.search.submitted);
const selectedFilter = useAppSelector((state) => state.search.filter);
// const filterByAccount = useAppSelector((state) => state.search.accountId || undefined);
// const { account } = useAccount(filterByAccount);
const filterByAccount = useAppSelector((state) => state.search.accountId || undefined);
const { account } = useAccount(filterByAccount);
const handleLoadMore = () => {
if (results.accounts.size || results.statuses.size || results.hashtags.size) {
@ -62,35 +73,45 @@ const SearchResults = () => {
}
};
// const handleUnsetAccount = () => dispatch(setSearchAccount(null));
const handleUnsetAccount = () => dispatch(setSearchAccount(null));
const handleAction = (filter: SearchFilter) =>{
setGlobalTimeline(false);
selectFilter(filter);
};
// const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter));
const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter));
// const renderFilterBar = () => {
// const items = [];
// items.push(
// {
// text: intl.formatMessage(messages.statuses),
// action: () => selectFilter('statuses'),
// name: 'statuses',
// },
// {
// text: intl.formatMessage(messages.accounts),
// action: () => selectFilter('accounts'),
// name: 'accounts',
// },
// );
const renderFilterBar = () => {
const items = [];
items.push(
{
label: intl.formatMessage(messages.statuses),
action: () => setGlobalTimeline(true),
name: 'statuses',
icon: globeIcon,
},
{
label: intl.formatMessage(messages.trends),
action: () => handleAction('statuses'),
name: 'trends',
icon: trendIcon,
},
{
label: intl.formatMessage(messages.accounts),
action: () => handleAction('accounts'),
name: 'accounts',
icon: userIcon,
},
{
label: intl.formatMessage(messages.search),
action: () => null,
name: 'search',
icon: searchIcon,
},
);
// items.push(
// {
// text: intl.formatMessage(messages.hashtags),
// action: () => selectFilter('hashtags'),
// name: 'hashtags',
// },
// );
// return <Tabs items={items} activeItem={selectedFilter} />;
// };
return <ExplorerTabs items={items} activeItem={selectedFilter} />;
};
const getCurrentIndex = (id: string): number => {
return resultsIds?.keySeq().findIndex(key => key === id);
@ -219,7 +240,7 @@ const SearchResults = () => {
return (
<>
{/* {filterByAccount ? (
{filterByAccount ? (
<HStack className='mb-4 border-b border-solid border-gray-200 px-2 pb-4 dark:border-gray-800' space={2}>
<IconButton iconClassName='h-5 w-5' src={xIcon} onClick={handleUnsetAccount} />
<Text truncate>
@ -231,10 +252,12 @@ const SearchResults = () => {
</Text>
</HStack>
) : (
<div className='px-4'>{renderFilterBar()}</div>
)} */}
<div className='relative px-4'>
{renderFilterBar()}
</div>
)}
{noResultsMessage || (
{globalTimeline ? <PublicTimeline /> : (noResultsMessage || (
<ScrollableList
id='search-results'
ref={node}
@ -257,7 +280,7 @@ const SearchResults = () => {
>
{searchResults || []}
</ScrollableList>
)}
))}
</>
);
};

Wyświetl plik

@ -76,7 +76,7 @@ const SearchZapSplit = (props: ISearchZapSplit) => {
dispatch(setSearchAccount(null));
dispatch(submitSearch());
history.push('/search');
history.push('/explorer');
} else {
dispatch(submitSearch());
}

Wyświetl plik

@ -88,7 +88,7 @@ const Search = (props: ISearch) => {
dispatch(setSearchAccount(null));
dispatch(submitSearch());
history.push('/search');
history.push('/explorer');
} else {
dispatch(submitSearch());
}

Wyświetl plik

@ -24,10 +24,10 @@ const ExplorerCards = () => {
const [isOpen, setIsOpen] = useState(true);
const intl = useIntl();
return (
<Stack>
<Stack className='mx-4 mt-4' space={2}>
<Stack
space={4}
className={`m-2 rounded-xl bg-gradient-to-r from-pink-400 via-purple-500 to-blue-400 sm:m-4 ${isOpen ? 'mt-0 px-5 pb-8 pt-4' : 'p-4'}`}
className={`rounded-xl bg-gradient-to-r from-primary-400 to-primary-700 ${isOpen ? 'mt-0 px-5 pb-8 pt-4' : 'p-4'}`}
>
<HStack justifyContent='between' className='text-white'>
<HStack space={2}>
@ -53,11 +53,11 @@ const ExplorerCards = () => {
</Text>
</Stack>
<HStack className={`mx-2 mb-2 sm:mx-4 sm:mb-4 ${isOpen ? 'max-h-96 opacity-100' : 'hidden max-h-0 opacity-0'}`} space={2}>
<HStack className={`${isOpen ? 'max-h-96 opacity-100' : 'hidden max-h-0 opacity-0'}`} space={2}>
{/* Nostr */}
<Stack
space={4}
className='w-1/2 rounded-xl bg-gradient-to-r from-pink-400 to-purple-500 px-5 pb-6 pt-4'
className='w-1/2 rounded-xl bg-gradient-to-r from-primary-400 to-primary-600 px-5 pb-6 pt-4'
justifyContent='center'
>
<HStack alignItems='center' justifyContent='between'>
@ -82,7 +82,7 @@ const ExplorerCards = () => {
{/* Bridge */}
<Stack
space={4}
className='w-1/2 rounded-xl bg-gradient-to-r from-purple-500 to-blue-400 px-5 pb-6 pt-4'
className='w-1/2 rounded-xl bg-gradient-to-r from-primary-600 to-primary-700 px-5 pb-6 pt-4'
>
<HStack alignItems='center' className='sm:min-w-48'>
{/* Title */}

Wyświetl plik

@ -15,7 +15,7 @@ import {
PlatformFilters,
RepliesFilter,
generateFilter,
} from 'soapbox/features/search/components/filters.tsx';
} from 'soapbox/features/explorer/components/filters.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
const messages = defineMessages({

Wyświetl plik

@ -13,9 +13,8 @@ import Stack from 'soapbox/components/ui/stack.tsx';
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import Toggle from 'soapbox/components/ui/toggle.tsx';
import { IGenerateFilter } from 'soapbox/features/explorer/components/explorerFilter.tsx';
import { SelectDropdown } from 'soapbox/features/forms/index.tsx';
import { IGenerateFilter } from 'soapbox/features/search/components/explorerFilter.tsx';
// import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
const messages = defineMessages({
filters: { id: 'column.explorer.filters', defaultMessage: 'Filters:' },
@ -48,7 +47,6 @@ const languages = {
da: 'Dansk',
de: 'Deutsch',
el: 'Ελληνικά',
'en-Shaw': '𐑖𐑱𐑝𐑾𐑯',
eo: 'Esperanto',
es: 'Español',
eu: 'Euskara',
@ -80,14 +78,12 @@ const languages = {
oc: 'Occitan',
pl: 'Polski',
pt: 'Português',
'pt-BR': 'Português do Brasil',
ro: 'Română',
ru: 'Русский',
sk: 'Slovenčina',
sl: 'Slovenščina',
sq: 'Shqip',
sr: 'Српски',
'sr-Latn': 'Srpski (latinica)',
sv: 'Svenska',
ta: 'தமிழ்',
te: 'తెలుగు',
@ -95,9 +91,6 @@ const languages = {
tr: 'Türkçe',
uk: 'Українська',
zh: '中文',
'zh-CN': '简体中文',
'zh-HK': '繁體中文(香港)',
'zh-TW': '繁體中文(臺灣)',
};
interface IFilter {
@ -370,6 +363,7 @@ const MediaFilter = ({ onChangeFilters }: IFilter) => {
const generateFilter = ({ name, state }: IGenerateFilter, onChangeFilters: React.Dispatch<React.SetStateAction<IGenerateFilter[]>>) => {
let borderColor = '';
let textColor = '';
let hasButton = false;
switch (name.toLowerCase()) {
case 'nostr':
borderColor = 'border-purple-500';
@ -391,19 +385,20 @@ const generateFilter = ({ name, state }: IGenerateFilter, onChangeFilters: React
}
borderColor = state ? 'border-green-500' : 'border-red-500';
textColor = state ? 'text-green-500' : 'text-red-500';
hasButton = true;
}
return (
<div
key={name}
className={`group m-1 flex items-center gap-0.5 whitespace-normal break-words rounded-full border-2 bg-transparent px-3 text-base font-medium shadow-sm hover:cursor-pointer hover:pr-1 ${borderColor} `}
className={`group m-1 flex items-center gap-0.5 whitespace-normal break-words rounded-full border-2 bg-transparent px-3 text-base font-medium shadow-sm hover:cursor-pointer ${hasButton ? 'hover:pr-1' : '' } ${borderColor} `}
>
{name}
<IconButton
{hasButton && <IconButton
iconClassName='!w-4' className={`hidden !p-0 px-1 group-hover:block ${textColor}`} src={xIcon} onClick={() => onChangeFilters((prevValue) => {
return prevValue.filter((x) => x.name !== name);
})}
/>
/>}
</div>
);
};

Wyświetl plik

@ -17,9 +17,10 @@ import {
useSuggestions,
} from 'soapbox/queries/suggestions.ts';
// @ts-ignore
import 'swiper/css';
const PeopleToFollowCard = ({ id }: { id: string }) => {
const PopularAccounts = ({ id }: { id: string }) => {
const account = useAccount(id).account;
const { logo } = useSoapboxConfig();
@ -70,7 +71,7 @@ const PeopleToFollowCard = ({ id }: { id: string }) => {
const AccountsCarousel = () => {
const isMobile = useIsMobile();
const { data: suggestions, isFetching } = useSuggestions();
const [isOpen, setIsOpen] = useState(false);
const [isOpen, setIsOpen] = useState(true);
if (!isFetching && !suggestions.length) {
@ -101,7 +102,7 @@ const AccountsCarousel = () => {
>
{suggestions.map((suggestion) => (
<SwiperSlide key={suggestion.account}>
<PeopleToFollowCard id={suggestion.account} />
<PopularAccounts id={suggestion.account} />
</SwiperSlide>
))}
</Swiper>

Wyświetl plik

@ -4,20 +4,19 @@ 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 SearchResults from 'soapbox/features/compose/components/search-results.tsx';
import Search from 'soapbox/features/compose/components/search.tsx';
import ExplorerCards from 'soapbox/features/search/components/explorer-cards.tsx';
import ExplorerFilter from 'soapbox/features/search/components/explorerFilter.tsx';
import AccountsCarousel from 'soapbox/features/search/components/people-to-follow-card.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';
const messages = defineMessages({
heading: { id: 'column.search', defaultMessage: 'Explorer' },
heading: { id: 'column.explorer', defaultMessage: 'Explorer' },
});
const SearchPage = () => {
const intl = useIntl();
return (
<Column label={intl.formatMessage(messages.heading)} slim>
<Column label={intl.formatMessage(messages.heading)} withHeader={false} slim>
<Stack space={4}>
<ExplorerCards />
@ -32,11 +31,8 @@ const SearchPage = () => {
<Divider />
<div className='px-4'>
<Search autoSubmit />
</div>
<SearchResults />
</Stack>
</Column>
);

Wyświetl plik

@ -70,6 +70,7 @@ const PublicTimeline = () => {
return (
<Column
withHeader={false}
label={intl.formatMessage(messages.title)}
action={features.publicTimelineLanguage ? <LanguageDropdown language={language} setLanguage={setLanguage} /> : null}
slim

Wyświetl plik

@ -38,7 +38,7 @@ const TrendsPanel = ({ limit }: ITrendsPanel) => {
<Widget
title={<FormattedMessage id='trends.title' defaultMessage='Trends' />}
action={
<Link className='text-right' to='/search' onClick={setHashtagsFilter}>
<Link className='text-right' to='/explorer' onClick={setHashtagsFilter}>
<Text tag='span' theme='primary' size='sm' className='hover:underline'>
{intl.formatMessage(messages.viewAll)}
</Text>

Wyświetl plik

@ -48,7 +48,6 @@ import Navbar from './components/navbar.tsx';
import {
Status,
CommunityTimeline,
PublicTimeline,
RemoteTimeline,
AccountTimeline,
AccountGallery,
@ -194,7 +193,6 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
https://stackoverflow.com/a/68637108
*/}
{features.federating && <WrappedRoute path='/timeline/local' exact page={HomePage} component={CommunityTimeline} content={children} publicRoute />}
{features.federating && <WrappedRoute path='/timeline/global' exact page={HomePage} component={PublicTimeline} content={children} publicRoute />}
{features.federating && <WrappedRoute path='/timeline/:instance' exact page={RemoteInstancePage} component={RemoteTimeline} content={children} publicRoute />}
{features.conversations && <WrappedRoute path='/conversations' page={DefaultPage} component={Conversations} content={children} />}
@ -209,11 +207,11 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
<Redirect from='/web/:path' to='/:path' />
<Redirect from='/timelines/home' to='/' />
<Redirect from='/timelines/public/local' to='/timeline/local' />
<Redirect from='/timelines/public' to='/timeline/global' />
<Redirect from='/timelines/public' to='/explorer' />
<Redirect from='/timelines/direct' to='/messages' />
{/* Pleroma FE web routes */}
<Redirect from='/main/all' to='/timeline/global' />
<Redirect from='/main/all' to='/explorer' />
<Redirect from='/main/public' to='/timeline/local' />
<Redirect from='/main/friends' to='/' />
<Redirect from='/tag/:id' to='/tags/:id' />
@ -251,7 +249,7 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
<Redirect from='/auth/mfa' to='/settings/mfa' />
<Redirect from='/auth/password/new' to='/reset-password' />
<Redirect from='/auth/password/edit' to={`/edit-password${search}`} />
<Redirect from='/timeline/fediverse' to='/timeline/global' />
<Redirect from='/timeline/fediverse' to='/explorer' />
<WrappedRoute path='/tags/:id' publicRoute page={DefaultPage} component={HashtagTimeline} content={children} />
@ -261,7 +259,7 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
<WrappedRoute path='/notifications' page={DefaultPage} component={Notifications} content={children} />
<WrappedRoute path='/search' page={SearchPage} component={Search} content={children} publicRoute />
<WrappedRoute path='/explorer' page={SearchPage} component={Search} content={children} publicRoute />
{features.suggestionsLocal && <WrappedRoute path='/suggestions/local' publicRoute page={DefaultPage} component={FollowRecommendations} content={children} componentParams={{ local: true }} />}
{features.suggestions && <WrappedRoute path='/suggestions' exact publicRoute page={DefaultPage} component={FollowRecommendations} content={children} />}
{features.profileDirectory && <WrappedRoute path='/directory' exact publicRoute page={DefaultPage} component={Directory} content={children} />}

Wyświetl plik

@ -58,7 +58,7 @@ export const BirthdaysModal = lazy(() => import('soapbox/features/ui/components/
export const BirthdayPanel = lazy(() => import('soapbox/components/birthday-panel.tsx'));
export const ListEditor = lazy(() => import('soapbox/features/list-editor/index.tsx'));
export const ListAdder = lazy(() => import('soapbox/features/list-adder/index.tsx'));
export const Search = lazy(() => import('soapbox/features/search/index.tsx'));
export const Search = lazy(() => import('soapbox/features/explorer/index.tsx'));
export const LoginPage = lazy(() => import('soapbox/features/auth-login/components/login-page.tsx'));
export const ExternalLogin = lazy(() => import('soapbox/features/external-login/index.tsx'));
export const LogoutPage = lazy(() => import('soapbox/features/auth-login/components/logout.tsx'));

Wyświetl plik

@ -368,6 +368,7 @@
"column.event_map": "Event location",
"column.event_participants": "Event participants",
"column.events": "Events",
"column.explorer": "Explorer",
"column.explorer.bridge_card.text": "Curious about Bridges? <a>Click here</a>",
"column.explorer.bridge_card.title": "Bridge",
"column.explorer.filters": "Filters:",
@ -451,7 +452,6 @@
"column.reblogs": "Reposts",
"column.registration": "Sign Up",
"column.scheduled_statuses": "Scheduled Posts",
"column.search": "Explorer",
"column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config",
"column.test": "Test timeline",
@ -459,6 +459,7 @@
"column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden",
"common.cancel": "Cancel",
"common.search": "Search",
"compare_history_modal.header": "Edit history",
"compose.character_counter.title": "Used {chars} out of {maxChars} {maxChars, plural, one {character} other {characters}}",
"compose.edit_success": "Your post was edited",
@ -1442,6 +1443,10 @@
"scheduled_status.cancel": "Cancel",
"search.action": "Search for “{query}”",
"search.placeholder": "Search",
"search_results.accounts": "Posts",
"search_results.filter_message": "You are searching for posts from @{acct}.",
"search_results.posts": "Accounts",
"search_results.trends": "Trends",
"security.codes.fail": "Failed to fetch backup codes",
"security.confirm.fail": "Incorrect code or password. Try again.",
"security.delete_account.fail": "Account deletion failed.",