From 875225591a61ad73ecdaef0104d737ecf17c46be Mon Sep 17 00:00:00 2001
From: reya
Date: Wed, 15 Nov 2023 14:20:45 +0700
Subject: [PATCH] wip: settings screen
---
src/app.tsx | 74 ++--
src/app/personal/editContact.tsx | 55 ---
src/app/personal/editProfile.tsx | 323 ------------------
src/app/personal/index.tsx | 26 --
src/app/settings/about.tsx | 3 +
src/app/settings/advanced.tsx | 3 +
src/app/settings/backup.tsx | 3 +
.../components/contactCard.tsx | 2 +-
.../components/postCard.tsx | 0
.../components/profileCard.tsx | 2 +-
.../components/relayCard.tsx | 0
.../components/zapCard.tsx | 0
src/app/settings/editContact.tsx | 41 +++
src/app/settings/editProfile.tsx | 303 ++++++++++++++++
src/app/settings/general.tsx | 3 +
src/app/settings/index.tsx | 20 +-
src/shared/accounts/active.tsx | 2 +-
src/shared/accounts/more.tsx | 14 +-
src/shared/layouts/settings.tsx | 79 ++++-
19 files changed, 500 insertions(+), 453 deletions(-)
delete mode 100644 src/app/personal/editContact.tsx
delete mode 100644 src/app/personal/editProfile.tsx
delete mode 100644 src/app/personal/index.tsx
create mode 100644 src/app/settings/about.tsx
create mode 100644 src/app/settings/advanced.tsx
create mode 100644 src/app/settings/backup.tsx
rename src/app/{personal => settings}/components/contactCard.tsx (97%)
rename src/app/{personal => settings}/components/postCard.tsx (100%)
rename src/app/{personal => settings}/components/profileCard.tsx (98%)
rename src/app/{personal => settings}/components/relayCard.tsx (100%)
rename src/app/{personal => settings}/components/zapCard.tsx (100%)
create mode 100644 src/app/settings/editContact.tsx
create mode 100644 src/app/settings/editProfile.tsx
create mode 100644 src/app/settings/general.tsx
diff --git a/src/app.tsx b/src/app.tsx
index bdc93bd4..2c4d295a 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -113,34 +113,6 @@ export default function App() {
},
],
},
- {
- path: '/personal',
- element: ,
- errorElement: ,
- children: [
- {
- path: '',
- async lazy() {
- const { PersonalScreen } = await import('@app/personal');
- return { Component: PersonalScreen };
- },
- },
- {
- path: 'edit-profile',
- async lazy() {
- const { EditProfileScreen } = await import('@app/personal/editProfile');
- return { Component: EditProfileScreen };
- },
- },
- {
- path: 'edit-contact',
- async lazy() {
- const { EditContactScreen } = await import('@app/personal/editContact');
- return { Component: EditContactScreen };
- },
- },
- ],
- },
{
path: '/new',
element: ,
@@ -260,8 +232,50 @@ export default function App() {
{
path: '',
async lazy() {
- const { SettingsScreen } = await import('@app/settings');
- return { Component: SettingsScreen };
+ const { UserSettingScreen } = await import('@app/settings');
+ return { Component: UserSettingScreen };
+ },
+ },
+ {
+ path: 'edit-profile',
+ async lazy() {
+ const { EditProfileScreen } = await import('@app/settings/editProfile');
+ return { Component: EditProfileScreen };
+ },
+ },
+ {
+ path: 'edit-contact',
+ async lazy() {
+ const { EditContactScreen } = await import('@app/settings/editContact');
+ return { Component: EditContactScreen };
+ },
+ },
+ {
+ path: 'general',
+ async lazy() {
+ const { GeneralSettingScreen } = await import('@app/settings/general');
+ return { Component: GeneralSettingScreen };
+ },
+ },
+ {
+ path: 'backup',
+ async lazy() {
+ const { BackupSettingScreen } = await import('@app/settings/backup');
+ return { Component: BackupSettingScreen };
+ },
+ },
+ {
+ path: 'advanced',
+ async lazy() {
+ const { AdvancedSettingScreen } = await import('@app/settings/advanced');
+ return { Component: AdvancedSettingScreen };
+ },
+ },
+ {
+ path: 'about',
+ async lazy() {
+ const { AboutScreen } = await import('@app/settings/about');
+ return { Component: AboutScreen };
},
},
],
diff --git a/src/app/personal/editContact.tsx b/src/app/personal/editContact.tsx
deleted file mode 100644
index 3cb871f1..00000000
--- a/src/app/personal/editContact.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-import { Link } from 'react-router-dom';
-
-import { useNDK } from '@libs/ndk/provider';
-import { useStorage } from '@libs/storage/provider';
-
-import { ArrowLeftIcon, LoaderIcon } from '@shared/icons';
-import { User } from '@shared/user';
-
-export function EditContactScreen() {
- const { db } = useStorage();
- const { ndk } = useNDK();
- const { status, data } = useQuery({
- queryKey: ['contacts'],
- queryFn: async () => {
- const user = ndk.getUser({ pubkey: db.account.pubkey });
-
- const follows = await user.follows();
- return [...follows];
- },
- refetchOnWindowFocus: false,
- });
-
- return (
-
-
-
-
- Back
-
-
Contact Manager
-
-
-
- {status === 'pending' ? (
-
-
-
- ) : (
- data.map((item) => (
-
-
-
- ))
- )}
-
-
- );
-}
diff --git a/src/app/personal/editProfile.tsx b/src/app/personal/editProfile.tsx
deleted file mode 100644
index 8d2d1a5d..00000000
--- a/src/app/personal/editProfile.tsx
+++ /dev/null
@@ -1,323 +0,0 @@
-import { NDKEvent, NDKKind, NDKUserProfile } from '@nostr-dev-kit/ndk';
-import { useQueryClient } from '@tanstack/react-query';
-import { message } from '@tauri-apps/plugin-dialog';
-import { useState } from 'react';
-import { useForm } from 'react-hook-form';
-import { Link } from 'react-router-dom';
-
-import { useNDK } from '@libs/ndk/provider';
-import { useStorage } from '@libs/storage/provider';
-
-import {
- ArrowLeftIcon,
- CheckCircleIcon,
- LoaderIcon,
- PlusIcon,
- UnverifiedIcon,
-} from '@shared/icons';
-
-import { useNostr } from '@utils/hooks/useNostr';
-
-export function EditProfileScreen() {
- const queryClient = useQueryClient();
-
- const [loading, setLoading] = useState(false);
- const [picture, setPicture] = useState('');
- const [banner, setBanner] = useState('');
- const [nip05, setNIP05] = useState({ verified: true, text: '' });
-
- const { db } = useStorage();
- const { ndk } = useNDK();
- const { upload } = useNostr();
- const {
- register,
- handleSubmit,
- reset,
- setError,
- formState: { isValid, errors },
- } = useForm({
- defaultValues: async () => {
- const res: NDKUserProfile = queryClient.getQueryData(['user', db.account.pubkey]);
- if (res.image) {
- setPicture(res.image);
- }
- if (res.banner) {
- setBanner(res.banner);
- }
- if (res.nip05) {
- setNIP05((prev) => ({ ...prev, text: res.nip05 }));
- }
- return res;
- },
- });
-
- const uploadAvatar = async () => {
- try {
- setLoading(true);
-
- const image = await upload();
-
- if (image) {
- setPicture(image);
- setLoading(false);
- }
- } catch (e) {
- setLoading(false);
- await message(`Upload failed, error: ${e}`, { title: 'Lume', type: 'error' });
- }
- };
-
- const uploadBanner = async () => {
- try {
- setLoading(true);
-
- const image = await upload();
-
- if (image) {
- setBanner(image);
- setLoading(false);
- }
- } catch (e) {
- setLoading(false);
- await message(`Upload failed, error: ${e}`, { title: 'Lume', type: 'error' });
- }
- };
-
- const onSubmit = async (data: NDKUserProfile) => {
- // start loading
- setLoading(true);
-
- const content = {
- ...data,
- username: data.name,
- display_name: data.name,
- bio: data.about,
- image: data.picture,
- };
-
- const event = new NDKEvent(ndk);
- event.kind = NDKKind.Metadata;
- event.tags = [];
-
- if (data.nip05) {
- const user = ndk.getUser({ pubkey: db.account.pubkey });
- const verify = await user.validateNip05(data.nip05);
- if (verify) {
- event.content = JSON.stringify({ ...content, nip05: data.nip05 });
- } else {
- setNIP05((prev) => ({ ...prev, verified: false }));
- setError('nip05', {
- type: 'manual',
- message: "Can't verify your Lume ID / NIP-05, please check again",
- });
- }
- } else {
- event.content = JSON.stringify(content);
- }
-
- const publishedRelays = await event.publish();
-
- if (publishedRelays) {
- // invalid cache
- queryClient.invalidateQueries({
- queryKey: ['user', db.account.pubkey],
- });
- // reset form
- reset();
- // reset state
- setLoading(false);
- setPicture(null);
- setBanner(null);
- } else {
- setLoading(false);
- }
- };
-
- return (
-
-
-
-
- Back
-
-
Edit Profile
-
-
-
-
- );
-}
diff --git a/src/app/personal/index.tsx b/src/app/personal/index.tsx
deleted file mode 100644
index d84191e7..00000000
--- a/src/app/personal/index.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { ContactCard } from '@app/personal/components/contactCard';
-import { PostCard } from '@app/personal/components/postCard';
-import { ProfileCard } from '@app/personal/components/profileCard';
-import { RelayCard } from '@app/personal/components/relayCard';
-import { ZapCard } from '@app/personal/components/zapCard';
-
-export function PersonalScreen() {
- return (
-
-
-
-
Personal Dashboard
-
-
-
-
- );
-}
diff --git a/src/app/settings/about.tsx b/src/app/settings/about.tsx
new file mode 100644
index 00000000..166794bc
--- /dev/null
+++ b/src/app/settings/about.tsx
@@ -0,0 +1,3 @@
+export function AboutScreen() {
+ return ;
+}
diff --git a/src/app/settings/advanced.tsx b/src/app/settings/advanced.tsx
new file mode 100644
index 00000000..fb2d693d
--- /dev/null
+++ b/src/app/settings/advanced.tsx
@@ -0,0 +1,3 @@
+export function AdvancedSettingScreen() {
+ return ;
+}
diff --git a/src/app/settings/backup.tsx b/src/app/settings/backup.tsx
new file mode 100644
index 00000000..b1c1c94b
--- /dev/null
+++ b/src/app/settings/backup.tsx
@@ -0,0 +1,3 @@
+export function BackupSettingScreen() {
+ return ;
+}
diff --git a/src/app/personal/components/contactCard.tsx b/src/app/settings/components/contactCard.tsx
similarity index 97%
rename from src/app/personal/components/contactCard.tsx
rename to src/app/settings/components/contactCard.tsx
index 3bc4ba75..29d3da76 100644
--- a/src/app/personal/components/contactCard.tsx
+++ b/src/app/settings/components/contactCard.tsx
@@ -37,7 +37,7 @@ export function ContactCard() {
Contacts
diff --git a/src/app/personal/components/postCard.tsx b/src/app/settings/components/postCard.tsx
similarity index 100%
rename from src/app/personal/components/postCard.tsx
rename to src/app/settings/components/postCard.tsx
diff --git a/src/app/personal/components/profileCard.tsx b/src/app/settings/components/profileCard.tsx
similarity index 98%
rename from src/app/personal/components/profileCard.tsx
rename to src/app/settings/components/profileCard.tsx
index 34823d55..a4756199 100644
--- a/src/app/personal/components/profileCard.tsx
+++ b/src/app/settings/components/profileCard.tsx
@@ -27,7 +27,7 @@ export function ProfileCard() {
diff --git a/src/app/personal/components/relayCard.tsx b/src/app/settings/components/relayCard.tsx
similarity index 100%
rename from src/app/personal/components/relayCard.tsx
rename to src/app/settings/components/relayCard.tsx
diff --git a/src/app/personal/components/zapCard.tsx b/src/app/settings/components/zapCard.tsx
similarity index 100%
rename from src/app/personal/components/zapCard.tsx
rename to src/app/settings/components/zapCard.tsx
diff --git a/src/app/settings/editContact.tsx b/src/app/settings/editContact.tsx
new file mode 100644
index 00000000..30dcd118
--- /dev/null
+++ b/src/app/settings/editContact.tsx
@@ -0,0 +1,41 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { useNDK } from '@libs/ndk/provider';
+import { useStorage } from '@libs/storage/provider';
+
+import { LoaderIcon } from '@shared/icons';
+import { User } from '@shared/user';
+
+export function EditContactScreen() {
+ const { db } = useStorage();
+ const { ndk } = useNDK();
+ const { status, data } = useQuery({
+ queryKey: ['contacts'],
+ queryFn: async () => {
+ const user = ndk.getUser({ pubkey: db.account.pubkey });
+
+ const follows = await user.follows();
+ return [...follows];
+ },
+ refetchOnWindowFocus: false,
+ });
+
+ return (
+
+ {status === 'pending' ? (
+
+
+
+ ) : (
+ data.map((item) => (
+
+
+
+ ))
+ )}
+
+ );
+}
diff --git a/src/app/settings/editProfile.tsx b/src/app/settings/editProfile.tsx
new file mode 100644
index 00000000..90e784a0
--- /dev/null
+++ b/src/app/settings/editProfile.tsx
@@ -0,0 +1,303 @@
+import { NDKEvent, NDKKind, NDKUserProfile } from '@nostr-dev-kit/ndk';
+import { useQueryClient } from '@tanstack/react-query';
+import { message } from '@tauri-apps/plugin-dialog';
+import { useState } from 'react';
+import { useForm } from 'react-hook-form';
+
+import { useNDK } from '@libs/ndk/provider';
+import { useStorage } from '@libs/storage/provider';
+
+import { CheckCircleIcon, LoaderIcon, PlusIcon, UnverifiedIcon } from '@shared/icons';
+
+import { useNostr } from '@utils/hooks/useNostr';
+
+export function EditProfileScreen() {
+ const queryClient = useQueryClient();
+
+ const [loading, setLoading] = useState(false);
+ const [picture, setPicture] = useState('');
+ const [banner, setBanner] = useState('');
+ const [nip05, setNIP05] = useState({ verified: true, text: '' });
+
+ const { db } = useStorage();
+ const { ndk } = useNDK();
+ const { upload } = useNostr();
+ const {
+ register,
+ handleSubmit,
+ reset,
+ setError,
+ formState: { isValid, errors },
+ } = useForm({
+ defaultValues: async () => {
+ const res: NDKUserProfile = queryClient.getQueryData(['user', db.account.pubkey]);
+ if (res.image) {
+ setPicture(res.image);
+ }
+ if (res.banner) {
+ setBanner(res.banner);
+ }
+ if (res.nip05) {
+ setNIP05((prev) => ({ ...prev, text: res.nip05 }));
+ }
+ return res;
+ },
+ });
+
+ const uploadAvatar = async () => {
+ try {
+ setLoading(true);
+
+ const image = await upload();
+
+ if (image) {
+ setPicture(image);
+ setLoading(false);
+ }
+ } catch (e) {
+ setLoading(false);
+ await message(`Upload failed, error: ${e}`, { title: 'Lume', type: 'error' });
+ }
+ };
+
+ const uploadBanner = async () => {
+ try {
+ setLoading(true);
+
+ const image = await upload();
+
+ if (image) {
+ setBanner(image);
+ setLoading(false);
+ }
+ } catch (e) {
+ setLoading(false);
+ await message(`Upload failed, error: ${e}`, { title: 'Lume', type: 'error' });
+ }
+ };
+
+ const onSubmit = async (data: NDKUserProfile) => {
+ // start loading
+ setLoading(true);
+
+ const content = {
+ ...data,
+ username: data.name,
+ display_name: data.name,
+ bio: data.about,
+ image: data.picture,
+ };
+
+ const event = new NDKEvent(ndk);
+ event.kind = NDKKind.Metadata;
+ event.tags = [];
+
+ if (data.nip05) {
+ const user = ndk.getUser({ pubkey: db.account.pubkey });
+ const verify = await user.validateNip05(data.nip05);
+ if (verify) {
+ event.content = JSON.stringify({ ...content, nip05: data.nip05 });
+ } else {
+ setNIP05((prev) => ({ ...prev, verified: false }));
+ setError('nip05', {
+ type: 'manual',
+ message: "Can't verify your Lume ID / NIP-05, please check again",
+ });
+ }
+ } else {
+ event.content = JSON.stringify(content);
+ }
+
+ const publishedRelays = await event.publish();
+
+ if (publishedRelays) {
+ // invalid cache
+ queryClient.invalidateQueries({
+ queryKey: ['user', db.account.pubkey],
+ });
+ // reset form
+ reset();
+ // reset state
+ setLoading(false);
+ setPicture(null);
+ setBanner(null);
+ } else {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/src/app/settings/general.tsx b/src/app/settings/general.tsx
new file mode 100644
index 00000000..b6f089a9
--- /dev/null
+++ b/src/app/settings/general.tsx
@@ -0,0 +1,3 @@
+export function GeneralSettingScreen() {
+ return
;
+}
diff --git a/src/app/settings/index.tsx b/src/app/settings/index.tsx
index 93ea91e9..fb272c15 100644
--- a/src/app/settings/index.tsx
+++ b/src/app/settings/index.tsx
@@ -1,3 +1,19 @@
-export function SettingsScreen() {
- return
;
+import { ContactCard } from '@app/settings/components/contactCard';
+import { PostCard } from '@app/settings/components/postCard';
+import { ProfileCard } from '@app/settings/components/profileCard';
+import { RelayCard } from '@app/settings/components/relayCard';
+import { ZapCard } from '@app/settings/components/zapCard';
+
+export function UserSettingScreen() {
+ return (
+
+ );
}
diff --git a/src/shared/accounts/active.tsx b/src/shared/accounts/active.tsx
index 3c734827..23d97d3a 100644
--- a/src/shared/accounts/active.tsx
+++ b/src/shared/accounts/active.tsx
@@ -25,7 +25,7 @@ export function ActiveAccount() {
return (
-
+
- Dashboard
-
-
-
-
Settings
-
-
-
+
diff --git a/src/shared/layouts/settings.tsx b/src/shared/layouts/settings.tsx
index 1c3e5d57..7280d4bb 100644
--- a/src/shared/layouts/settings.tsx
+++ b/src/shared/layouts/settings.tsx
@@ -1,8 +1,11 @@
-import { Outlet, ScrollRestoration } from 'react-router-dom';
+import { NavLink, Outlet, ScrollRestoration } from 'react-router-dom';
+import { twMerge } from 'tailwind-merge';
import { WindowTitlebar } from 'tauri-controls';
import { useStorage } from '@libs/storage/provider';
+import { SecureIcon, SettingsIcon } from '@shared/icons';
+
export function SettingsLayout() {
const { db } = useStorage();
@@ -13,7 +16,79 @@ export function SettingsLayout() {
) : (
)}
-
+
+
+
+ twMerge(
+ 'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 hover:bg-neutral-100 dark:hover:bg-neutral-900',
+ isActive
+ ? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
+ : ''
+ )
+ }
+ >
+
+ User
+
+
+ twMerge(
+ 'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
+ isActive
+ ? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
+ : ''
+ )
+ }
+ >
+
+ General
+
+
+ twMerge(
+ 'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
+ isActive
+ ? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
+ : ''
+ )
+ }
+ >
+
+ Backup
+
+
+ twMerge(
+ 'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
+ isActive
+ ? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
+ : ''
+ )
+ }
+ >
+
+ Advanced
+
+
+ twMerge(
+ 'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900',
+ isActive
+ ? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
+ : ''
+ )
+ }
+ >
+
+ About
+
+