mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-12 08:27:27 +02:00
feat: enhance user menu for multi-account support
- Added support for displaying all accounts in user menu - Implemented account switching via dropdown menu - Show active account with checkmark indicator - List other accounts in "Switch Account" section - Added "Add account" button that opens command launcher - Users can now switch between multiple accounts seamlessly - Each account displays avatar, name, and NIP-05 identifier UI Improvements: - Active account shown at top with green checkmark - Other accounts listed below with avatars - "Add account" button for adding new accounts - Maintains existing relays and logout functionality Technical Changes: - Use useAppShell() hook for opening command launcher - Use IAccount type instead of BaseAccount for type safety - Filter accounts list to separate active from other accounts - Observable subscriptions for reactive account updates
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { User } from "lucide-react";
|
||||
import { User, Check, UserPlus } from "lucide-react";
|
||||
import accounts from "@/services/accounts";
|
||||
import { ExtensionSigner } from "applesauce-signers";
|
||||
import { ExtensionAccount } from "applesauce-accounts/accounts";
|
||||
@@ -6,6 +6,7 @@ import { useProfile } from "@/hooks/useProfile";
|
||||
import { useObservableMemo } from "applesauce-react/hooks";
|
||||
import { getDisplayName } from "@/lib/nostr-utils";
|
||||
import { useGrimoire } from "@/core/state";
|
||||
import { useAppShell } from "@/components/layouts/AppShellContext";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -21,6 +22,7 @@ import Nip05 from "./nip05";
|
||||
import { RelayLink } from "./RelayLink";
|
||||
import SettingsDialog from "@/components/SettingsDialog";
|
||||
import { useState } from "react";
|
||||
import type { IAccount } from "applesauce-accounts";
|
||||
|
||||
function UserAvatar({ pubkey }: { pubkey: string }) {
|
||||
const profile = useProfile(pubkey);
|
||||
@@ -53,10 +55,15 @@ function UserLabel({ pubkey }: { pubkey: string }) {
|
||||
|
||||
export default function UserMenu() {
|
||||
const account = useObservableMemo(() => accounts.active$, []);
|
||||
const allAccounts = useObservableMemo(() => accounts.accounts$, []);
|
||||
const { state, addWindow } = useGrimoire();
|
||||
const { openCommandLauncher } = useAppShell();
|
||||
const relays = state.activeAccount?.relays;
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
|
||||
// Get other accounts (not the active one)
|
||||
const otherAccounts = allAccounts.filter((acc) => acc.id !== account?.id);
|
||||
|
||||
function openProfile() {
|
||||
if (!account?.pubkey) return;
|
||||
addWindow(
|
||||
@@ -78,6 +85,15 @@ export default function UserMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
function switchAccount(targetAccount: IAccount<any, any, any>) {
|
||||
accounts.setActive(targetAccount.id);
|
||||
}
|
||||
|
||||
function addAccount() {
|
||||
// Open the command launcher (user will type "login" command)
|
||||
openCommandLauncher();
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
if (!account) return;
|
||||
accounts.removeAccount(account);
|
||||
@@ -103,15 +119,54 @@ export default function UserMenu() {
|
||||
<DropdownMenuContent className="w-80" align="start">
|
||||
{account ? (
|
||||
<>
|
||||
{/* Active Account */}
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel
|
||||
className="cursor-crosshair hover:bg-muted/50"
|
||||
onClick={openProfile}
|
||||
>
|
||||
<UserLabel pubkey={account.pubkey} />
|
||||
<div className="flex items-center gap-2">
|
||||
<Check className="size-4 text-primary" />
|
||||
<UserLabel pubkey={account.pubkey} />
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
</DropdownMenuGroup>
|
||||
|
||||
{/* Other Accounts */}
|
||||
{otherAccounts.length > 0 && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel className="text-xs text-muted-foreground font-normal">
|
||||
Switch Account
|
||||
</DropdownMenuLabel>
|
||||
{otherAccounts.map((acc) => (
|
||||
<DropdownMenuItem
|
||||
key={acc.id}
|
||||
onClick={() => switchAccount(acc)}
|
||||
className="cursor-crosshair"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<UserAvatar pubkey={acc.pubkey} />
|
||||
<UserLabel pubkey={acc.pubkey} />
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Add Account */}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={addAccount}
|
||||
className="cursor-crosshair"
|
||||
>
|
||||
<UserPlus className="mr-2 size-4" />
|
||||
Add account
|
||||
</DropdownMenuItem>
|
||||
|
||||
{/* Relays */}
|
||||
{relays && relays.length > 0 && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
@@ -134,15 +189,8 @@ export default function UserMenu() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Logout */}
|
||||
<DropdownMenuSeparator />
|
||||
{/* <DropdownMenuItem
|
||||
onClick={() => setShowSettings(true)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Settings className="mr-2 size-4" />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator /> */}
|
||||
<DropdownMenuItem onClick={logout} className="cursor-crosshair">
|
||||
Log out
|
||||
</DropdownMenuItem>
|
||||
|
||||
Reference in New Issue
Block a user