From 89bb8d88f6a4ae80ac1045cc75bdef7363ba7752 Mon Sep 17 00:00:00 2001 From: reya Date: Tue, 2 Apr 2024 13:19:26 +0700 Subject: [PATCH] feat: settings screens --- apps/desktop2/src/app.tsx | 1 + apps/desktop2/src/routes/__root.tsx | 2 + apps/desktop2/src/routes/auth/new/backup.tsx | 2 +- .../src/routes/auth/settings.lazy.tsx | 33 ++--- apps/desktop2/src/routes/index.tsx | 7 + apps/desktop2/src/routes/settings.tsx | 106 +++++++++++++++ apps/desktop2/src/routes/settings/backup.tsx | 122 ++++++++++++++++++ .../src/routes/settings/general.lazy.tsx | 5 + .../src/routes/settings/index.lazy.tsx | 9 -- .../src/routes/settings/user.lazy.tsx | 5 + .../desktop2/src/routes/settings/zap.lazy.tsx | 96 ++++++++++++++ packages/ark/src/ark.ts | 9 +- packages/icons/src/settings.tsx | 8 +- packages/icons/src/user.tsx | 30 ++--- packages/types/index.d.ts | 1 + packages/ui/src/note/content.tsx | 22 ++-- src-tauri/locales/en.json | 2 +- src-tauri/src/main.rs | 1 + src-tauri/src/nostr/keys.rs | 11 ++ src-tauri/src/tray.rs | 21 +-- 20 files changed, 419 insertions(+), 74 deletions(-) create mode 100644 apps/desktop2/src/routes/settings.tsx create mode 100644 apps/desktop2/src/routes/settings/backup.tsx create mode 100644 apps/desktop2/src/routes/settings/general.lazy.tsx delete mode 100644 apps/desktop2/src/routes/settings/index.lazy.tsx create mode 100644 apps/desktop2/src/routes/settings/user.lazy.tsx create mode 100644 apps/desktop2/src/routes/settings/zap.lazy.tsx diff --git a/apps/desktop2/src/app.tsx b/apps/desktop2/src/app.tsx index e288989d..cdc90969 100644 --- a/apps/desktop2/src/app.tsx +++ b/apps/desktop2/src/app.tsx @@ -37,6 +37,7 @@ const router = createRouter({ ark: undefined!, platform: platformName, locale: osLocale, + settings: null, queryClient, }, }); diff --git a/apps/desktop2/src/routes/__root.tsx b/apps/desktop2/src/routes/__root.tsx index 649e9ad0..ec91d210 100644 --- a/apps/desktop2/src/routes/__root.tsx +++ b/apps/desktop2/src/routes/__root.tsx @@ -7,12 +7,14 @@ import { import { type Ark } from "@lume/ark"; import { type QueryClient } from "@tanstack/react-query"; import { type Platform } from "@tauri-apps/plugin-os"; +import { Settings } from "@lume/types"; interface RouterContext { ark: Ark; queryClient: QueryClient; platform: Platform; locale: string; + settings: Settings; } export const Route = createRootRouteWithContext()({ diff --git a/apps/desktop2/src/routes/auth/new/backup.tsx b/apps/desktop2/src/routes/auth/new/backup.tsx index f9c71ff0..e90f21e4 100644 --- a/apps/desktop2/src/routes/auth/new/backup.tsx +++ b/apps/desktop2/src/routes/auth/new/backup.tsx @@ -68,7 +68,7 @@ function Screen() {
-
-
- - - {t("global.continue")} - -
+
); diff --git a/apps/desktop2/src/routes/index.tsx b/apps/desktop2/src/routes/index.tsx index 9778a9e2..03b1fb96 100644 --- a/apps/desktop2/src/routes/index.tsx +++ b/apps/desktop2/src/routes/index.tsx @@ -24,6 +24,10 @@ export const Route = createFileRoute("/")({ const account = accounts[0].npub; const loadedAccount = await ark.load_selected_account(account); + const settings = await ark.get_settings(account); + + // Update settings + context.settings = settings; if (loadedAccount) { throw redirect({ @@ -43,12 +47,15 @@ export const Route = createFileRoute("/")({ function Screen() { const ark = useArk(); const navigate = useNavigate(); + const context = Route.useRouteContext(); const [loading, setLoading] = useState(false); const select = async (npub: string) => { setLoading(true); const loadAccount = await ark.load_selected_account(npub); + context.settings = await ark.get_settings(npub); + if (loadAccount) { navigate({ to: "/$account/home", diff --git a/apps/desktop2/src/routes/settings.tsx b/apps/desktop2/src/routes/settings.tsx new file mode 100644 index 00000000..394e3c23 --- /dev/null +++ b/apps/desktop2/src/routes/settings.tsx @@ -0,0 +1,106 @@ +import { SettingsIcon, UserIcon, ZapIcon, SecureIcon } from "@lume/icons"; +import { cn } from "@lume/utils"; +import { Link } from "@tanstack/react-router"; +import { Outlet, createFileRoute } from "@tanstack/react-router"; +import { useTranslation } from "react-i18next"; + +export const Route = createFileRoute("/settings")({ + component: Screen, +}); + +function Screen() { + const { t } = useTranslation(); + + return ( +
+
+
+ + {({ isActive }) => { + return ( +
+ +

+ {t("settings.general.title")} +

+
+ ); + }} + + + {({ isActive }) => { + return ( +
+ +

+ {t("settings.user.title")} +

+
+ ); + }} + + + {({ isActive }) => { + return ( +
+ +

+ {t("settings.zap.title")} +

+
+ ); + }} + + + {({ isActive }) => { + return ( +
+ +

+ {t("settings.backup.title")} +

+
+ ); + }} + +
+
+
+
+ +
+
+
+ ); +} diff --git a/apps/desktop2/src/routes/settings/backup.tsx b/apps/desktop2/src/routes/settings/backup.tsx new file mode 100644 index 00000000..1b3e8832 --- /dev/null +++ b/apps/desktop2/src/routes/settings/backup.tsx @@ -0,0 +1,122 @@ +import { type Account } from "@lume/types"; +import { User } from "@lume/ui"; +import { displayNsec } from "@lume/utils"; +import { createFileRoute } from "@tanstack/react-router"; +import { invoke } from "@tauri-apps/api/core"; +import { writeText } from "@tauri-apps/plugin-clipboard-manager"; +import { useState } from "react"; +import { toast } from "sonner"; + +export const Route = createFileRoute("/settings/backup")({ + component: Screen, + loader: async ({ context }) => { + const ark = context.ark; + const npubs = await ark.get_all_accounts(); + + let accounts: Account[] = []; + + for (const account of npubs) { + const nsec: string = await invoke("get_stored_nsec", { + npub: account.npub, + }); + accounts.push({ ...account, nsec }); + } + + return accounts; + }, +}); + +function Screen() { + const accounts = Route.useLoaderData(); + + return ( +
+ {accounts.map((account, index) => ( +
+
+ Account {index} +
+ +
+ ))} +
+ ); +} + +function Account({ account }: { account: Account }) { + const [key, setKey] = useState(account.nsec); + const [copied, setCopied] = useState(false); + const [passphase, setPassphase] = useState(""); + + const encrypt = async () => { + const encrypted: string = await invoke("get_encrypted_key", { + npub: account.npub, + password: passphase, + }); + setKey(encrypted); + }; + + const copyKey = async () => { + try { + await writeText(key); + setCopied(true); + } catch (e) { + toast.error(e); + } + }; + + return ( +
+ + + +
+ + +
+
+
+
+
+ + +
+
+ +
+ setPassphase(e.target.value)} + className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800" + /> + +
+
+
+
+ ); +} diff --git a/apps/desktop2/src/routes/settings/general.lazy.tsx b/apps/desktop2/src/routes/settings/general.lazy.tsx new file mode 100644 index 00000000..8066268d --- /dev/null +++ b/apps/desktop2/src/routes/settings/general.lazy.tsx @@ -0,0 +1,5 @@ +import { createLazyFileRoute } from '@tanstack/react-router' + +export const Route = createLazyFileRoute('/settings/general')({ + component: () =>
Hello /settings/general!
+}) \ No newline at end of file diff --git a/apps/desktop2/src/routes/settings/index.lazy.tsx b/apps/desktop2/src/routes/settings/index.lazy.tsx deleted file mode 100644 index 1ac6a83c..00000000 --- a/apps/desktop2/src/routes/settings/index.lazy.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { createLazyFileRoute } from "@tanstack/react-router"; - -export const Route = createLazyFileRoute("/settings/")({ - component: Screen, -}); - -function Screen() { - return
Settings
; -} diff --git a/apps/desktop2/src/routes/settings/user.lazy.tsx b/apps/desktop2/src/routes/settings/user.lazy.tsx new file mode 100644 index 00000000..36126678 --- /dev/null +++ b/apps/desktop2/src/routes/settings/user.lazy.tsx @@ -0,0 +1,5 @@ +import { createLazyFileRoute } from '@tanstack/react-router' + +export const Route = createLazyFileRoute('/settings/user')({ + component: () =>
Hello /settings/user!
+}) \ No newline at end of file diff --git a/apps/desktop2/src/routes/settings/zap.lazy.tsx b/apps/desktop2/src/routes/settings/zap.lazy.tsx new file mode 100644 index 00000000..ece44974 --- /dev/null +++ b/apps/desktop2/src/routes/settings/zap.lazy.tsx @@ -0,0 +1,96 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import { invoke } from "@tauri-apps/api/core"; +import { useState } from "react"; +import { toast } from "sonner"; + +export const Route = createLazyFileRoute("/settings/zap")({ + component: Screen, +}); + +function Screen() { + return ( +
+
+ + +
+
+ ); +} + +function Connection() { + const [uri, setUri] = useState(""); + + const connect = async () => { + try { + await invoke("set_nwc", { uri }); + } catch (e) { + toast.error(String(e)); + } + }; + + return ( +
+
Connection
+
+
+ +
+ setUri(e.target.value)} + placeholder="nostrconnect://" + className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800" + /> + +
+
+
+
+ ); +} + +function DefaultAmount() { + return ( +
+
Default amount
+
+
+ +
+ + +
+
+
+
+ ); +} diff --git a/packages/ark/src/ark.ts b/packages/ark/src/ark.ts index 85ad2959..adbd627b 100644 --- a/packages/ark/src/ark.ts +++ b/packages/ark/src/ark.ts @@ -527,8 +527,13 @@ export class Ark { const settings: Settings = JSON.parse(cmd); return settings; - } catch (e) { - throw new Error(e); + } catch { + const defaultSettings: Settings = { + autoUpdate: false, + enhancedPrivacy: false, + notification: false, + }; + return defaultSettings; } } diff --git a/packages/icons/src/settings.tsx b/packages/icons/src/settings.tsx index 64e0cf70..d7292acb 100644 --- a/packages/icons/src/settings.tsx +++ b/packages/icons/src/settings.tsx @@ -7,15 +7,15 @@ export function SettingsIcon( ); diff --git a/packages/icons/src/user.tsx b/packages/icons/src/user.tsx index 1295d333..01ddfcbb 100644 --- a/packages/icons/src/user.tsx +++ b/packages/icons/src/user.tsx @@ -1,24 +1,16 @@ import { SVGProps } from "react"; export function UserIcon( - props: JSX.IntrinsicAttributes & SVGProps, + props: JSX.IntrinsicAttributes & SVGProps, ) { - return ( - - - - ); + return ( + + + + ); } diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts index 73b552e9..012aa982 100644 --- a/packages/types/index.d.ts +++ b/packages/types/index.d.ts @@ -56,6 +56,7 @@ export interface Contact { export interface Account { npub: string; + nsec?: string; contacts?: string[]; interests?: Interests; } diff --git a/packages/ui/src/note/content.tsx b/packages/ui/src/note/content.tsx index 12415f5b..7b536125 100644 --- a/packages/ui/src/note/content.tsx +++ b/packages/ui/src/note/content.tsx @@ -1,4 +1,4 @@ -import { Kind } from "@lume/types"; +import { Kind, Settings } from "@lume/types"; import { AUDIOS, IMAGES, @@ -17,6 +17,7 @@ import { Hashtag } from "./mentions/hashtag"; import { VideoPreview } from "./preview/video"; import { ImagePreview } from "./preview/image"; import reactStringReplace from "react-string-replace"; +import { useRouteContext } from "@tanstack/react-router"; export function NoteContent({ compact = true, @@ -25,6 +26,7 @@ export function NoteContent({ compact?: boolean; className?: string; }) { + const settings: Settings = useRouteContext({ strict: false }); const event = useNoteContext(); const content = useMemo(() => { const text = event.content.trim(); @@ -81,16 +83,18 @@ export function NoteContent({ const url = new URL(match); const ext = url.pathname.split(".")[1]; - if (IMAGES.includes(ext)) { - return ; - } + if (!settings.enhancedPrivacy) { + if (IMAGES.includes(ext)) { + return ; + } - if (VIDEOS.includes(ext)) { - return ; - } + if (VIDEOS.includes(ext)) { + return ; + } - if (AUDIOS.includes(ext)) { - return ; + if (AUDIOS.includes(ext)) { + return ; + } } return ( diff --git a/src-tauri/locales/en.json b/src-tauri/locales/en.json index 1d96ffe3..d47cf177 100644 --- a/src-tauri/locales/en.json +++ b/src-tauri/locales/en.json @@ -234,7 +234,7 @@ } }, "user": { - "title": "User" + "title": "Account" }, "zap": { "title": "Zap", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 07add541..fd3a9e34 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -103,6 +103,7 @@ fn main() { nostr::keys::create_keys, nostr::keys::save_key, nostr::keys::get_encrypted_key, + nostr::keys::get_stored_nsec, nostr::keys::verify_signer, nostr::keys::load_selected_account, nostr::keys::event_to_bech32, diff --git a/src-tauri/src/nostr/keys.rs b/src-tauri/src/nostr/keys.rs index 7df72e17..10ccdc46 100644 --- a/src-tauri/src/nostr/keys.rs +++ b/src-tauri/src/nostr/keys.rs @@ -103,6 +103,17 @@ pub fn get_encrypted_key(npub: &str, password: &str) -> Result { } } +#[tauri::command] +pub fn get_stored_nsec(npub: &str) -> Result { + let keyring = Entry::new("Lume Secret Storage", npub).unwrap(); + + if let Ok(nsec) = keyring.get_password() { + Ok(nsec) + } else { + Err("Key not found".into()) + } +} + #[tauri::command] pub async fn load_selected_account(npub: &str, state: State<'_, Nostr>) -> Result { let client = &state.client; diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index 8e866bc7..76b54647 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -60,15 +60,18 @@ pub fn create_tray(app: &tauri::AppHandle) -> tauri::Result<()> { println!("todo!") } "settings" => { - let _ = - WebviewWindowBuilder::new(app, "settings", WebviewUrl::App(PathBuf::from("settings"))) - .title("Editor") - .min_inner_size(600., 500.) - .inner_size(800., 500.) - .hidden_title(true) - .title_bar_style(TitleBarStyle::Overlay) - .build() - .unwrap(); + let _ = WebviewWindowBuilder::new( + app, + "settings", + WebviewUrl::App(PathBuf::from("settings/general")), + ) + .title("Editor") + .min_inner_size(600., 500.) + .inner_size(800., 500.) + .hidden_title(true) + .title_bar_style(TitleBarStyle::Overlay) + .build() + .unwrap(); } "quit" => { app.exit(0);