diff --git a/messages/en.json b/messages/en.json index 96925c4..bb50f96 100644 --- a/messages/en.json +++ b/messages/en.json @@ -105,11 +105,23 @@ }, "Index": { "accounts": "Accounts", + "beta": "Beta - Limit funds and use at your own risk.", "contacts": "Contacts", + "create-wallet": "Create Wallet", + "full-refresh": "Full Refresh", + "help": "Help", + "home": "Home", + "loading": "Loading", + "logout": "Logout", + "new-wallet": "New Wallet", + "refresh": "Refresh", + "restore-wallet": "Restore Wallet", + "select-wallet": "Select Wallet", "settings": "Settings", "swaps": "Swaps", "transactions": "Transactions", - "wallet": "Wallet" + "wallet": "Wallet", + "wallets": "Wallets" }, "Public": { "404": { diff --git a/messages/es.json b/messages/es.json index dcca1a2..b1267f8 100644 --- a/messages/es.json +++ b/messages/es.json @@ -105,11 +105,23 @@ }, "Index": { "accounts": "Cuentas", + "beta": "Beta - Limita la cantidad de fondos y usa bajo tu propio riesgo.", "contacts": "Contactos", + "create-wallet": "Crear Billetera", + "full-refresh": "Refrescar Balance", + "help": "Ayuda", + "home": "Casa", + "loading": "Cargando", + "logout": "Salir", + "new-wallet": "Nueva Billetera", + "refresh": "Refrescar", + "restore-wallet": "Restaurar Billetera", + "select-wallet": "Seleccionar Billetera", "settings": "ConfiguraciĆ³n", "swaps": "Intercambios", "transactions": "Transacciones", - "wallet": "Billetera" + "wallet": "Billetera", + "wallets": "Billeteras" }, "Public": { "404": { diff --git a/src/components/button/LogoutButton.tsx b/src/components/button/LogoutButton.tsx index cb213cb..e3411e9 100644 --- a/src/components/button/LogoutButton.tsx +++ b/src/components/button/LogoutButton.tsx @@ -1,6 +1,7 @@ 'use client'; import { LogOut } from 'lucide-react'; +import { useTranslations } from 'next-intl'; import { useLogoutMutation } from '@/graphql/mutations/__generated__/logout.generated'; import { useKeyStore } from '@/stores/keys'; @@ -12,6 +13,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'; import { useToast } from '../ui/use-toast'; export const LogoutButtonWithTooltip = () => { + const t = useTranslations('Index'); + const { toast } = useToast(); const clearKeys = useKeyStore(s => s.clear); @@ -35,21 +38,22 @@ export const LogoutButtonWithTooltip = () => { - Logout + {t('logout')} ); }; export const LogoutButton = () => { + const t = useTranslations('Index'); + const { toast } = useToast(); const clearKeys = useKeyStore(s => s.clear); @@ -68,14 +72,13 @@ export const LogoutButton = () => { }); return ( - + +

{t('logout')}

+ ); }; diff --git a/src/components/button/RefreshWallet.tsx b/src/components/button/RefreshWallet.tsx index db20018..2547e59 100644 --- a/src/components/button/RefreshWallet.tsx +++ b/src/components/button/RefreshWallet.tsx @@ -1,11 +1,12 @@ 'use client'; -import { Loader2, RefreshCcw } from 'lucide-react'; +import { RefreshCw, RotateCw } from 'lucide-react'; import { FC } from 'react'; import { useRefreshWalletMutation } from '@/graphql/mutations/__generated__/refreshWallet.generated'; +import { cn } from '@/utils/cn'; -import { CommandItem } from '../ui/command'; +import { DropdownMenuItem } from '../ui/dropdown-menu'; import { useToast } from '../ui/use-toast'; export const RefreshWallet: FC<{ @@ -26,13 +27,21 @@ export const RefreshWallet: FC<{ }); return ( - refresh()} className="cursor-pointer"> - {title} - {loading ? ( - + { + e.preventDefault(); + refresh(); + }} + > + {fullScan ? ( + ) : ( - + )} - + {title} + ); }; diff --git a/src/components/button/VaultButtonV2.tsx b/src/components/button/VaultButtonV2.tsx index 07ad556..fa4a4e1 100644 --- a/src/components/button/VaultButtonV2.tsx +++ b/src/components/button/VaultButtonV2.tsx @@ -236,7 +236,8 @@ const VaultPasswordButton: FC<{ className?: string; variant?: Variants; size?: 'md'; -}> = ({ lockedTitle, className, variant, size }) => { + unstyled?: boolean; +}> = ({ lockedTitle, className, variant, size, unstyled }) => { const t = useTranslations('App.Wallet.Vault'); const keys = useKeyStore(s => s.keys); @@ -257,17 +258,21 @@ const VaultPasswordButton: FC<{ type="button" variant={variant} size={size} - className={cn('flex items-center justify-center', className)} + className={cn( + 'flex items-center justify-center space-x-2', + className + )} + unstyled={unstyled} > {keys ? ( <> - - {t('unlocked')} + +

{t('unlocked')}

) : ( <> - - {lockedTitle} + +

{lockedTitle}

)} @@ -309,6 +314,7 @@ const PasskeyVaultButton: FC<{ size?: 'md'; protectedSymmetricKey: string; passkeyId: string; + unstyled?: boolean; }> = ({ lockedTitle, className, @@ -316,6 +322,7 @@ const PasskeyVaultButton: FC<{ size, protectedSymmetricKey, passkeyId, + unstyled, }) => { const t = useTranslations('App.Wallet.Vault'); @@ -427,14 +434,15 @@ const PasskeyVaultButton: FC<{ type="button" variant={variant} size={size} - className={cn('flex items-center justify-center', className)} + className={cn('flex items-center justify-center space-x-2', className)} disabled={loading || addLoading} onClick={() => { setup({ variables: { id: passkeyId } }); }} + unstyled={unstyled} > - - {lockedTitle} + +

{lockedTitle}

); } @@ -444,13 +452,14 @@ const PasskeyVaultButton: FC<{ type="button" variant={variant} size={size} - className={cn('flex items-center justify-center', className)} + className={cn('flex items-center justify-center space-x-2', className)} onClick={() => { clearKeys(); }} + unstyled={unstyled} > - - {t('unlocked')} + +

{t('unlocked')}

); }; @@ -460,7 +469,8 @@ export const VaultButton: FC<{ className?: string; variant?: Variants; size?: 'md'; -}> = ({ lockedTitle, className, variant, size }) => { + unstyled?: boolean; +}> = ({ lockedTitle, className, variant, size, unstyled }) => { const t = useTranslations(); const lockedTitleFinal = lockedTitle || t('App.Wallet.Vault.locked'); @@ -473,11 +483,12 @@ export const VaultButton: FC<{ type="button" variant={variant} size={size} - className={cn('flex items-center justify-center', className)} + className={cn('flex items-center justify-center space-x-2', className)} disabled + unstyled={unstyled} > - - {lockedTitleFinal} + +

{lockedTitleFinal}

); } @@ -488,11 +499,12 @@ export const VaultButton: FC<{ type="button" variant={variant} size={size} - className={cn('flex items-center justify-center', className)} + className={cn('flex items-center justify-center space-x-2', className)} disabled + unstyled={unstyled} > - - {t('Common.error')} + +

{t('Common.error')}

); } @@ -506,6 +518,7 @@ export const VaultButton: FC<{ size={size} protectedSymmetricKey={data.user.protected_symmetric_key} passkeyId={data.user.using_passkey_id} + unstyled={unstyled} /> ); } @@ -516,6 +529,7 @@ export const VaultButton: FC<{ className={className} variant={variant} size={size} + unstyled={unstyled} /> ); }; diff --git a/src/components/button/WalletButton.tsx b/src/components/button/WalletButton.tsx index 15c1226..989f4d3 100644 --- a/src/components/button/WalletButton.tsx +++ b/src/components/button/WalletButton.tsx @@ -1,35 +1,45 @@ 'use client'; -import { CaretSortIcon, CheckIcon } from '@radix-ui/react-icons'; -import { CircleEqual, PlusCircle, Settings } from 'lucide-react'; +import { + Check, + ChevronsUpDown, + Lock, + Plus, + PlusCircle, + Settings2, + Unlock, +} from 'lucide-react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; -import { useEffect, useMemo, useState } from 'react'; +import { useTranslations } from 'next-intl'; +import { FC, useEffect, useMemo } from 'react'; import { useLocalStorage } from 'usehooks-ts'; import { Button } from '@/components/ui/button'; -import { - Command, - CommandGroup, - CommandItem, - CommandList, -} from '@/components/ui/command'; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from '@/components/ui/popover'; import { useUserQuery } from '@/graphql/queries/__generated__/user.generated'; import { useGetAllWalletsQuery } from '@/graphql/queries/__generated__/wallet.generated'; import { useChat, useContactStore } from '@/stores/contacts'; +import { useKeyStore } from '@/stores/keys'; import { cn } from '@/utils/cn'; import { LOCALSTORAGE_KEYS } from '@/utils/constants'; import { ROUTES } from '@/utils/routes'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '../ui/dropdown-menu'; import { RefreshWallet } from './RefreshWallet'; +import { VaultButton } from './VaultButtonV2'; + +export const WalletButton: FC<{ className?: string; cbk?: () => void }> = ({ + className, + cbk, +}) => { + const t = useTranslations('Index'); -export function WalletButton() { - const [open, setOpen] = useState(false); + const keys = useKeyStore(s => s.keys); const setCurrentContact = useContactStore(s => s.setCurrentContact); const setCurrentPaymentOption = useChat(s => s.setCurrentPaymentOption); @@ -66,124 +76,119 @@ export function WalletButton() { }, [data]); const buttonText = useMemo(() => { - if (loading) return 'Loading...'; - if (!wallets.length) return 'Create a wallet'; + if (loading) return t('loading') + '...'; + if (!wallets.length) return t('create-wallet'); if (value) { - return wallets.find(w => w.value === value)?.label || 'Select wallet...'; + return wallets.find(w => w.value === value)?.label || t('select-wallet'); } - }, [loading, wallets, value]); + }, [loading, wallets, value, t]); if (error) return null; if (!buttonText) return null; return ( - - + + - - - - - {wallets.length ? ( - - {wallets.map(w => ( - { - setValue(currentValue); - push(ROUTES.dashboard); - setOpen(false); - setCurrentContact(undefined); - setCurrentPaymentOption(undefined); - }} - > - {w.label} - - - ))} - - ) : null} - - {!!value && wallets.length ? ( - - - - { - setOpen(false); - }} - > - - Settings - - - - - ) : null} - - {!reachedWalletLimit ? ( - - { - setOpen(false); - }} - > - - New Wallet - - - - { - setOpen(false); - }} - > - - Restore Wallet - - - - - ) : null} - - - - + + + {!!value && wallets.length ? ( + <> + + + + cbk?.()} + className="flex items-center" + > + + {t('settings')} + + + + +
+ + ) : null} + +

