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:
parent
3582262a44
commit
5017c1b7ff
3 changed files with 56 additions and 6 deletions
|
|
@ -15,6 +15,7 @@ import {
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { LoginArea } from '@/components/auth/LoginArea';
|
import { LoginArea } from '@/components/auth/LoginArea';
|
||||||
|
import { LoginDialog } from '@/components/auth/LoginDialog';
|
||||||
import { SasquatchGuide } from '@/components/SasquatchGuide';
|
import { SasquatchGuide } from '@/components/SasquatchGuide';
|
||||||
import { useSasquatchGuide } from '@/hooks/useSasquatchGuide';
|
import { useSasquatchGuide } from '@/hooks/useSasquatchGuide';
|
||||||
import { BackupReminder } from '@/components/BackupReminder';
|
import { BackupReminder } from '@/components/BackupReminder';
|
||||||
|
|
@ -124,16 +125,21 @@ function MobileHeaderUserMenu({
|
||||||
pubkey,
|
pubkey,
|
||||||
onLogout,
|
onLogout,
|
||||||
onSetStatus,
|
onSetStatus,
|
||||||
|
onAddAccount,
|
||||||
}: {
|
}: {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
onSetStatus: () => void;
|
onSetStatus: () => void;
|
||||||
|
onAddAccount: () => void;
|
||||||
}) {
|
}) {
|
||||||
const author = useAuthor(pubkey);
|
const author = useAuthor(pubkey);
|
||||||
const metadata = author.data?.metadata;
|
const metadata = author.data?.metadata;
|
||||||
const displayName = metadata?.name ?? genUserName(pubkey);
|
const displayName = metadata?.name ?? genUserName(pubkey);
|
||||||
const avatar = metadata?.picture;
|
const avatar = metadata?.picture;
|
||||||
|
|
||||||
|
// Multi-account: list other logins so the user can switch on mobile.
|
||||||
|
const { otherUsers, setLogin } = useLoggedInAccounts();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -225,6 +231,36 @@ function MobileHeaderUserMenu({
|
||||||
<span className="text-orange-400">Wallet</span>
|
<span className="text-orange-400">Wallet</span>
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</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 />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={onLogout}
|
onClick={onLogout}
|
||||||
|
|
@ -404,6 +440,7 @@ export function Layout({ children, noPadding }: LayoutProps) {
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
const [showStatusDialog, setShowStatusDialog] = useState(false);
|
const [showStatusDialog, setShowStatusDialog] = useState(false);
|
||||||
const [showCustomizeNav, setShowCustomizeNav] = useState(false);
|
const [showCustomizeNav, setShowCustomizeNav] = useState(false);
|
||||||
|
const [showAddAccount, setShowAddAccount] = useState(false);
|
||||||
const { currentMessage, dismissCurrentMessage } = useSasquatchGuide();
|
const { currentMessage, dismissCurrentMessage } = useSasquatchGuide();
|
||||||
const { user } = useCurrentUser();
|
const { user } = useCurrentUser();
|
||||||
const { totalBalance: cashuBalance, isInitialized: hasCashu } = useCashu();
|
const { totalBalance: cashuBalance, isInitialized: hasCashu } = useCashu();
|
||||||
|
|
@ -658,6 +695,7 @@ export function Layout({ children, noPadding }: LayoutProps) {
|
||||||
pubkey={user.pubkey}
|
pubkey={user.pubkey}
|
||||||
onLogout={() => removeLogin(currentAccount?.id || '')}
|
onLogout={() => removeLogin(currentAccount?.id || '')}
|
||||||
onSetStatus={() => setShowStatusDialog(true)}
|
onSetStatus={() => setShowStatusDialog(true)}
|
||||||
|
onAddAccount={() => setShowAddAccount(true)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LoginArea compact className="flex flex-row gap-1" />
|
<LoginArea compact className="flex flex-row gap-1" />
|
||||||
|
|
@ -986,6 +1024,13 @@ export function Layout({ children, noPadding }: LayoutProps) {
|
||||||
onOpenChange={setShowStatusDialog}
|
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 */}
|
{/* Newsletter: floating pill + Monday admin reminder */}
|
||||||
<NewsletterOverlay />
|
<NewsletterOverlay />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -264,8 +264,13 @@ export function NotificationBell({ className }: { className?: string }) {
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent align="end" className="w-[380px] max-w-[calc(100vw-1.5rem)] p-0" sideOffset={8}>
|
<PopoverContent
|
||||||
<div className="flex items-center justify-between border-b border-white/10 px-4 py-3">
|
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">
|
<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
|
<Bell className="h-4 w-4 text-teal-400" /> Notifications
|
||||||
</h3>
|
</h3>
|
||||||
|
|
@ -278,8 +283,8 @@ export function NotificationBell({ className }: { className?: string }) {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Tabs defaultValue="all" className="w-full">
|
<Tabs defaultValue="all" className="flex min-h-0 w-full flex-1 flex-col">
|
||||||
<TabsList className="grid w-full grid-cols-4 rounded-none border-b border-white/10 bg-transparent p-0">
|
<TabsList className="grid w-full shrink-0 grid-cols-4 rounded-none border-b border-white/10 bg-transparent p-0">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="all"
|
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"
|
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>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<ScrollArea className="max-h-[65vh]">
|
<ScrollArea className="min-h-0 flex-1">
|
||||||
{/* ─── ALL ─────────────────────────────────────────────────── */}
|
{/* ─── ALL ─────────────────────────────────────────────────── */}
|
||||||
<TabsContent value="all" className="m-0 p-3">
|
<TabsContent value="all" className="m-0 p-3">
|
||||||
{isLoading && badgeTotal === 0 && (
|
{isLoading && badgeTotal === 0 && (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useNostr } from '@nostrify/react';
|
import { useNostr } from '@nostrify/react';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
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 type { NostrEvent } from '@nostrify/nostrify';
|
||||||
import { useNostrLogin } from '@nostrify/react/login';
|
import { useNostrLogin } from '@nostrify/react/login';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue