|
|
|
|
@ -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 };
|