Skip to content

Commit

Permalink
Merge pull request #962 from gofractally/bf/chainmail-accounts-update
Browse files Browse the repository at this point in the history
Update Chain mail to use new accounts system
  • Loading branch information
brandonfancher authored Dec 15, 2024
2 parents a5d8c5c + 99952ce commit 346cebc
Show file tree
Hide file tree
Showing 19 changed files with 377 additions and 156 deletions.
206 changes: 167 additions & 39 deletions services/user/Chainmail/ui/src/components/account-switcher.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,180 @@
import { useIncomingMessages, useUser } from "@hooks";
import {
useCreateConnectionToken,
useCurrentAccounts,
useLoggedInUser,
useLogout,
useSelectAccount,
} from "@hooks";
import { cn } from "@lib/utils";
import { Avatar, AvatarFallback } from "@shadcn/avatar";
import { Button } from "@shadcn/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@shadcn/dropdown-menu";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@shadcn/select";
ChevronsUpDown,
LogIn,
LogOut,
PlusCircle,
User,
UserPlus,
} from "lucide-react";

interface AccountSwitcherProps {
isCollapsed: boolean;
}

export function AccountSwitcher({ isCollapsed }: AccountSwitcherProps) {
const { setSelectedMessageId } = useIncomingMessages();
const { availableAccounts, user, setUser } = useUser();
const { mutateAsync: onLogin } = useCreateConnectionToken();

const { data: loggedInUser } = useLoggedInUser();
const isLoggedIn = !!loggedInUser;

const { data: currentAccounts, isPending: isLoadingAccounts } =
useCurrentAccounts();

const isNoOptions = !isLoggedIn && currentAccounts.length == 0;

const { mutateAsync: onLogout } = useLogout();
const { mutateAsync: selectAccount } = useSelectAccount();

const otherConnectedAccounts = currentAccounts.filter(
(account) => account !== loggedInUser,
);

return (
<Select
defaultValue={user}
onValueChange={(value) => {
setSelectedMessageId("");
setUser(value);
}}
>
<SelectTrigger
className={cn(
"flex items-center gap-2 [&>span]:line-clamp-1 [&>span]:flex [&>span]:w-full [&>span]:items-center [&>span]:gap-1 [&>span]:truncate [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0",
isCollapsed &&
"flex h-9 w-9 shrink-0 items-center justify-center p-0 [&>span]:w-auto [&>svg]:hidden",
)}
aria-label="Select account"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="lg"
variant="ghost"
className={cn("w-full", !isCollapsed && "pl-1 pr-2")}
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarFallback className="rounded-lg">
{loggedInUser?.[0] ?? <LogIn className="h-4 w-4" />}
</AvatarFallback>
</Avatar>
{isCollapsed ? null : (
<>
<div className="ml-2 grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{loggedInUser ?? "Get started"}
</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
// side={isMobile ? "bottom" : "right"}
side="right"
align="end"
sideOffset={4}
>
<SelectValue placeholder="Select an account">
<span className={cn("ml-2", isCollapsed && "hidden")}>
{user}
</span>
</SelectValue>
</SelectTrigger>
<SelectContent>
{availableAccounts.map((account) => (
<SelectItem key={account} value={account}>
<div className="flex items-center gap-3 [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0 [&_svg]:text-foreground">
{account}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
{loggedInUser ? (
<>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarFallback className="rounded-lg">
{loggedInUser[0]}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{loggedInUser}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
</>
) : null}
{isNoOptions && (
<DropdownMenuItem
onClick={() => {
onLogin();
}}
>
<LogIn className="mr-2 h-4 w-4" />
<span>
{isLoadingAccounts ? "Loading..." : "Log in"}
</span>
</DropdownMenuItem>
)}
{!isNoOptions && !otherConnectedAccounts.length ? (
<DropdownMenuItem
onClick={() => {
onLogin();
}}
>
<UserPlus className="mr-2 h-4 w-4" />
<span>Switch account</span>
</DropdownMenuItem>
) : null}
{!isNoOptions && otherConnectedAccounts.length ? (
<DropdownMenuGroup>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<UserPlus className="mr-2 h-4 w-4" />
<span>
{loggedInUser
? "Switch account"
: "Select an account"}
</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
{otherConnectedAccounts.map((account) => (
<DropdownMenuItem
key={account}
onClick={() => {
selectAccount(account);
}}
>
<User className="mr-2 h-4 w-4" />
<span>{account}</span>
</DropdownMenuItem>
))}
{otherConnectedAccounts.length ? (
<DropdownMenuSeparator />
) : null}
<DropdownMenuItem
onClick={() => {
onLogin();
}}
>
<PlusCircle className="mr-2 h-4 w-4" />
<span>More...</span>
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
</DropdownMenuGroup>
) : null}
<DropdownMenuItem
disabled={!isLoggedIn}
onClick={() => {
onLogout();
}}
>
<LogOut className="mr-2 h-4 w-4" />
<span>Log out</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
9 changes: 4 additions & 5 deletions services/user/Chainmail/ui/src/components/compose-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { toast } from "sonner";

import { type PluginId } from "@psibase/common-lib";

import { getSupervisor } from "@lib/supervisor";

import { Button } from "@shadcn/button";
import {
Dialog,
Expand All @@ -27,7 +25,7 @@ import { Separator } from "@shadcn/separator";

import { MarkdownEditor } from "@components";
import { ControlBar } from "@components/editor";
import { type Message, useDraftMessages, useUser } from "@hooks";
import { type Message, useDraftMessages, useLoggedInUser } from "@hooks";

import {
Form,
Expand All @@ -38,6 +36,8 @@ import {
} from "@shadcn/form";
import { Input } from "@shadcn/input";

import { supervisor } from "src/main";

interface SupervisorError {
code: number;
producer: PluginId;
Expand All @@ -58,7 +58,7 @@ export const ComposeDialog = ({
}) => {
const [open, setOpen] = useState(false);
const isSent = useRef(false);
const { user } = useUser();
const { data: user } = useLoggedInUser();
const { drafts, setDrafts, getDrafts, deleteDraftById } =
useDraftMessages();

Expand Down Expand Up @@ -122,7 +122,6 @@ export const ComposeDialog = ({
}

try {
const supervisor = await getSupervisor();
// TODO: Improve error detection. This promise resolves with success before the transaction is pushed.
await supervisor.functionCall({
service: "chainmail",
Expand Down
5 changes: 2 additions & 3 deletions services/user/Chainmail/ui/src/components/mail-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import {
ReplyDialogTriggerIconWithTooltip,
} from "@components";
import { Message, useDraftMessages, useIncomingMessages } from "@hooks";
import { getSupervisor } from "@lib/supervisor";
import { wait } from "@lib/utils";

import { supervisor } from "src/main";

export function MailDisplay({
message,
mailbox,
Expand Down Expand Up @@ -97,7 +98,6 @@ const ActionBar = ({

const onArchive = async (itemId: string) => {
let id = parseInt(itemId);
const supervisor = await getSupervisor();
// TODO: Improve error detection. This promise resolves with success before the transaction is pushed.
await supervisor.functionCall({
service: "chainmail",
Expand All @@ -119,7 +119,6 @@ const ActionBar = ({

const onSave = async (itemId: string) => {
let id = parseInt(itemId);
const supervisor = await getSupervisor();
await supervisor.functionCall({
service: "chainmail",
intf: "api",
Expand Down
2 changes: 1 addition & 1 deletion services/user/Chainmail/ui/src/components/mode-toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function ModeToggle() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Button variant="outline" size="icon" className="my-2">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
Expand Down
12 changes: 9 additions & 3 deletions services/user/Chainmail/ui/src/components/nav-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import { cn } from "@lib/utils";

import { AccountSwitcher } from "./account-switcher";
import { Nav } from "./nav";
import { useNavigate, useLocation } from "react-router-dom";
import { useIncomingMessages } from "@hooks";
import { useLocation } from "react-router-dom";
import { useIncomingMessages, useLoggedInUser } from "@hooks";

interface Props {
isCollapsed?: boolean;
}

export const NavMenu = ({ isCollapsed = false }: Props) => {
const navigate = useNavigate();
const { data: loggedInUser } = useLoggedInUser();
const isLoggedIn = !!loggedInUser;

const { pathname } = useLocation();
const at = pathname;
Expand Down Expand Up @@ -41,30 +42,35 @@ export const NavMenu = ({ isCollapsed = false }: Props) => {
icon: Inbox,
variant: at === "/" ? "default" : "ghost",
href: "/",
disabled: !isLoggedIn,
},
{
title: "Drafts",
icon: PencilLine,
variant: at === "/drafts" ? "default" : "ghost",
href: "/drafts",
disabled: !isLoggedIn,
},
{
title: "Saved",
icon: Pin,
variant: at === "/saved" ? "default" : "ghost",
href: "/saved",
disabled: !isLoggedIn,
},
{
title: "Sent",
icon: Send,
variant: at === "/sent" ? "default" : "ghost",
href: "/sent",
disabled: !isLoggedIn,
},
{
title: "Archived",
icon: Archive,
variant: at === "/archived" ? "default" : "ghost",
href: "/archived",
disabled: !isLoggedIn,
},
]}
/>
Expand Down
16 changes: 8 additions & 8 deletions services/user/Chainmail/ui/src/components/nav.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { useNavigate } from "react-router-dom";
import { LucideIcon } from "lucide-react";

import { cn } from "../lib/utils";
import { buttonVariants } from "../shad/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "../shad/components/ui/tooltip";
import { useNavigate } from "react-router-dom";
import { buttonVariants } from "@shadcn/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@shadcn/tooltip";
import { cn } from "@lib/utils";

interface NavProps {
isCollapsed: boolean;
Expand All @@ -17,6 +13,7 @@ interface NavProps {
icon: LucideIcon;
variant: "default" | "ghost";
href: string;
disabled?: boolean;
}[];
}

Expand All @@ -42,6 +39,8 @@ export function Nav({ links, isCollapsed }: NavProps) {
"h-9 w-9",
link.variant === "default" &&
"dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white",
link.disabled &&
"pointer-events-none opacity-50",
)}
>
<link.icon className="h-4 w-4" />
Expand Down Expand Up @@ -77,6 +76,7 @@ export function Nav({ links, isCollapsed }: NavProps) {
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white",
"justify-start",
)}
disabled={link.disabled}
>
<link.icon className="mr-2 h-4 w-4" />
{link.title}
Expand Down
Loading

0 comments on commit 346cebc

Please sign in to comment.