Skip to content

Commit

Permalink
refactor: settings redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
secondl1ght committed Oct 29, 2024
1 parent bd9f8ae commit 85c87b4
Show file tree
Hide file tree
Showing 26 changed files with 1,276 additions and 823 deletions.
43 changes: 43 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,60 @@
"Dashboard": {
"asset": "An asset in your wallet.",
"buy": "Buy",
"go": "Go",
"liquid-explainer": "Liquid Bitcoin (L-BTC) is a wrapped version of Bitcoin (BTC) issued by the Liquid Network, a sidechain-based, Bitcoin layer-2 settlement network. It is a 1:1 pegged asset, meaning that one L-BTC is equivalent to one BTC. L-BTC is designed to improve the functionality of Bitcoin transactions, issuance of digital assets, and exchanges.",
"no-contacts": "No contacts to display.",
"price": "Price",
"recent": "Recent Contacts",
"secure": "Please secure your account with a stronger password.",
"sell": "Sell",
"tether-explainer": "Tether (USDT) is a stablecoin, a type of cryptocurrency designed to maintain a stable value relative to a fiat currency, in this case, the United States dollar (USD). It is issued by Tether Limited, a company founded in 2014, and is pegged to the value of the US dollar.",
"transactions": "Recent Transactions",
"warning": "In order to send USD you need to add Bitcoin to your wallet.",
"warning-title": "Add Bitcoin!",
"what-asset": "What is {asset}?"
},
"Settings": {
"2fa": "2-Factor Authentication",
"add-passkey": "Add Passkey",
"added": "Added",
"appearance": "Appearance",
"auth-app": "Authenticator App",
"change-pass": "Change Password",
"current-pass": "Current Password",
"dark": "Dark",
"dark-mode": "Dark Mode",
"display": "Display Mode",
"enable-encrypt": "Enable Encryption",
"enabled": "Enabled",
"encrypt-enabled": "Encryption Enabled",
"enter-otp": "Enter the one time password:",
"good": "Good",
"lang": "Preferred Language",
"language": "Language",
"light": "Light",
"light-mode": "Light Mode",
"login-only": "Login Only",
"login-wallet": "Login & Wallet",
"new-pass": "New Password",
"no-scan": "No QR code scanner? Enter the text below:",
"not-set": "Not Set",
"off": "OFF",
"otp": "Scan the QR code below with your preferred authenticator app or manually enter the code provided. Once set up, you will need to enter a one-time password (OTP) generated by the app each time you log in.",
"pass-confirm": "I have saved my password and understand that if I lose it I cannot recover my account and access to my funds will be lost forever.",
"passkeys": "Passkeys",
"saved-passkeys": "Saved Passkeys",
"setup": "Set Up",
"setup-2fa": "Setup 2FA",
"strong": "Strong",
"system": "System",
"system-mode": "System Mode",
"unknown-mode": "Unknown Mode",
"unlock-encrypt": "Unlock to Encrypt",
"very-strong": "Very Strong",
"very-weak": "Very Weak",
"weak": "Weak"
},
"Wallet": {
"Settings": {
"decrypt": "Decrypt to view your wallet’s secret mnemonic.",
Expand Down
43 changes: 43 additions & 0 deletions messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,60 @@
"Dashboard": {
"asset": "Un activo en tu billetera.",
"buy": "Comprar",
"go": "Go",
"liquid-explainer": "Liquid Bitcoin (L-BTC) es una versión envuelta de Bitcoin (BTC) emitida por la Liquid Network, una red de liquidación de segunda capa basada en una cadena lateral de Bitcoin. Es un activo vinculado 1:1, lo que significa que un L-BTC es equivalente a un BTC. L-BTC está diseñado para mejorar la funcionalidad de las transacciones de Bitcoin, la emisión de activos digitales y los intercambios.",
"no-contacts": "No hay contactos para mostrar.",
"price": "Precio",
"recent": "Contactos Recientes",
"secure": "Please secure your account with a stronger password.",
"sell": "Vender",
"tether-explainer": "Tether (USDT) es una stablecoin, un tipo de criptomoneda diseñada para mantener un valor estable en relación con una moneda fiduciaria, en este caso, el dólar estadounidense (USD). Es emitida por Tether Limited, una compañía fundada en 2014, y está vinculada al valor del dólar estadounidense.",
"transactions": "Transacciones Recientes",
"warning": "Para enviar USD tienes que tener Bitcoin en tu billetera.",
"warning-title": "Agrega Bitcoin!",
"what-asset": "Qué es {asset}?"
},
"Settings": {
"2fa": "2-Factor Authentication",
"add-passkey": "Add Passkey",
"added": "Added",
"appearance": "Appearance",
"auth-app": "Authenticator App",
"change-pass": "Change Password",
"current-pass": "Current Password",
"dark": "Dark",
"dark-mode": "Dark Mode",
"display": "Display Mode",
"enable-encrypt": "Enable Encryption",
"enabled": "Enabled",
"encrypt-enabled": "Encryption Enabled",
"enter-otp": "Enter the one time password:",
"good": "Good",
"lang": "Preferred Language",
"language": "Language",
"light": "Light",
"light-mode": "Light Mode",
"login-only": "Login Only",
"login-wallet": "Login & Wallet",
"new-pass": "New Password",
"no-scan": "No QR code scanner? Enter the text below:",
"not-set": "Not Set",
"off": "OFF",
"otp": "Scan the QR code below with your preferred authenticator app or manually enter the code provided. Once set up, you will need to enter a one-time password (OTP) generated by the app each time you log in.",
"pass-confirm": "I have saved my password and understand that if I lose it I cannot recover my account and access to my funds will be lost forever.",
"passkeys": "Passkeys",
"saved-passkeys": "Saved Passkeys",
"setup": "Set Up",
"setup-2fa": "Setup 2FA",
"strong": "Strong",
"system": "System",
"system-mode": "System Mode",
"unknown-mode": "Unknown Mode",
"unlock-encrypt": "Unlock to Encrypt",
"very-strong": "Very Strong",
"very-weak": "Very Weak",
"weak": "Weak"
},
"Wallet": {
"Settings": {
"decrypt": "Desencriptar para ver la frase secreta de tu billetera.",
Expand Down
5 changes: 5 additions & 0 deletions src/app/(app)/(layout)/settings/appearance/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Appearance } from '@/views/settings/Appearance';

export default function Page() {
return <Appearance />;
}
5 changes: 5 additions & 0 deletions src/app/(app)/(layout)/settings/language/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Language } from '@/views/settings/Language';

export default function Page() {
return <Language />;
}
5 changes: 5 additions & 0 deletions src/app/(app)/(layout)/settings/passkeys/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Passkeys } from '@/views/settings/Passkeys';

export default function Page() {
return <Passkeys />;
}
5 changes: 5 additions & 0 deletions src/app/(app)/(layout)/settings/password/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Password } from '@/views/settings/Password';

