Merge branch 'lang-stream' into 'main'

Rework LanguageDropdown, disable streaming when a language is set

Closes #1745

See merge request soapbox-pub/soapbox!3137
environments/review-main-yi2y9f/deployments/4869
Alex Gleason 2024-10-06 04:18:15 +00:00
commit 1de38c5ac7
4 zmienionych plików z 44 dodań i 51 usunięć

Wyświetl plik

@ -213,8 +213,8 @@ const expandHomeTimeline = ({ url, maxId }: ExpandHomeTimelineOpts = {}, done =
return expandTimeline('home', endpoint, params, done); return expandTimeline('home', endpoint, params, done);
}; };
const expandPublicTimeline = ({ url, maxId, onlyMedia }: Record<string, any> = {}, done = noOp) => const expandPublicTimeline = ({ url, maxId, onlyMedia, language }: Record<string, any> = {}, done = noOp) =>
expandTimeline(`public${onlyMedia ? ':media' : ''}`, url || '/api/v1/timelines/public', url ? {} : { max_id: maxId, only_media: !!onlyMedia }, done); expandTimeline(`public${onlyMedia ? ':media' : ''}`, url || '/api/v1/timelines/public', url ? {} : { max_id: maxId, only_media: !!onlyMedia, language: language || undefined }, done);
const expandRemoteTimeline = (instance: string, { url, maxId, onlyMedia }: Record<string, any> = {}, done = noOp) => const expandRemoteTimeline = (instance: string, { url, maxId, onlyMedia }: Record<string, any> = {}, done = noOp) =>
expandTimeline(`remote${onlyMedia ? ':media' : ''}:${instance}`, url || '/api/v1/timelines/public', url ? {} : { local: false, instance: instance, max_id: maxId, only_media: !!onlyMedia }, done); expandTimeline(`remote${onlyMedia ? ':media' : ''}:${instance}`, url || '/api/v1/timelines/public', url ? {} : { local: false, instance: instance, max_id: maxId, only_media: !!onlyMedia }, done);

Wyświetl plik

@ -2,12 +2,16 @@ import { useTimelineStream } from './useTimelineStream';
interface UsePublicStreamOpts { interface UsePublicStreamOpts {
onlyMedia?: boolean; onlyMedia?: boolean;
language?: string;
} }
function usePublicStream({ onlyMedia }: UsePublicStreamOpts = {}) { function usePublicStream({ onlyMedia, language }: UsePublicStreamOpts = {}) {
return useTimelineStream( return useTimelineStream(
`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`,
`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`,
null,
null,
{ enabled: !language }, // TODO: support language streaming
); );
} }

Wyświetl plik

