From 3db834ca0b36169d104b4e6174a922506b82d9f6 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Wed, 3 Jun 2026 11:56:29 +0200 Subject: [PATCH] anon signing --- .../AccountQuickSwitchMenuItems.tsx | 29 +++- src/components/AnonUserAvatar.tsx | 25 ++++ .../ContentPreview/FollowPackPreview.tsx | 4 +- src/components/FollowButton/index.tsx | 4 +- src/components/HelpAndAccountMenu.tsx | 61 +++++--- src/components/MuteButton/index.tsx | 4 +- src/components/NoteOptions/useMenuActions.tsx | 11 +- .../NotificationThreadWatchButtons/index.tsx | 4 +- src/components/ProfileOptions/index.tsx | 47 +++--- src/components/StoredAccountSwitchSelect.tsx | 62 +++++++- src/hooks/useSignGatedControl.ts | 13 +- src/i18n/locales/en.ts | 9 ++ src/lib/account.ts | 18 ++- src/lib/anon-session.ts | 42 ++++++ src/providers/NostrProvider/index.tsx | 141 ++++++++++++++++-- src/providers/nostr-context.tsx | 4 + src/services/client.service.ts | 10 +- src/types/index.d.ts | 10 +- 18 files changed, 417 insertions(+), 81 deletions(-) create mode 100644 src/components/AnonUserAvatar.tsx create mode 100644 src/lib/anon-session.ts diff --git a/src/components/AccountQuickSwitchMenuItems.tsx b/src/components/AccountQuickSwitchMenuItems.tsx index 8243575f..2ae02d81 100644 --- a/src/components/AccountQuickSwitchMenuItems.tsx +++ b/src/components/AccountQuickSwitchMenuItems.tsx @@ -1,3 +1,4 @@ +import { AnonUserAvatar } from '@/components/AnonUserAvatar' import { SimpleUserAvatar } from '@/components/UserAvatar' import { SimpleUsername } from '@/components/Username' import { @@ -7,6 +8,8 @@ import { } from '@/components/ui/dropdown-menu' import { accountPointerKey, + createAnonAccountPointer, + isAnonAccount, isRedundantAccountPick, isSameAccountPubkey, listSwitchableAccounts @@ -19,19 +22,32 @@ import { Check } from 'lucide-react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' +const anonAccount = createAnonAccountPointer() + export function AccountQuickSwitchMenuItems({ onAfterSwitch }: { onAfterSwitch?: () => void }) { const { t } = useTranslation() const { accounts, account, + isAnonSession, switchAccount, retryNip07SignerForPreferredAccount } = useNostr() const rows = listSwitchableAccounts(accounts) - if (rows.length <= 1) return null + if (rows.length === 0 && !isAnonSession) return null const handleSwitch = async (act: TAccountPointer) => { + if (isAnonAccount(act)) { + if (isAnonSession) { + onAfterSwitch?.() + return + } + await switchAccount(act) + onAfterSwitch?.() + return + } + if (isRedundantAccountPick(act, account)) { if (account?.signerType === 'npub' && act.signerType === 'nip-07') { const switched = await switchAccount(act) @@ -62,8 +78,19 @@ export function AccountQuickSwitchMenuItems({ onAfterSwitch }: { onAfterSwitch?: {t('notificationsViewAsAccount')} + void handleSwitch(anonAccount)}> + + + {t('accountSwitch.anon')} + + {t('accountSwitch.anonHintShort')} + + + + {rows.map((act) => { const active = + !isAnonSession && account != null && isSameAccountPubkey(act, account) && (account.signerType === act.signerType || diff --git a/src/components/AnonUserAvatar.tsx b/src/components/AnonUserAvatar.tsx new file mode 100644 index 00000000..272b343d --- /dev/null +++ b/src/components/AnonUserAvatar.tsx @@ -0,0 +1,25 @@ +import { cn } from '@/lib/utils' +import { UserRound } from 'lucide-react' + +export function AnonUserAvatar({ + size = 'small', + className +}: { + size?: 'small' | 'medium' + className?: string +}) { + const dim = size === 'small' ? 'size-8' : 'size-10' + const icon = size === 'small' ? 'size-4' : 'size-5' + return ( +
+ +
+ ) +} diff --git a/src/components/ContentPreview/FollowPackPreview.tsx b/src/components/ContentPreview/FollowPackPreview.tsx index 0163e981..e632b63a 100644 --- a/src/components/ContentPreview/FollowPackPreview.tsx +++ b/src/components/ContentPreview/FollowPackPreview.tsx @@ -39,7 +39,7 @@ export default function FollowPackPreview({ className?: string }) { const { t } = useTranslation() - const { pubkey } = useNostr() + const { pubkey, canManageIdentity } = useNostr() const followList = useFollowListOptional() const followings = followList?.followings ?? [] const { mutePubkeySet } = useMuteList() @@ -169,7 +169,7 @@ export default function FollowPackPreview({ ) : null} - {!pubkey ? ( + {!canManageIdentity ? (

{t('Please log in to follow')}

) : !followList ? null : ( @@ -176,12 +197,12 @@ function TitlebarAccountMenu({ onBrowseCache: () => void }) { const { t } = useTranslation() - const { account, profile } = useNostr() + const { account, profile, isAnonSession } = useNostr() const pubkey = account?.pubkey - const { profile: fetchedProfile } = useFetchProfile(pubkey) + const { profile: fetchedProfile } = useFetchProfile(isAnonSession ? undefined : pubkey) const resolvedProfile = useMemo( - () => profileForActivePubkey(pubkey, profile, fetchedProfile), - [pubkey, profile, fetchedProfile] + () => (isAnonSession ? null : profileForActivePubkey(pubkey, profile, fetchedProfile)), + [pubkey, profile, fetchedProfile, isAnonSession] ) const { current, display } = usePrimaryPage() const [menuOpen, setMenuOpen] = useState(false) @@ -201,7 +222,9 @@ function TitlebarAccountMenu({ title={t('Account menu')} aria-label={t('Account menu')} > - {resolvedProfile ? ( + {isAnonSession ? ( + + ) : resolvedProfile ? ( isVideo(resolvedProfile.avatar ?? '') ? (