Fix mobile bugs: DM/crew-chat crash, notification popover clipping, multi-account switch

#2 (crash): useDirectMessages called getEventHash() without importing it —
`getEventHash is not defined` broke crew chat and any NIP-17 DM send. Added
the missing import from nostr-tools.

#3 (bell not loading on phone): the notifications popover had a fixed-height
scroll area and no collision handling, so on small screens it rendered clipped
against the top edge with the tab content cut off. The popover now constrains
to the Radix available-height, uses a flex column, and the tab list/scroll area
flex correctly — all tabs (incl. Crews/News) are reachable on mobile.

#4 (multi-account on phone): the mobile header user menu had only Set Status +
Log Out. Added an Accounts section that lists other logged-in npubs to switch
between, plus "Add another account" which opens the login dialog. Desktop
already had this via AccountSwitcher; mobile now reaches parity.

Note: #1/#5/#6 (stuck on publish/vouch/delete) were reported against the shipped
1.2.2 APK, which also contained the #2 crash. Rebuild 1.2.3 and re-test — the
sign/publish paths in current source are correct (Amber round-trip has a 90s
timeout + surfaced errors).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-09-03 20:09:01 +07:00
parent 3582262a44
commit 5017c1b7ff
3 changed files with 56 additions and 6 deletions

View file

@ -15,6 +15,7 @@ import {
DropdownMenuSeparator,
} from '@/components/ui/dropdown-menu';
import { LoginArea } from '@/components/auth/LoginArea';
import { LoginDialog } from '@/components/auth/LoginDialog';
import { SasquatchGuide } from '@/components/SasquatchGuide';
import { useSasquatchGuide } from '@/hooks/useSasquatchGuide';
import { BackupReminder } from '@/components/BackupReminder';
@ -124,16 +125,21 @@ function MobileHeaderUserMenu({
pubkey,
onLogout,
onSetStatus,
onAddAccount,
}: {
pubkey: string;
onLogout: () => void;
onSetStatus: () => void;
onAddAccount: () => void;
}) {
const author = useAuthor(pubkey);
const metadata = author.data?.metadata;
const displayName = metadata?.name ?? genUserName(pubkey);
const avatar = metadata?.picture;
// Multi-account: list other logins so the user can switch on mobile.
const { otherUsers, setLogin } = useLoggedInAccounts();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -225,6 +231,36 @@ function MobileHeaderUserMenu({
<span className="text-orange-400">Wallet</span>
</Link>
</DropdownMenuItem>
{/* ── Accounts: switch between logged-in npubs + add another ── */}
<DropdownMenuSeparator />
<div className="px-2 py-1 text-[10px] font-black uppercase tracking-widest text-gray-500">
Accounts
</div>
{otherUsers.map((account) => {
const name = account.metadata?.name ?? genUserName(account.pubkey);
return (
<DropdownMenuItem
key={account.id}
onClick={() => setLogin(account.id)}
className="flex items-center gap-2 cursor-pointer"
>
<Avatar className="h-6 w-6">
<AvatarImage src={account.metadata?.picture} alt={name} />
<AvatarFallback className="text-[9px]">{name.slice(0, 2).toUpperCase()}</AvatarFallback>
</Avatar>
<span className="flex-1 truncate">{name}</span>
</DropdownMenuItem>
);
})}
<DropdownMenuItem
onClick={onAddAccount}
className="flex items-center gap-2 cursor-pointer"
>
<Plus className="h-4 w-4 text-teal-400" />
<span className="text-teal-400">Add another account</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={onLogout}
@ -404,6 +440,7 @@ export function Layout({ children, noPadding }: LayoutProps) {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [showStatusDialog, setShowStatusDialog] = useState(false);
const [showCustomizeNav, setShowCustomizeNav] = useState(false);
const [showAddAccount, setShowAddAccount] = useState(false);
const { currentMessage, dismissCurrentMessage } = useSasquatchGuide();
const { user } = useCurrentUser();
const { totalBalance: cashuBalance, isInitialized: hasCashu } = useCashu();
@ -658,6 +695,7 @@ export function Layout({ children, noPadding }: LayoutProps) {
pubkey={user.pubkey}
onLogout={() => removeLogin(currentAccount?.id || '')}
onSetStatus={() => setShowStatusDialog(true)}
onAddAccount={() => setShowAddAccount(true)}
/>
) : (
<LoginArea compact className="flex flex-row gap-1" />
@ -986,6 +1024,13 @@ export function Layout({ children, noPadding }: LayoutProps) {
onOpenChange={setShowStatusDialog}
/>
{/* Add-another-account (multi-account switch on mobile) */}
<LoginDialog
isOpen={showAddAccount}
onClose={() => setShowAddAccount(false)}
onLogin={() => setShowAddAccount(false)}
/>
{/* Newsletter: floating pill + Monday admin reminder */}
<NewsletterOverlay />
</div>

View file

@ -264,8 +264,13 @@ export function NotificationBell({ className }: { className?: string }) {
)}
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-[380px] max-w-[calc(100vw-1.5rem)] p-0" sideOffset={8}>
<div className="flex items-center justify-between border-b border-white/10 px-4 py-3">
<PopoverContent
align="end"
collisionPadding={8}
className="flex max-h-[min(70vh,var(--radix-popover-content-available-height))] w-[380px] max-w-[calc(100vw-1.5rem)] flex-col overflow-hidden p-0"
sideOffset={8}
>
<div className="flex shrink-0 items-center justify-between border-b border-white/10 px-4 py-3">
<h3 className="flex items-center gap-2 text-sm font-black uppercase tracking-wide text-white">
<Bell className="h-4 w-4 text-teal-400" /> Notifications
</h3>
@ -278,8 +283,8 @@ export function NotificationBell({ className }: { className?: string }) {
</button>
</div>
<Tabs defaultValue="all" className="w-full">
<TabsList className="grid w-full grid-cols-4 rounded-none border-b border-white/10 bg-transparent p-0">
<Tabs defaultValue="all" className="flex min-h-0 w-full flex-1 flex-col">
<TabsList className="grid w-full shrink-0 grid-cols-4 rounded-none border-b border-white/10 bg-transparent p-0">
<TabsTrigger
value="all"
className="rounded-none border-b-2 border-transparent py-2.5 text-xs font-bold data-[state=active]:border-teal-400 data-[state=active]:bg-transparent data-[state=active]:text-teal-400"
@ -309,7 +314,7 @@ export function NotificationBell({ className }: { className?: string }) {
</TabsTrigger>
</TabsList>
<ScrollArea className="max-h-[65vh]">
<ScrollArea className="min-h-0 flex-1">
{/* ─── ALL ─────────────────────────────────────────────────── */}
<TabsContent value="all" className="m-0 p-3">
{isLoading && badgeTotal === 0 && (

View file

@ -1,7 +1,7 @@
import { useCallback, useMemo } from 'react';
import { useNostr } from '@nostrify/react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { nip44, nip19, generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools';
import { nip44, nip19, generateSecretKey, getPublicKey, finalizeEvent, getEventHash } from 'nostr-tools';
import type { NostrEvent } from '@nostrify/nostrify';
import { useNostrLogin } from '@nostrify/react/login';