export default function Page() {
return <Password />;
}
2 changes: 2 additions & 0 deletions src/components/button/LogoutButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const LogoutButtonWithTooltip = () => {
onCompleted: () => {
clearKeys();
localStorage.removeItem(LOCALSTORAGE_KEYS.currentWalletId);
localStorage.removeItem('pw');
window.location.assign(ROUTES.home);
},
onError: () =>
Expand Down Expand Up @@ -62,6 +63,7 @@ export const LogoutButton = () => {
onCompleted: () => {
clearKeys();
localStorage.removeItem(LOCALSTORAGE_KEYS.currentWalletId);
localStorage.removeItem('pw');
window.location.assign(ROUTES.home);
},
onError: () =>
Expand Down
7 changes: 7 additions & 0 deletions src/components/form/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { startAuthentication } from '@simplewebauthn/browser';
import { PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/types';
import stringEntropy from 'fast-password-entropy';
import { Loader2 } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';
Expand Down Expand Up @@ -55,6 +56,8 @@ export const LoginForm = () => {

const [login, { data }] = useLoginMutation({
onCompleted: data => {
localStorage.setItem('pw', entropy.toString());

if (data.login.initial.two_factor?.methods.find(m => m.enabled)) {
setView('2fa');
} else {
Expand All @@ -81,6 +84,8 @@ export const LoginForm = () => {
},
});

const entropy = stringEntropy(form.getValues().password);

const onSubmit = async (data: z.infer<typeof FormSchema>) => {
if (loading) return;

Expand Down Expand Up @@ -205,6 +210,7 @@ export const LoginForm = () => {
<FormField
control={form.control}
name="email"
disabled={disabled}
render={({ field }) => (
<FormItem>
<FormLabel>{c('email')}</FormLabel>
Expand All @@ -219,6 +225,7 @@ export const LoginForm = () => {
<FormField
control={form.control}
name="password"
disabled={disabled}
render={({ field }) => (
<FormItem>
<FormLabel>{c('password')}</FormLabel>
Expand Down
6 changes: 4 additions & 2 deletions src/components/form/SignUpForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ export function SignUpForm() {
return;
}

localStorage.setItem('pw', entropy.toString());

window.location.href = ROUTES.dashboard;

break;
Expand All @@ -160,7 +162,7 @@ export function SignUpForm() {
return () => {
if (workerRef.current) workerRef.current.terminate();
};
}, [client, toast]);
}, [client, toast, entropy]);

return view === 'waitlist' ? (
<WaitlistForm setView={setView} setSubscriber={setSubscriber} />
Expand Down Expand Up @@ -259,7 +261,7 @@ export function SignUpForm() {
{s('set')}
</h1>

<p className="mb-6 rounded-xl border border-orange-400 px-4 py-2 text-sm">
<p className="mb-6 rounded-xl border border-orange-500 px-4 py-2 text-sm dark:border-orange-400">
{s('save')}
</p>

Expand Down
53 changes: 30 additions & 23 deletions src/components/toggle/LanguageToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use client';

import { Globe } from 'lucide-react';
import { ChevronsUpDown, Globe } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { useLocale } from 'next-intl';
import * as React from 'react';
import { useState } from 'react';
import { useIsClient } from 'usehooks-ts';
import { FC, useState } from 'react';

import {
DropdownMenu,
Expand All @@ -14,46 +14,53 @@ import {
} from '@/components/ui/dropdown-menu';
import { SupportedLanguage } from '@/i18n';
import { cn } from '@/utils/cn';
import { localeToLanguage } from '@/views/settings/Settings';

import { Button } from '../ui/button-v2';

const getCookie = () =>
export const getCookie = () =>
document.cookie
.split('; ')
.find(c => c.startsWith('locale='))
?.split('=')[1] as SupportedLanguage | undefined;

const setCookie = (locale: SupportedLanguage) =>
export const setCookie = (locale: SupportedLanguage) =>
(document.cookie = `locale=${locale}; max-age=31536000; path=/;`);

const deleteCookie = () =>
export const deleteCookie = () =>
(document.cookie = 'locale=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;');

export function LanguageToggle() {
const isClient = useIsClient();
export const LanguageToggle: FC<{ type?: 'compact' | 'select' }> = ({
type = 'compact',
}) => {
const locale = useLocale();

const { refresh } = useRouter();

const [language, setLanguage] = useState<SupportedLanguage | undefined>(
typeof window !== 'undefined' ? getCookie() : undefined
);

if (!isClient) return null;

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="neutral"
size="sm"
className="flex items-center space-x-1"
>
<Globe size={16} />
<p className="uppercase">
{language || document.documentElement.lang}
</p>
<span className="sr-only">Toggle theme</span>
</Button>
{type === 'compact' ? (
<Button
variant="neutral"
size="sm"
className="flex items-center space-x-1"
>
<Globe size={16} />
<p className="uppercase">{language || locale}</p>
<span className="sr-only">Toggle theme</span>
</Button>
) : type === 'select' ? (
<button className="flex h-10 w-full items-center justify-between space-x-2 rounded-xl border border-slate-200 px-4 dark:border-neutral-800">
<p>{localeToLanguage(language)}</p>

<ChevronsUpDown size={16} />
</button>
) : null}
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
Expand All @@ -78,12 +85,12 @@ export function LanguageToggle() {
onClick={() => {
deleteCookie();
setLanguage(undefined);
window.location.reload();
refresh();
}}
>
<p className={cn(!language && 'font-bold')}>System</p>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
};
26 changes: 15 additions & 11 deletions src/components/toggle/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
'use client';

import { Moon, Sun } from 'lucide-react';
import { ChevronsUpDown, Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import * as React from 'react';

import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { cn } from '@/utils/cn';

export function ThemeToggle() {
const { setTheme } = useTheme();
const { theme, setTheme } = useTheme();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<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>
</Button>
<button className="flex h-10 w-full items-center justify-between space-x-2 rounded-xl border border-slate-200 px-4 dark:border-neutral-800">
<p className="capitalize">{theme}</p>

<ChevronsUpDown size={16} />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme('light')}>
Light
<p className={cn(theme === 'light' && 'font-bold')}>Light</p>

<Sun size={16} className="ml-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>
Dark
<p className={cn(theme === 'dark' && 'font-bold')}>Dark</p>

<Moon size={16} className="ml-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>
System
<p className={cn(theme === 'system' && 'font-bold')}>System</p>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
Expand Down
Loading

0 comments on commit 85c87b4

Please sign in to comment.