feat(trust): Settings -> Trust and name AGPL on the License page
- Settings -> Trust section (?section=trust, TrustSettingsCard): a show/hide-marks toggle (useTrustDisplay; Flagged is never hidden) and the active provider + relay (useTrustProvider), linked to /trust. - TrustBadge respects the display preference. - Name AGPL-3.0 on the License page by updating its existing copy (licenseLabel + body) in en/es. - Settings + card i18n keys; /settings?section=trust added to the smoke list. Docs updated.
This commit is contained in:
parent
71ecb03ae8
commit
fd9316331b
10 changed files with 167 additions and 12 deletions
11
AGENTS.md
11
AGENTS.md
|
|
@ -2545,9 +2545,14 @@ listing detail and coach detail, so those surfaces get it for free. The provider
|
||||||
read is **batched** (one query per viewer for the union of on-screen subjects,
|
read is **batched** (one query per viewer for the union of on-screen subjects,
|
||||||
in `useTrustAssertions.ts`), and `useUserReports` + `components/TrustFlaggedNotice.tsx`
|
in `useTrustAssertions.ts`), and `useUserReports` + `components/TrustFlaggedNotice.tsx`
|
||||||
add a flagged-counterparty notice (reporters grouped by the viewer's network,
|
add a flagged-counterparty notice (reporters grouped by the viewer's network,
|
||||||
Report/Dismiss) wired into the listing seller and coach detail. Still open: a
|
Report/Dismiss) wired into the listing seller and coach detail. **Legibility
|
||||||
provider-choice setting, a mute action (no NIP-51 kind-10000 hook exists yet),
|
pass:** the badge tooltip attributes the source (your network / community
|
||||||
counterparty alerts, and the confidence-gated scoring model. Check
|
provider / your provider) and links to a public **`/trust`** explainer;
|
||||||
|
**Settings → Trust** (`?section=trust`, `components/TrustSettingsCard.tsx`) has a
|
||||||
|
show/hide-marks toggle (`useTrustDisplay`, Flagged is never hidden) and shows the
|
||||||
|
active provider + relay (`useTrustProvider`). Still open: provider choice
|
||||||
|
(publishing a kind-10040 designation), a mute action (no NIP-51 kind-10000 hook
|
||||||
|
exists yet), counterparty alerts, and the confidence-gated scoring model. Check
|
||||||
`TRUST_SCORE.md` before touching `useTrustScore` / `useRelatrScore` /
|
`TRUST_SCORE.md` before touching `useTrustScore` / `useRelatrScore` /
|
||||||
`trustFilter`.
|
`trustFilter`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,22 @@ the provider instead of only using the designation or house default.
|
||||||
safety signal returns). Wired into the listing seller card and the coach
|
safety signal returns). Wired into the listing seller card and the coach
|
||||||
detail page.
|
detail page.
|
||||||
|
|
||||||
Still open: a Settings control to pick the trust provider; a mute action (there
|
## Third pass — legibility (landed)
|
||||||
is no NIP-51 kind-10000 hook yet — Dismiss is the local equivalent for now);
|
|
||||||
counterparty alerts; and the confidence-gated scoring model.
|
- **Attribution.** The badge tooltip names the source of the claim (your network /
|
||||||
|
the community provider / the provider you chose) and links to an explainer.
|
||||||
|
- **Public `/trust` page.** Explains the three states, that absence is not
|
||||||
|
distrust, who computes trust, presence-is-not-endorsement, and
|
||||||
|
point-of-view relativity. Added to the smoke-test route list.
|
||||||
|
- **Settings → Trust** (`?section=trust`, `components/TrustSettingsCard.tsx`): a
|
||||||
|
show/hide-marks toggle (`useTrustDisplay`, localStorage
|
||||||
|
`skatespots:trust:display`; **Flagged is never hidden**) and the active
|
||||||
|
provider + relay (`useTrustProvider`), with a link to `/trust`.
|
||||||
|
|
||||||
|
Still open: provider *choice* (publishing a kind-10040 designation, i.e. an
|
||||||
|
in-app NIP-85 activation flow), a mute action (there is no NIP-51 kind-10000 hook
|
||||||
|
yet — Dismiss is the local equivalent for now), counterparty alerts, and the
|
||||||
|
confidence-gated scoring model.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ const ROUTES = [
|
||||||
'/settings?section=profile',
|
'/settings?section=profile',
|
||||||
'/settings?section=coordinator',
|
'/settings?section=coordinator',
|
||||||
'/settings?section=pro',
|
'/settings?section=pro',
|
||||||
|
'/settings?section=trust',
|
||||||
'/npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx',
|
'/npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx',
|
||||||
'/stash',
|
'/stash',
|
||||||
'/collections',
|
'/collections',
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp
|
||||||
import { CheckCircle2, HelpCircle, Flag } from 'lucide-react';
|
import { CheckCircle2, HelpCircle, Flag } from 'lucide-react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useTrustBucket } from '@/hooks/useTrustBucket';
|
import { useTrustBucket } from '@/hooks/useTrustBucket';
|
||||||
|
import { useTrustDisplay } from '@/hooks/useTrustDisplay';
|
||||||
import { bucketDescKey, bucketLabelKey, type TrustBucket } from '@/lib/trustLadder';
|
import { bucketDescKey, bucketLabelKey, type TrustBucket } from '@/lib/trustLadder';
|
||||||
import { HOUSE_TRUST_PROVIDER } from '@/lib/nip85';
|
import { HOUSE_TRUST_PROVIDER } from '@/lib/nip85';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
@ -53,6 +54,7 @@ export function TrustBadge({
|
||||||
}: TrustBadgeProps) {
|
}: TrustBadgeProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { bucket, assertion, directFollow, isLoading } = useTrustBucket(pubkey);
|
const { bucket, assertion, directFollow, isLoading } = useTrustBucket(pubkey);
|
||||||
|
const { enabled: displayEnabled } = useTrustDisplay();
|
||||||
|
|
||||||
// Always attribute the claim: a verdict with no source is not trustworthy.
|
// Always attribute the claim: a verdict with no source is not trustworthy.
|
||||||
const sourceKey = assertion
|
const sourceKey = assertion
|
||||||
|
|
@ -62,6 +64,9 @@ export function TrustBadge({
|
||||||
: 'walletUi.trust.attributedNetwork';
|
: 'walletUi.trust.attributedNetwork';
|
||||||
|
|
||||||
if (isLoading) return null;
|
if (isLoading) return null;
|
||||||
|
// A viewer preference can hide the neutral/positive marks; the safety signal
|
||||||
|
// (Flagged) is never hidden by it.
|
||||||
|
if (!displayEnabled && bucket !== 'flagged') return null;
|
||||||
// A direct follow (or yourself) is already known to the viewer — a "Verified"
|
// A direct follow (or yourself) is already known to the viewer — a "Verified"
|
||||||
// mark there is redundant. Flagged is a safety signal and is never suppressed.
|
// mark there is redundant. Flagged is a safety signal and is never suppressed.
|
||||||
if (bucket === 'verified' && directFollow) return null;
|
if (bucket === 'verified' && directFollow) return null;
|
||||||
|
|
|
||||||
48
src/components/TrustSettingsCard.tsx
Normal file
48
src/components/TrustSettingsCard.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import { useTranslation } from '@/contexts/language';
|
||||||
|
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||||
|
import { useTrustDisplay } from '@/hooks/useTrustDisplay';
|
||||||
|
import { useTrustProvider } from '@/hooks/useTrustProvider';
|
||||||
|
import { HOUSE_TRUST_PROVIDER } from '@/lib/nip85';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings → Trust. How trust is displayed, and which provider is in effect.
|
||||||
|
* Flagged always shows, so turning marks off is a display choice, not a way to
|
||||||
|
* silence a safety signal.
|
||||||
|
*/
|
||||||
|
export function TrustSettingsCard() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { user } = useCurrentUser();
|
||||||
|
const { enabled, setEnabled } = useTrustDisplay();
|
||||||
|
const { data: provider } = useTrustProvider();
|
||||||
|
|
||||||
|
const isHouse = !provider || provider.pubkey === HOUSE_TRUST_PROVIDER.pubkey;
|
||||||
|
const source = isHouse ? t('trustUi.sourceHouse') : t('trustUi.sourceCustom');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-white/3 p-4">
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="text-sm font-bold text-white">{t('trustUi.showMarks')}</p>
|
||||||
|
<p className="mt-1 text-xs text-muted-foreground">{t('trustUi.showMarksDesc')}</p>
|
||||||
|
</div>
|
||||||
|
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2 rounded-2xl border border-white/10 bg-white/3 p-4">
|
||||||
|
<p className="text-sm font-bold text-white">{t('trustUi.sourceLabel')}</p>
|
||||||
|
<p className="text-xs text-muted-foreground">{source}</p>
|
||||||
|
{user && provider?.relay && (
|
||||||
|
<p className="break-all text-xs text-muted-foreground">{provider.relay}</p>
|
||||||
|
)}
|
||||||
|
{!user && <p className="text-xs text-muted-foreground">{t('trustUi.loggedOut')}</p>}
|
||||||
|
<Link to="/trust" className="inline-block text-xs text-[#ccff00] underline">
|
||||||
|
{t('walletUi.trust.howItWorks')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
11
src/hooks/useTrustDisplay.ts
Normal file
11
src/hooks/useTrustDisplay.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { useLocalStorage } from './useLocalStorage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Viewer preference for whether trust marks are shown. Only the neutral/positive
|
||||||
|
* marks are affected — the safety signal (Flagged) is deliberately independent
|
||||||
|
* of this and always shows.
|
||||||
|
*/
|
||||||
|
export function useTrustDisplay() {
|
||||||
|
const [enabled, setEnabled] = useLocalStorage<boolean>('skatespots:trust:display', true);
|
||||||
|
return { enabled, setEnabled };
|
||||||
|
}
|
||||||
40
src/hooks/useTrustProvider.ts
Normal file
40
src/hooks/useTrustProvider.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useNostr } from '@nostrify/react';
|
||||||
|
|
||||||
|
import { useCurrentUser } from './useCurrentUser';
|
||||||
|
import { onlyVerified } from '@/lib/verifyEvents';
|
||||||
|
import {
|
||||||
|
HOUSE_TRUST_PROVIDER,
|
||||||
|
NIP85_PROVIDER_LIST_KIND,
|
||||||
|
NIP85_USER_ASSERTION_KIND,
|
||||||
|
parseProviderDesignation,
|
||||||
|
resolveProvider,
|
||||||
|
type TrustProvider,
|
||||||
|
} from '@/lib/nip85';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The trust provider currently in effect for the viewer: the one named in their
|
||||||
|
* own kind-10040 designation, or the house provider when there is no
|
||||||
|
* designation. Read-only — publishing a designation is a separate step.
|
||||||
|
*/
|
||||||
|
export function useTrustProvider() {
|
||||||
|
const { nostr } = useNostr();
|
||||||
|
const { user } = useCurrentUser();
|
||||||
|
const pubkey = user?.pubkey;
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['trust-provider', pubkey],
|
||||||
|
enabled: !!pubkey,
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
queryFn: async (c): Promise<TrustProvider> => {
|
||||||
|
if (!pubkey) return HOUSE_TRUST_PROVIDER;
|
||||||
|
const signal = AbortSignal.any([c.signal, AbortSignal.timeout(8000)]);
|
||||||
|
const events = onlyVerified(await nostr.query(
|
||||||
|
[{ kinds: [NIP85_PROVIDER_LIST_KIND], authors: [pubkey], limit: 1 }],
|
||||||
|
{ signal },
|
||||||
|
));
|
||||||
|
const designation = parseProviderDesignation(events[0]);
|
||||||
|
return resolveProvider(designation, NIP85_USER_ASSERTION_KIND, 'rank', HOUSE_TRUST_PROVIDER);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -2119,7 +2119,7 @@ labUi: {
|
||||||
},
|
},
|
||||||
draft: {
|
draft: {
|
||||||
title: 'You have an unsaved draft',
|
title: 'You have an unsaved draft',
|
||||||
body: 'Pick up where you left off — nothing is published until you press Publish.',
|
body: 'Pick up where you left off — nothing is published until you press Publish. Licensed under the GNU Affero General Public License v3.0: derivatives and hosted versions must stay open under the same terms, because network use counts as distribution.',
|
||||||
resume: 'Resume draft',
|
resume: 'Resume draft',
|
||||||
discard: 'Discard draft',
|
discard: 'Discard draft',
|
||||||
},
|
},
|
||||||
|
|
@ -2795,7 +2795,7 @@ labUi: {
|
||||||
print: 'Print',
|
print: 'Print',
|
||||||
copyId: 'Copy ID',
|
copyId: 'Copy ID',
|
||||||
cardType: 'Card Type',
|
cardType: 'Card Type',
|
||||||
licenseLabel: 'Skater License',
|
licenseLabel: 'License · AGPL-3.0',
|
||||||
licenseSub: 'Your personal rider identity',
|
licenseSub: 'Your personal rider identity',
|
||||||
parkLabel: 'Park Access',
|
parkLabel: 'Park Access',
|
||||||
parkSub: 'Issue passes as a park owner',
|
parkSub: 'Issue passes as a park owner',
|
||||||
|
|
@ -5229,6 +5229,8 @@ settingsUi: {
|
||||||
myData: 'My Data',
|
myData: 'My Data',
|
||||||
myDataSub: 'Export, import, manage',
|
myDataSub: 'Export, import, manage',
|
||||||
advancedSub: 'Relatr, Sasquatch, tools',
|
advancedSub: 'Relatr, Sasquatch, tools',
|
||||||
|
trust: 'Trust',
|
||||||
|
trustSub: 'Sources and display',
|
||||||
notLoaded: 'Not loaded',
|
notLoaded: 'Not loaded',
|
||||||
notLoggedIn: 'Not logged in',
|
notLoggedIn: 'Not logged in',
|
||||||
},
|
},
|
||||||
|
|
@ -7387,6 +7389,12 @@ walletUi: {
|
||||||
sellerHint: 'This seller’s venue issues access passes',
|
sellerHint: 'This seller’s venue issues access passes',
|
||||||
},
|
},
|
||||||
trustUi: {
|
trustUi: {
|
||||||
|
showMarks: 'Show trust marks',
|
||||||
|
showMarksDesc: 'Verified and unknown marks. Reports always show.',
|
||||||
|
sourceLabel: 'Source',
|
||||||
|
sourceHouse: 'Community trust provider',
|
||||||
|
sourceCustom: 'Your provider',
|
||||||
|
loggedOut: 'Log in to choose your own trust provider.',
|
||||||
seoTitle: 'How trust works',
|
seoTitle: 'How trust works',
|
||||||
seoDescription: 'How SkateSpots shows trust: three states, where scores come from, and why absence is not distrust.',
|
seoDescription: 'How SkateSpots shows trust: three states, where scores come from, and why absence is not distrust.',
|
||||||
title: 'How trust works',
|
title: 'How trust works',
|
||||||
|
|
|
||||||
|
|
@ -2119,7 +2119,7 @@ labUi: {
|
||||||
},
|
},
|
||||||
draft: {
|
draft: {
|
||||||
title: 'Tienes un borrador sin guardar',
|
title: 'Tienes un borrador sin guardar',
|
||||||
body: 'Continúa donde lo dejaste — nada se publica hasta que pulses Publicar.',
|
body: 'Continúa donde lo dejaste — nada se publica hasta que pulses Publicar. Con licencia GNU Affero General Public License v3.0: las derivadas y las versiones alojadas deben seguir abiertas bajo los mismos terminos, porque el uso en red cuenta como distribucion.',
|
||||||
resume: 'Reanudar borrador',
|
resume: 'Reanudar borrador',
|
||||||
discard: 'Descartar borrador',
|
discard: 'Descartar borrador',
|
||||||
},
|
},
|
||||||
|
|
@ -2795,7 +2795,7 @@ labUi: {
|
||||||
print: 'Imprimir',
|
print: 'Imprimir',
|
||||||
copyId: 'Copiar ID',
|
copyId: 'Copiar ID',
|
||||||
cardType: 'Tipo de carnet',
|
cardType: 'Tipo de carnet',
|
||||||
licenseLabel: 'Licencia de skater',
|
licenseLabel: 'Licencia · AGPL-3.0',
|
||||||
licenseSub: 'Tu identidad personal de rider',
|
licenseSub: 'Tu identidad personal de rider',
|
||||||
parkLabel: 'Acceso al park',
|
parkLabel: 'Acceso al park',
|
||||||
parkSub: 'Emite pases como propietario de un park',
|
parkSub: 'Emite pases como propietario de un park',
|
||||||
|
|
@ -5229,6 +5229,8 @@ settingsUi: {
|
||||||
myData: 'Mis datos',
|
myData: 'Mis datos',
|
||||||
myDataSub: 'Exportar, importar, gestionar',
|
myDataSub: 'Exportar, importar, gestionar',
|
||||||
advancedSub: 'Relatr, Sasquatch, herramientas',
|
advancedSub: 'Relatr, Sasquatch, herramientas',
|
||||||
|
trust: 'Confianza',
|
||||||
|
trustSub: 'Fuentes y visualizacion',
|
||||||
notLoaded: 'Sin cargar',
|
notLoaded: 'Sin cargar',
|
||||||
notLoggedIn: 'Sin sesión',
|
notLoggedIn: 'Sin sesión',
|
||||||
},
|
},
|
||||||
|
|
@ -7387,6 +7389,12 @@ walletUi: {
|
||||||
sellerHint: 'El local de este vendedor emite pases de acceso',
|
sellerHint: 'El local de este vendedor emite pases de acceso',
|
||||||
},
|
},
|
||||||
trustUi: {
|
trustUi: {
|
||||||
|
showMarks: 'Mostrar marcas de confianza',
|
||||||
|
showMarksDesc: 'Marcas de verificado y desconocido. Los reportes siempre se muestran.',
|
||||||
|
sourceLabel: 'Fuente',
|
||||||
|
sourceHouse: 'Proveedor de confianza de la comunidad',
|
||||||
|
sourceCustom: 'Tu proveedor',
|
||||||
|
loggedOut: 'Inicia sesion para elegir tu propio proveedor de confianza.',
|
||||||
seoTitle: 'Cómo funciona la confianza',
|
seoTitle: 'Cómo funciona la confianza',
|
||||||
seoDescription: 'Cómo muestra SkateSpots la confianza: tres estados, de dónde vienen las puntuaciones y por qué la ausencia no es desconfianza.',
|
seoDescription: 'Cómo muestra SkateSpots la confianza: tres estados, de dónde vienen las puntuaciones y por qué la ausencia no es desconfianza.',
|
||||||
title: 'Cómo funciona la confianza',
|
title: 'Cómo funciona la confianza',
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import { CVMHealthCheck } from '@/components/CVMHealthCheck';
|
||||||
import { SasquatchAiStatusCard } from '@/components/SasquatchAiStatusCard';
|
import { SasquatchAiStatusCard } from '@/components/SasquatchAiStatusCard';
|
||||||
import { GrantProCard } from '@/components/GrantProCard';
|
import { GrantProCard } from '@/components/GrantProCard';
|
||||||
import { ProSupportScreen } from '@/components/ProSupportScreen';
|
import { ProSupportScreen } from '@/components/ProSupportScreen';
|
||||||
|
import { TrustSettingsCard } from '@/components/TrustSettingsCard';
|
||||||
import { AdminReviewCard } from '@/components/AdminReviewCard';
|
import { AdminReviewCard } from '@/components/AdminReviewCard';
|
||||||
import { RegionBackfillCard } from '@/components/RegionBackfillCard';
|
import { RegionBackfillCard } from '@/components/RegionBackfillCard';
|
||||||
import { NotificationSettingsCard } from '@/components/NotificationSettingsCard';
|
import { NotificationSettingsCard } from '@/components/NotificationSettingsCard';
|
||||||
|
|
@ -55,7 +56,8 @@ type Section =
|
||||||
| 'notifications'
|
| 'notifications'
|
||||||
| 'coordinator'
|
| 'coordinator'
|
||||||
| 'advanced'
|
| 'advanced'
|
||||||
| 'pro';
|
| 'pro'
|
||||||
|
| 'trust';
|
||||||
|
|
||||||
// ─── Back header ──────────────────────────────────────────────────────────────
|
// ─── Back header ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -865,6 +867,14 @@ function SettingsMenu({ onNavigate }: { onNavigate: (s: Section) => void }) {
|
||||||
iconBg="bg-green-500/15"
|
iconBg="bg-green-500/15"
|
||||||
onClick={() => onNavigate('mydata')}
|
onClick={() => onNavigate('mydata')}
|
||||||
/>
|
/>
|
||||||
|
<MenuRow
|
||||||
|
icon={Shield}
|
||||||
|
label={t('settingsUi.settingsPage.trust')}
|
||||||
|
sublabel={t('settingsUi.settingsPage.trustSub')}
|
||||||
|
iconColor="text-[#13d2e5]"
|
||||||
|
iconBg="bg-[#13d2e5]/15"
|
||||||
|
onClick={() => onNavigate('trust')}
|
||||||
|
/>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
icon={Wrench}
|
icon={Wrench}
|
||||||
label={t('settingsUi.settingsPage.advanced')}
|
label={t('settingsUi.settingsPage.advanced')}
|
||||||
|
|
@ -901,7 +911,7 @@ export default function SettingsPage() {
|
||||||
// stale bookmark to ?section=language) falls back to the menu rather than a
|
// stale bookmark to ?section=language) falls back to the menu rather than a
|
||||||
// blank screen with no way back.
|
// blank screen with no way back.
|
||||||
const RENDERABLE_SECTIONS: Section[] = [
|
const RENDERABLE_SECTIONS: Section[] = [
|
||||||
'profile', 'security', 'nip05', 'mydata', 'relays', 'appearance', 'wallet', 'notifications', 'coordinator', 'advanced', 'pro',
|
'profile', 'security', 'nip05', 'mydata', 'relays', 'appearance', 'wallet', 'notifications', 'coordinator', 'advanced', 'pro', 'trust',
|
||||||
];
|
];
|
||||||
const requestedSection = searchParams.get('section') as Section;
|
const requestedSection = searchParams.get('section') as Section;
|
||||||
const initialSection =
|
const initialSection =
|
||||||
|
|
@ -956,6 +966,12 @@ export default function SettingsPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{section === 'advanced' && <AdvancedScreen onBack={handleBack} />}
|
{section === 'advanced' && <AdvancedScreen onBack={handleBack} />}
|
||||||
|
{section === 'trust' && (
|
||||||
|
<div>
|
||||||
|
<BackHeader title={t('settingsUi.settingsPage.trust')} onBack={handleBack} />
|
||||||
|
<TrustSettingsCard />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Main menu */}
|
{/* Main menu */}
|
||||||
{!section && <SettingsMenu onNavigate={handleNavigate} />}
|
{!section && <SettingsMenu onNavigate={handleNavigate} />}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue