pl-fe: Rename Privacy page before adding non-URL related stuff

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-10-18 17:08:59 +02:00
parent 550de1cddb
commit c9048198d9
5 changed files with 7 additions and 218 deletions

View File

@ -148,7 +148,7 @@ import {
StatusHoverCard,
TestTimeline,
ThemeEditor,
UrlPrivacy,
Privacy,
UserIndex,
WrenchedTimeline,
} from './util/async-components';
@ -325,7 +325,8 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = React.memo(({ chil
<WrappedRoute path='/settings/mfa' layout={DefaultLayout} component={MfaForm} exact />
<WrappedRoute path='/settings/tokens' layout={DefaultLayout} component={AuthTokenList} content={children} />
{features.interactionRequests && <WrappedRoute path='/settings/interaction_policies' layout={DefaultLayout} component={InteractionPolicies} content={children} />}
<WrappedRoute path='/settings/url_privacy' layout={DefaultLayout} component={UrlPrivacy} content={children} />
<Redirect from='/settings/url_privacy' to='/settings/privacy' />
<WrappedRoute path='/settings/privacy' layout={DefaultLayout} component={Privacy} content={children} />
<WrappedRoute path='/settings' layout={DefaultLayout} component={Settings} content={children} />
<WrappedRoute path='/pl-fe/config' adminOnly layout={DefaultLayout} component={PlFeConfig} content={children} />

View File

@ -95,7 +95,7 @@ export const Share = lazy(() => import('pl-fe/pages/utils/share'));
export const Status = lazy(() => import('pl-fe/pages/statuses/status'));
export const TestTimeline = lazy(() => import('pl-fe/pages/timelines/test-timeline'));
export const ThemeEditor = lazy(() => import('pl-fe/pages/dashboard/theme-editor'));
export const UrlPrivacy = lazy(() => import('pl-fe/pages/settings/url-privacy'));
export const Privacy = lazy(() => import('pl-fe/pages/settings/privacy'));
export const UserIndex = lazy(() => import('pl-fe/pages/dashboard/user-index'));
export const WrenchedTimeline = lazy(() => import('pl-fe/pages/timelines/wrenched-timeline'));

View File

@ -1596,6 +1596,7 @@
"settings.mutes_blocks": "Mutes and blocks",
"settings.other": "Other options",
"settings.preferences": "Preferences",
"settings.privacy": "Privacy",
"settings.profile": "Profile",
"settings.save.success": "Your preferences have been saved!",
"settings.security": "Security",

View File

@ -37,7 +37,7 @@ const messages = defineMessages({
security: { id: 'settings.security', defaultMessage: 'Security' },
sessions: { id: 'settings.sessions', defaultMessage: 'Active sessions' },
settings: { id: 'settings.settings', defaultMessage: 'Settings' },
urlPrivacy: { id: 'settings.url_privacy', defaultMessage: 'URL privacy' },
privacy: { id: 'settings.privacy', defaultMessage: 'Privacy' },
});
/** User settings page. */
@ -105,7 +105,7 @@ const SettingsPage = () => {
{features.sessions && (
<ListItem label={intl.formatMessage(messages.sessions)} to='/settings/tokens' />
)}
<ListItem label={intl.formatMessage(messages.urlPrivacy)} to='/settings/url_privacy' />
<ListItem label={<FormattedMessage id='settings.privacy' defaultMessage='Privacy' />} to='/settings/url_privacy' />
</List>
</CardBody>

View File

@ -1,213 +0,0 @@
import { mappings } from '@mkljczk/url-purify';
import React, { useEffect, useState } from 'react';
import { defineMessages, FormattedList, FormattedMessage, useIntl } from 'react-intl';
import { useMutative } from 'use-mutative';
import { changeSetting } from 'pl-fe/actions/settings';
import List, { ListItem } from 'pl-fe/components/list';
import Button from 'pl-fe/components/ui/button';
import Card, { CardBody, CardHeader, CardTitle } from 'pl-fe/components/ui/card';
import Column from 'pl-fe/components/ui/column';
import Form from 'pl-fe/components/ui/form';
import FormActions from 'pl-fe/components/ui/form-actions';
import FormGroup from 'pl-fe/components/ui/form-group';
import Input from 'pl-fe/components/ui/input';
import Toggle from 'pl-fe/components/ui/toggle';
import { SelectDropdown } from 'pl-fe/features/forms';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useSettings } from 'pl-fe/hooks/use-settings';
import KVStore from 'pl-fe/storage/kv-store';
import { KVStoreRedirectServicesItem } from 'pl-fe/utils/url-purify';
const messages = defineMessages({
urlPrivacy: { id: 'settings.url_privacy', defaultMessage: 'URL privacy' },
rulesUrlPlaceholder: { id: 'url_privacy.rules_url.placeholder', defaultMessage: 'Rules URL' },
hashUrlPlaceholder: { id: 'url_privacy.hash_url.placeholder', defaultMessage: 'Hash URL' },
redirectLinksModeOff: { id: 'url_privacy.redirect_links_mode.off', defaultMessage: 'Disabled' },
redirectLinksModeAuto: { id: 'url_privacy.redirect_links_mode.auto', defaultMessage: 'From URL' },
redirectLinksModeManual: { id: 'url_privacy.redirect_links_mode.manual', defaultMessage: 'Specify manually' },
redirectServicesUrlPlaceholder: { id: 'url_privacy.redirect_services_url.placeholder', defaultMessage: 'Rules URL' },
redirectServicePlaceholder: { id: 'url_privacy.redirect_services_url.placeholder', defaultMessage: 'eg. https://proxy.example.org' },
});
const UrlPrivacy = () => {
const dispatch = useAppDispatch();
const me = useAppSelector((state) => state.me);
const intl = useIntl();
const { urlPrivacy } = useSettings();
const [displayTargetHost, setDisplayTargetHost] = useState(urlPrivacy.displayTargetHost);
const [clearLinksInCompose, setClearLinksInCompose] = useState(urlPrivacy.clearLinksInCompose);
const [clearLinksInContent, setClearLinksInContent] = useState(urlPrivacy.clearLinksInContent);
const [hashUrl, setHashUrl] = useState(urlPrivacy.hashUrl);
const [rulesUrl, setRulesUrl] = useState(urlPrivacy.rulesUrl);
const [redirectLinksMode, setRedirectLinksMode] = useState(urlPrivacy.redirectLinksMode);
const [redirectServicesUrl, setRedirectServicesUrl] = useState(urlPrivacy.redirectServicesUrl);
const [redirectServices, setRedirectServices] = useMutative(urlPrivacy.redirectServices);
const onSubmit = () => {
const value = {
...urlPrivacy,
displayTargetHost,
clearLinksInCompose,
clearLinksInContent,
hashUrl,
rulesUrl,
redirectLinksMode,
redirectServicesUrl,
redirectServices,
};
switch (redirectLinksMode) {
case 'off':
value.redirectServicesUrl = '';
value.redirectServices = {};
break;
case 'manual':
value.redirectServicesUrl = '';
break;
case 'auto':
value.redirectServices = {};
break;
}
dispatch(changeSetting(['urlPrivacy'], value, {
save: true,
showAlert: true,
}));
};
const handleChangeRedirectLinksMode = (event: React.ChangeEvent<HTMLSelectElement>) => {
if (redirectLinksMode === 'auto' && event.target.value === 'manual') {
KVStore.getItem<KVStoreRedirectServicesItem>(`url-purify-redirect-services:${me}`).then((services) => {
if (!services?.redirectServices) return;
setRedirectServices(
Object.fromEntries(
mappings.map(({ name, targets }) => ([
name,
services.redirectServices.find((service) => targets.includes(service.type) && service.instances.length)?.instances[0].split('|')[0] || '',
])),
),
);
}).catch(() => { });
}
return setRedirectLinksMode(event.target.value as 'off');
};
useEffect(() => {
}, [dispatch]);
return (
<Column label={intl.formatMessage(messages.urlPrivacy)} transparent withHeader={false}>
<Card className='space-y-4' variant='rounded'>
<CardHeader backHref='/settings'>
<CardTitle title={intl.formatMessage(messages.urlPrivacy)} />
</CardHeader>
<CardBody>
<Form onSubmit={onSubmit}>
<List>
<ListItem label={<FormattedMessage id='url_privacy.display_target_host' defaultMessage='Always display the domain external links lead to' />}>
<Toggle checked={displayTargetHost} onChange={({ target }) => setDisplayTargetHost(target.checked)} />
</ListItem>
</List>
<List>
<ListItem label={<FormattedMessage id='url_privacy.clear_links_in_compose' defaultMessage='Suggest removing tracking parameters when composing a post' />}>
<Toggle checked={clearLinksInCompose} onChange={({ target }) => setClearLinksInCompose(target.checked)} />
</ListItem>
<ListItem label={<FormattedMessage id='url_privacy.clear_links_in_content' defaultMessage='Remove tracking parameters from displayed posts' />}>
<Toggle checked={clearLinksInContent} onChange={({ target }) => setClearLinksInContent(target.checked)} />
</ListItem>
</List>
<FormGroup
labelText={<FormattedMessage id='url_privacy.rules_url.label' defaultMessage='URL cleaning rules database address' />}
hintText={<FormattedMessage id='url_privacy.rules_url.hint' defaultMessage='Rules database in ClearURLs-compatible format, eg. {url}' values={{ url: 'https://rules2.clearurls.xyz/data.minify.json' }} />}
>
<Input
type='text'
placeholder={intl.formatMessage(messages.rulesUrlPlaceholder)}
value={rulesUrl}
onChange={({ target }) => setRulesUrl(target.value)}
/>
</FormGroup>
<FormGroup
labelText={<FormattedMessage id='url_privacy.hash_url.label' defaultMessage='URL cleaning rules hash address (optional)' />}
hintText={<FormattedMessage id='url_privacy.hash_url.hint' defaultMessage='SHA256 hash of rules database, used to avoid unnecessary fetches, eg. {url}' values={{ url: 'https://rules2.clearurls.xyz/rules.minify.hash' }} />}
>
<Input
type='text'
placeholder={intl.formatMessage(messages.hashUrlPlaceholder)}
value={hashUrl}
onChange={({ target }) => setHashUrl(target.value)}
/>
</FormGroup>
<List>
<ListItem label={<FormattedMessage id='url_privacy.redirect_links_mode' defaultMessage='Redirect links to popular websites to privacy-respecting proxy services' />}>
<SelectDropdown
className='max-w-fit'
items={{
off: intl.formatMessage(messages.redirectLinksModeOff),
auto: intl.formatMessage(messages.redirectLinksModeAuto),
manual: intl.formatMessage(messages.redirectLinksModeManual),
}}
defaultValue={redirectLinksMode}
onChange={handleChangeRedirectLinksMode}
/>
</ListItem>
</List>
{redirectLinksMode === 'auto' && (
<FormGroup
labelText={<FormattedMessage id='url_privacy.redirect_services_url.label' defaultMessage='Redirect services URLs database address' />}
hintText={<FormattedMessage id='url_privacy.redirect_services_url.hint' defaultMessage='URLs database in Farside-compatible format, eg. {url}' values={{ url: 'https://raw.githubusercontent.com/benbusby/farside/refs/heads/main/services.json' }} />}
>
<Input
type='text'
placeholder={intl.formatMessage(messages.redirectServicesUrlPlaceholder)}
value={redirectServicesUrl}
onChange={({ target }) => setRedirectServicesUrl(target.value)}
/>
</FormGroup>
)}
{redirectLinksMode === 'manual' && (
mappings.map((service) => (
<FormGroup
key={service.name}
labelText={<FormattedMessage id='url_privacy.redirect_services.name' defaultMessage='{name}' values={{ name: service.name }} />}
hintText={<FormattedMessage id='url_privacy.redirect_services.patterns' defaultMessage='Matches: {pattern}, eg. {services}, leave empty for no redirect' values={{ pattern: service.urlPattern, services: <FormattedList value={service.targets} /> }} />}
>
<Input
outerClassName='grow'
type='text'
value={redirectServices[service.name]}
onChange={(e) => setRedirectServices((services) => {
services[service.name] = e.target.value;
})}
placeholder={intl.formatMessage(messages.redirectServicePlaceholder)}
/>
</FormGroup>
))
)}
<FormActions>
<Button type='submit'>
<FormattedMessage id='url_privacy.save' defaultMessage='Save' />
</Button>
</FormActions>
</Form>
</CardBody>
</Card>
</Column>
);
};
export { UrlPrivacy as default };