@ -1,54 +1,43 @@
import React, { useState } from 'react'; import React from 'react';
import { openDropdownMenu } from 'soapbox/actions/dropdown-menu'; import { openDropdownMenu } from 'soapbox/actions/dropdown-menu';
import { clearTimeline, expandPublicTimeline } from 'soapbox/actions/timelines'; import DropdownMenu, { MenuItem } from 'soapbox/components/dropdown-menu';
import DropdownMenu from 'soapbox/components/dropdown-menu';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
import { languages } from 'soapbox/features/preferences'; import { languages } from 'soapbox/features/preferences';
import { useAppDispatch } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks';
const formatterLanguage = (lang: {}) => { function formatLanguages(languageMap: Record<string, string>) {
const langCodes = Object.keys(languageMap).sort().map((sig) => {
const sigLanguage = Object.keys(lang).sort().map((sig) => {
return sig.substring(0, 2); return sig.substring(0, 2);
}); });
return [...new Set(sigLanguage)]; return [...new Set(langCodes)];
}; }
interface ILanguageDropdown {
language: string;
setLanguage(language: string): void;
}
/** /**
* * Let the user select a language to filter the public timeline.
*/ */
const LanguageDropdown = () => { const LanguageDropdown: React.FC<ILanguageDropdown> = ({ language, setLanguage }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [languageIcon, setLanguageIcon] = useState(''); const formattedLanguages = formatLanguages(languages);
const handleChangeLanguage = (language?: string) => { const newMenu: MenuItem[] = [{ icon: require('@tabler/icons/outline/world.svg'), text: 'Default', action: () => {
if (language) { setLanguage('');
dispatch(clearTimeline('public'));
dispatch(expandPublicTimeline({ url: `/api/v1/timelines/public?language=${language}` }));
} else {
dispatch(clearTimeline('public'));
dispatch(expandPublicTimeline({ url: '/api/v1/timelines/public' }));
}
};
const formattedLanguage = formatterLanguage(languages);
const newMenu: any[] = [{ icon: require('@tabler/icons/outline/world.svg'), text: 'Default', action: () => {
setLanguageIcon('');
handleChangeLanguage();
} }]; } }];
formattedLanguage.map((lg: string) => { formattedLanguages.map((lang: string) => {
const languageText = languages[lg as keyof typeof languages]; const languageName = languages[lang as keyof typeof languages];
if (languageText !== undefined) { if (languageName) {
newMenu.push({ newMenu.push({
text: `${lg.toUpperCase()} - ${languageText}`, text: `${lang.toUpperCase()} - ${languageName}`,
action: () => { action: () => {
setLanguageIcon(lg.toUpperCase()); setLanguage(lang);
handleChangeLanguage(lg);
}, },
}); });
} }
@ -57,14 +46,13 @@ const LanguageDropdown = () => {
return ( return (
<DropdownMenu items={newMenu} modal> <DropdownMenu items={newMenu} modal>
{ languageIcon === '' ? {language ? (
<SvgIcon src={require('@tabler/icons/outline/world.svg')} className='text-gray-700 hover:cursor-pointer hover:text-gray-500 black:absolute black:right-0 black:top-4 black:text-white black:hover:text-gray-600 sm:mr-4 dark:text-white' />
:
<button type='button' className='flex h-full rounded-lg border-2 border-gray-700 px-1 text-gray-700 hover:cursor-pointer hover:border-gray-500 hover:text-gray-500 sm:mr-4 dark:border-white dark:text-white dark:hover:border-gray-700' onClick={() => dispatch(openDropdownMenu())}> <button type='button' className='flex h-full rounded-lg border-2 border-gray-700 px-1 text-gray-700 hover:cursor-pointer hover:border-gray-500 hover:text-gray-500 sm:mr-4 dark:border-white dark:text-white dark:hover:border-gray-700' onClick={() => dispatch(openDropdownMenu())}>
{languageIcon} {language.toUpperCase()}
</button> </button>
} ) : (
<SvgIcon src={require('@tabler/icons/outline/world.svg')} className='text-gray-700 hover:cursor-pointer hover:text-gray-500 black:absolute black:right-0 black:top-4 black:text-white black:hover:text-gray-600 sm:mr-4 dark:text-white' />
)}
</DropdownMenu> </DropdownMenu>
); );
}; };

Wyświetl plik

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@ -25,6 +25,8 @@ const PublicTimeline = () => {
const features = useFeatures(); const features = useFeatures();
const theme = useTheme(); const theme = useTheme();
const [language, setLanguage] = useState<string>(localStorage.getItem('soapbox:global:language') || '');
const instance = useInstance(); const instance = useInstance();
const settings = useSettings(); const settings = useSettings();
const onlyMedia = settings.public.other.onlyMedia; const onlyMedia = settings.public.other.onlyMedia;
@ -52,24 +54,23 @@ const PublicTimeline = () => {
return dispatch(expandPublicTimeline({ onlyMedia })); return dispatch(expandPublicTimeline({ onlyMedia }));
}; };
usePublicStream({ onlyMedia }); usePublicStream({ onlyMedia, language });
useEffect(() => {
dispatch(expandPublicTimeline({ onlyMedia }));
}, [onlyMedia]);
useEffect(() => { useEffect(() => {
dispatch(clearTimeline('public')); dispatch(clearTimeline('public'));
dispatch(expandPublicTimeline({ url: '/api/v1/timelines/public' })); localStorage.setItem('soapbox:global:language', language);
}, []); }, [language]);
useEffect(() => {
dispatch(expandPublicTimeline({ onlyMedia, language }));
}, [onlyMedia, language]);
return ( return (
<Column <Column
className='-mt-3 sm:mt-0' className='-mt-3 sm:mt-0'
label={intl.formatMessage(messages.title)} label={intl.formatMessage(messages.title)}
transparent={!isMobile} transparent={!isMobile}
action={features.publicTimelineLanguage ? <LanguageDropdown /> : null} action={features.publicTimelineLanguage ? <LanguageDropdown language={language} setLanguage={setLanguage} /> : null}
// actionRightPosition
> >
<PinnedHostsPicker /> <PinnedHostsPicker />