+ {t('wallets')} +

+ + {wallets.length + ? wallets.map(w => ( + { + setValue(w.value); + push(ROUTES.dashboard); + cbk?.(); + setCurrentContact(undefined); + setCurrentPaymentOption(undefined); + }} + > + {w.label} + {value === w.value ? ( + + ) : null} + + )) + : null} + + {!reachedWalletLimit ? ( + <> + + cbk?.()} + className="flex items-center" + > + + {t('new-wallet')} + + + + cbk?.()} + className="flex items-center" + > + + {t('restore-wallet')} + + + + ) : null} + + ); -} +}; diff --git a/src/components/layout/AppLayout.tsx b/src/components/layout/AppLayout.tsx index f34a1ff..bd1ea4e 100644 --- a/src/components/layout/AppLayout.tsx +++ b/src/components/layout/AppLayout.tsx @@ -1,24 +1,21 @@ import { ArrowLeftRight, Home, - Landmark, LifeBuoy, Menu, MessageCircle, ScrollText, Settings, - Settings2, - Vault, + Wallet, } from 'lucide-react'; import Link from 'next/link'; -import { FC, ReactNode, useState } from 'react'; +import { useTranslations } from 'next-intl'; +import { FC, ReactNode, useMemo, useState } from 'react'; import { LogoutButton, LogoutButtonWithTooltip, } from '@/components/button/LogoutButton'; -import { VaultButton } from '@/components/button/VaultButton'; -import { ThemeToggle } from '@/components/toggle/ThemeToggle'; import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; import { @@ -26,55 +23,56 @@ import { TooltipContent, TooltipTrigger, } from '@/components/ui/tooltip'; +import { useUserQuery } from '@/graphql/queries/__generated__/user.generated'; +import { useGetAllWalletsQuery } from '@/graphql/queries/__generated__/wallet.generated'; import { ROUTES } from '@/utils/routes'; import { WalletButton } from '../button/WalletButton'; +import { Logo } from '../Logo'; import { Badge } from '../ui/badge'; +import { Button as ButtonV2 } from '../ui/button-v2'; export const AppLayout: FC<{ children: ReactNode }> = ({ children }) => { + const t = useTranslations('Index'); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const [mobileWalletMenuOpen, setMobileWalletMenuOpen] = useState(false); + + const { data } = useGetAllWalletsQuery(); + const { data: userData } = useUserQuery(); + + const reachedWalletLimit = useMemo(() => { + if (!data?.wallets.find_many || !userData?.user.wallet) return true; + + return data.wallets.find_many.length >= userData.user.wallet.wallet_limit; + }, [data?.wallets.find_many, userData?.user.wallet]); return ( -
-