mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
update settings screen
This commit is contained in:
parent
c29ed9669e
commit
f2eb7a90ad
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -2677,7 +2677,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lume"
|
name = "lume"
|
||||||
version = "1.2.0"
|
version = "1.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -25,22 +25,6 @@
|
|||||||
"hiddenTitle": true,
|
"hiddenTitle": true,
|
||||||
"visible": false,
|
"visible": false,
|
||||||
"fileDropEnabled": true
|
"fileDropEnabled": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"width": 1080,
|
|
||||||
"height": 800,
|
|
||||||
"resizable": false,
|
|
||||||
"theme": "Dark",
|
|
||||||
"label": "settings",
|
|
||||||
"url": "settings",
|
|
||||||
"title": "Settings",
|
|
||||||
"titleBarStyle": "Overlay",
|
|
||||||
"transparent": false,
|
|
||||||
"center": true,
|
|
||||||
"fullscreen": false,
|
|
||||||
"hiddenTitle": true,
|
|
||||||
"visible": false,
|
|
||||||
"fileDropEnabled": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -32,26 +32,6 @@
|
|||||||
"effects": ["hudWindow"],
|
"effects": ["hudWindow"],
|
||||||
"state": "followsWindowActiveState"
|
"state": "followsWindowActiveState"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"width": 1080,
|
|
||||||
"height": 800,
|
|
||||||
"resizable": false,
|
|
||||||
"theme": "Dark",
|
|
||||||
"label": "settings",
|
|
||||||
"url": "settings",
|
|
||||||
"title": "Settings",
|
|
||||||
"titleBarStyle": "Overlay",
|
|
||||||
"transparent": true,
|
|
||||||
"center": true,
|
|
||||||
"fullscreen": false,
|
|
||||||
"hiddenTitle": true,
|
|
||||||
"visible": false,
|
|
||||||
"fileDropEnabled": false,
|
|
||||||
"windowEffects": {
|
|
||||||
"effects": ["hudWindow"],
|
|
||||||
"state": "followsWindowActiveState"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -29,26 +29,6 @@
|
|||||||
"effects": ["micaDark", "micaLight", "acrylic"],
|
"effects": ["micaDark", "micaLight", "acrylic"],
|
||||||
"state": "followsWindowActiveState"
|
"state": "followsWindowActiveState"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"width": 1080,
|
|
||||||
"height": 800,
|
|
||||||
"resizable": false,
|
|
||||||
"theme": "Dark",
|
|
||||||
"label": "settings",
|
|
||||||
"url": "settings",
|
|
||||||
"title": "Settings",
|
|
||||||
"titleBarStyle": "Overlay",
|
|
||||||
"transparent": true,
|
|
||||||
"center": true,
|
|
||||||
"fullscreen": false,
|
|
||||||
"hiddenTitle": true,
|
|
||||||
"visible": false,
|
|
||||||
"fileDropEnabled": false,
|
|
||||||
"windowEffects": {
|
|
||||||
"effects": ["micaDark", "micaLight", "acrylic"],
|
|
||||||
"state": "followsWindowActiveState"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
16
src/app.tsx
16
src/app.tsx
@ -110,6 +110,7 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: '/auth',
|
path: '/auth',
|
||||||
element: <AuthLayout />,
|
element: <AuthLayout />,
|
||||||
|
errorElement: <ErrorScreen />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'welcome',
|
path: 'welcome',
|
||||||
@ -121,6 +122,7 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: 'import',
|
path: 'import',
|
||||||
element: <AuthImportScreen />,
|
element: <AuthImportScreen />,
|
||||||
|
errorElement: <ErrorScreen />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@ -148,6 +150,7 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
element: <AuthCreateScreen />,
|
element: <AuthCreateScreen />,
|
||||||
|
errorElement: <ErrorScreen />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@ -175,6 +178,7 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: 'onboarding',
|
path: 'onboarding',
|
||||||
element: <OnboardingScreen />,
|
element: <OnboardingScreen />,
|
||||||
|
errorElement: <ErrorScreen />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@ -232,23 +236,17 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
element: <SettingsLayout />,
|
element: <SettingsLayout />,
|
||||||
|
errorElement: <ErrorScreen />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'general',
|
path: '',
|
||||||
async lazy() {
|
async lazy() {
|
||||||
const { GeneralSettingsScreen } = await import('@app/settings/general');
|
const { GeneralSettingsScreen } = await import('@app/settings/general');
|
||||||
return { Component: GeneralSettingsScreen };
|
return { Component: GeneralSettingsScreen };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'shortcuts',
|
path: 'backup',
|
||||||
async lazy() {
|
|
||||||
const { ShortcutsSettingsScreen } = await import('@app/settings/shortcuts');
|
|
||||||
return { Component: ShortcutsSettingsScreen };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'account',
|
|
||||||
async lazy() {
|
async lazy() {
|
||||||
const { AccountSettingsScreen } = await import('@app/settings/account');
|
const { AccountSettingsScreen } = await import('@app/settings/account');
|
||||||
return { Component: AccountSettingsScreen };
|
return { Component: AccountSettingsScreen };
|
||||||
|
@ -32,11 +32,11 @@ export function ChatsList() {
|
|||||||
if (status === 'loading') {
|
if (status === 'loading') {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="inline-flex h-10 items-center gap-2.5 pl-4 border-l-2 border-transparent">
|
<div className="inline-flex h-10 items-center gap-2.5 border-l-2 border-transparent pl-4">
|
||||||
<div className="relative h-7 w-7 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
<div className="relative h-7 w-7 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
<div className="h-4 w-full animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
<div className="h-4 w-full animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
<div className="inline-flex h-10 items-center gap-2.5 pl-4 border-l-2 border-transparent">
|
<div className="inline-flex h-10 items-center gap-2.5 border-l-2 border-transparent pl-4">
|
||||||
<div className="relative h-7 w-7 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
<div className="relative h-7 w-7 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
<div className="h-4 w-full animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
<div className="h-4 w-full animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { nip19 } from 'nostr-tools';
|
||||||
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
@ -7,84 +8,119 @@ import { EyeOffIcon, EyeOnIcon } from '@shared/icons';
|
|||||||
import { useStronghold } from '@stores/stronghold';
|
import { useStronghold } from '@stores/stronghold';
|
||||||
|
|
||||||
export function AccountSettingsScreen() {
|
export function AccountSettingsScreen() {
|
||||||
const [type, setType] = useState('password');
|
|
||||||
const privkey = useStronghold((state) => state.privkey);
|
|
||||||
const { db } = useStorage();
|
const { db } = useStorage();
|
||||||
|
|
||||||
const showPrivateKey = () => {
|
const [privType, setPrivType] = useState('password');
|
||||||
if (type === 'password') {
|
const [nsecType, setNsecType] = useState('password');
|
||||||
setType('text');
|
|
||||||
|
const privkey = useStronghold((state) => state.privkey);
|
||||||
|
const nsec = useMemo(() => nip19.nsecEncode(privkey), [privkey]);
|
||||||
|
|
||||||
|
const showPrivkey = () => {
|
||||||
|
if (privType === 'password') {
|
||||||
|
setPrivType('text');
|
||||||
} else {
|
} else {
|
||||||
setType('password');
|
setPrivType('password');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showNsec = () => {
|
||||||
|
if (nsecType === 'password') {
|
||||||
|
setNsecType('text');
|
||||||
|
} else {
|
||||||
|
setNsecType('password');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full px-3 pt-12">
|
<div className="h-full w-full px-3 pt-11">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h1 className="text-lg font-semibold text-white">Account</h1>
|
<h1 className="text-xl font-semibold text-white">Account</h1>
|
||||||
<div className="">
|
<div className="flex flex-col gap-4 rounded-xl bg-white/10 p-3 backdrop-blur-xl">
|
||||||
{status === 'loading' ? (
|
<div className="flex flex-col gap-1">
|
||||||
<p>Loading...</p>
|
<label htmlFor="pubkey" className="text-base font-semibold text-white/50">
|
||||||
) : (
|
Public Key
|
||||||
<div className="flex flex-col gap-4">
|
</label>
|
||||||
<div className="flex flex-col gap-1">
|
<input
|
||||||
<label htmlFor="pubkey" className="text-base font-semibold text-white/50">
|
readOnly
|
||||||
Public Key
|
value={db.account.pubkey}
|
||||||
</label>
|
className="relative w-full rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
||||||
<input
|
/>
|
||||||
readOnly
|
</div>
|
||||||
value={db.account.pubkey}
|
<div className="flex flex-col gap-1">
|
||||||
className="relative w-2/3 rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
<label htmlFor="npub" className="text-base font-semibold text-white/50">
|
||||||
/>
|
Npub
|
||||||
</div>
|
</label>
|
||||||
<div className="flex flex-col gap-1">
|
<input
|
||||||
<label htmlFor="npub" className="text-base font-semibold text-white/50">
|
readOnly
|
||||||
Npub
|
value={db.account.npub}
|
||||||
</label>
|
className="relative w-full rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
||||||
<input
|
/>
|
||||||
readOnly
|
</div>
|
||||||
value={db.account.npub}
|
<div className="flex flex-col gap-1">
|
||||||
className="relative w-2/3 rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
<label htmlFor="privkey" className="text-base font-semibold text-white/50">
|
||||||
/>
|
Private Key
|
||||||
</div>
|
</label>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="relative w-full">
|
||||||
<label
|
<input
|
||||||
htmlFor="privkey"
|
readOnly
|
||||||
className="text-base font-semibold text-white/50"
|
type={privType}
|
||||||
>
|
value={privkey}
|
||||||
Private Key
|
className="relative w-full rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
||||||
</label>
|
/>
|
||||||
<div className="relative w-2/3">
|
<button
|
||||||
<input
|
type="button"
|
||||||
readOnly
|
onClick={() => showPrivkey()}
|
||||||
type={type}
|
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700"
|
||||||
value={privkey}
|
>
|
||||||
className="relative w-full rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
{privType === 'password' ? (
|
||||||
|
<EyeOffIcon
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className="text-white/50 group-hover:text-white"
|
||||||
/>
|
/>
|
||||||
<button
|
) : (
|
||||||
type="button"
|
<EyeOnIcon
|
||||||
onClick={() => showPrivateKey()}
|
width={20}
|
||||||
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700"
|
height={20}
|
||||||
>
|
className="text-white/50 group-hover:text-white"
|
||||||
{type === 'password' ? (
|
/>
|
||||||
<EyeOffIcon
|
)}
|
||||||
width={20}
|
</button>
|
||||||
height={20}
|
|
||||||
className="text-white/50 group-hover:text-white"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<EyeOnIcon
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
className="text-white/50 group-hover:text-white"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<label htmlFor="privkey" className="text-base font-semibold text-white/50">
|
||||||
|
Nsec
|
||||||
|
</label>
|
||||||
|
<div className="relative w-full">
|
||||||
|
<input
|
||||||
|
readOnly
|
||||||
|
type={nsecType}
|
||||||
|
value={nsec}
|
||||||
|
className="relative w-full rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => showNsec()}
|
||||||
|
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700"
|
||||||
|
>
|
||||||
|
{privType === 'password' ? (
|
||||||
|
<EyeOffIcon
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className="text-white/50 group-hover:text-white"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EyeOnIcon
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className="text-white/50 group-hover:text-white"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
28
src/app/settings/components/dataPath.tsx
Normal file
28
src/app/settings/components/dataPath.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { appConfigDir } from '@tauri-apps/api/path';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function DataPath() {
|
||||||
|
const [path, setPath] = useState<string>('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getPath() {
|
||||||
|
const dir = await appConfigDir();
|
||||||
|
setPath(dir);
|
||||||
|
}
|
||||||
|
getPath();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="inline-flex items-center justify-between px-5 py-4">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className="font-medium leading-none text-zinc-200">App data path</span>
|
||||||
|
<span className="text-sm leading-none text-white/50">
|
||||||
|
Where the local data is stored
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="inline-flex items-center gap-2">
|
||||||
|
<span className="font-medium text-zinc-300">{path}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
import { getVersion } from '@tauri-apps/plugin-app';
|
import { getVersion } from '@tauri-apps/plugin-app';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { RefreshIcon } from '@shared/icons';
|
|
||||||
|
|
||||||
export function VersionSetting() {
|
export function VersionSetting() {
|
||||||
const [version, setVersion] = useState<string>('');
|
const [version, setVersion] = useState<string>('');
|
||||||
|
|
||||||
@ -24,12 +22,6 @@ export function VersionSetting() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2">
|
||||||
<span className="font-medium text-zinc-300">{version}</span>
|
<span className="font-medium text-zinc-300">{version}</span>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-zinc-800 font-medium hover:bg-fuchsia-500"
|
|
||||||
>
|
|
||||||
<RefreshIcon className="h-4 w-4 text-white" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { AutoStartSetting } from '@app/settings/components/autoStart';
|
import { AutoStartSetting } from '@app/settings/components/autoStart';
|
||||||
import { CacheTimeSetting } from '@app/settings/components/cacheTime';
|
import { DataPath } from '@app/settings/components/dataPath';
|
||||||
import { VersionSetting } from '@app/settings/components/version';
|
import { VersionSetting } from '@app/settings/components/version';
|
||||||
|
|
||||||
export function GeneralSettingsScreen() {
|
export function GeneralSettingsScreen() {
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full px-3 pt-12">
|
<div className="h-full w-full px-3 pt-11">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h1 className="text-lg font-semibold text-white">General</h1>
|
<h1 className="text-xl font-semibold text-white">General</h1>
|
||||||
<div className="w-full rounded-xl bg-white/10 backdrop-blur-xl">
|
<div className="w-full rounded-xl bg-white/10 backdrop-blur-xl">
|
||||||
<div className="flex h-full w-full flex-col divide-y divide-white/5">
|
<div className="flex h-full w-full flex-col divide-y divide-white/5">
|
||||||
<AutoStartSetting />
|
<AutoStartSetting />
|
||||||
<CacheTimeSetting />
|
<DataPath />
|
||||||
<VersionSetting />
|
<VersionSetting />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,7 +42,7 @@ export function WidgetList({ params }: { params: Widget }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full shrink-0 grow-0 basis-[400px] overflow-hidden">
|
<div className="relative h-full shrink-0 grow-0 basis-[400px] overflow-hidden">
|
||||||
<TitleBar title="Add widget" />
|
<TitleBar id={params.id} title="Add widget" />
|
||||||
<div className="flex flex-col gap-8 px-3">
|
<div className="flex flex-col gap-8 px-3">
|
||||||
{DefaultWidgets.map((row: WidgetGroup) => renderItem(row))}
|
{DefaultWidgets.map((row: WidgetGroup) => renderItem(row))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,7 @@ export const NDKInstance = () => {
|
|||||||
// return all validate relays
|
// return all validate relays
|
||||||
return verifiedRelays;
|
return verifiedRelays;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
e.forEach((error) => console.error(error));
|
console.error('ndk instance error: ', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { NDKFilter, NDKKind } from '@nostr-dev-kit/ndk';
|
import { NDKFilter, NDKKind } from '@nostr-dev-kit/ndk';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
|
import { AccountMoreActions } from '@shared/accounts/more';
|
||||||
import { Image } from '@shared/image';
|
import { Image } from '@shared/image';
|
||||||
|
|
||||||
import { useNostr } from '@utils/hooks/useNostr';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
import { useProfile } from '@utils/hooks/useProfile';
|
import { useProfile } from '@utils/hooks/useProfile';
|
||||||
import { sendNativeNotification } from '@utils/notification';
|
import { sendNativeNotification } from '@utils/notification';
|
||||||
|
import { displayNpub } from '@utils/shortenKey';
|
||||||
|
|
||||||
export function ActiveAccount() {
|
export function ActiveAccount() {
|
||||||
const { db } = useStorage();
|
const { db } = useStorage();
|
||||||
@ -57,16 +58,23 @@ export function ActiveAccount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<div className="flex h-16 items-center justify-between border-l-2 border-transparent pb-2 pl-4 pr-2">
|
||||||
to={`/users/${db.account.pubkey}`}
|
<div className="flex items-center gap-2.5">
|
||||||
className="flex h-10 items-center gap-2.5 rounded-r-lg border-l-2 border-transparent pl-4 pr-2"
|
<Image
|
||||||
>
|
src={user?.picture || user?.image}
|
||||||
<Image
|
alt={db.account.npub}
|
||||||
src={user?.picture || user?.image}
|
className="h-10 w-10 shrink-0 rounded-lg object-cover"
|
||||||
alt={db.account.npub}
|
/>
|
||||||
className="h-7 w-7 shrink-0 rounded object-cover"
|
<div className="flex w-full flex-1 flex-col items-start gap-1.5">
|
||||||
/>
|
<p className="max-w-[10rem] truncate font-bold leading-none text-white">
|
||||||
<p className="text-white/80">Your profile</p>
|
{user?.name || user?.display_name || user?.nip05}
|
||||||
</Link>
|
</p>
|
||||||
|
<span className="max-w-[8rem] truncate text-sm leading-none text-white/50">
|
||||||
|
{displayNpub(db.account.pubkey, 16)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AccountMoreActions pubkey={db.account.pubkey} />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import { Image } from '@shared/image';
|
|
||||||
|
|
||||||
import { useProfile } from '@utils/hooks/useProfile';
|
|
||||||
|
|
||||||
export function InactiveAccount({ data }: { data: any }) {
|
|
||||||
const { user } = useProfile(data.npub);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative h-9 w-9 shrink-0">
|
|
||||||
<Image src={user?.image} alt={data.npub} className="h-9 w-9 rounded object-cover" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
55
src/shared/accounts/more.tsx
Normal file
55
src/shared/accounts/more.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { HorizontalDotsIcon } from '@shared/icons';
|
||||||
|
|
||||||
|
export function AccountMoreActions({ pubkey }: { pubkey: string }) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
||||||
|
<DropdownMenu.Trigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="group ml-auto inline-flex h-7 w-7 items-center justify-center rounded-md hover:bg-white/10"
|
||||||
|
>
|
||||||
|
<HorizontalDotsIcon className="h-5 w-5 text-white/80 group-hover:text-white" />
|
||||||
|
</button>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content className="flex w-[200px] flex-col overflow-hidden rounded-xl bg-white/10 p-2 backdrop-blur-3xl focus:outline-none">
|
||||||
|
<DropdownMenu.Item asChild>
|
||||||
|
<Link
|
||||||
|
to={`/users/${pubkey}`}
|
||||||
|
className="inline-flex h-10 items-center rounded-lg px-2 text-sm font-medium text-white hover:bg-white/10 focus:outline-none"
|
||||||
|
>
|
||||||
|
Profile
|
||||||
|
</Link>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item asChild>
|
||||||
|
<Link
|
||||||
|
to={`/settings/backup`}
|
||||||
|
className="inline-flex h-10 items-center rounded-lg px-2 text-sm font-medium text-white hover:bg-white/10 focus:outline-none"
|
||||||
|
>
|
||||||
|
Backup
|
||||||
|
</Link>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item asChild>
|
||||||
|
<Link
|
||||||
|
to={`/settings/`}
|
||||||
|
className="inline-flex h-10 items-center rounded-lg px-2 text-sm font-medium text-white hover:bg-white/10 focus:outline-none"
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</Link>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item asChild>
|
||||||
|
<button className="inline-flex h-10 items-center rounded-lg px-2 text-sm font-medium text-white hover:bg-white/10">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
);
|
||||||
|
}
|
@ -55,3 +55,4 @@ export * from './share';
|
|||||||
export * from './expand';
|
export * from './expand';
|
||||||
export * from './focus';
|
export * from './focus';
|
||||||
export * from './chevronUp';
|
export * from './chevronUp';
|
||||||
|
export * from './secure';
|
||||||
|
22
src/shared/icons/secure.tsx
Normal file
22
src/shared/icons/secure.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function SecureIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="square"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M20.25 6.22a1 1 0 00-.684-.948l-7.25-2.417a1 1 0 00-.632 0l-7.25 2.417a1 1 0 00-.684.949v5.691c0 4.973 4.25 7.338 8.25 9.496 4-2.158 8.25-4.523 8.25-9.496V6.221z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
@ -1,60 +1,58 @@
|
|||||||
import { NavLink, Outlet, ScrollRestoration } from 'react-router-dom';
|
import { Link, NavLink, Outlet, ScrollRestoration } from 'react-router-dom';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
import { ArrowLeftIcon, SecureIcon, SettingsIcon } from '@shared/icons';
|
||||||
|
|
||||||
export function SettingsLayout() {
|
export function SettingsLayout() {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen w-screen">
|
<div className="flex h-screen w-screen">
|
||||||
<div className="relative flex shrink-0 flex-row bg-black/80 pt-12">
|
<div className="relative flex h-full w-[232px] flex-col bg-black/80">
|
||||||
<div className="relative flex w-[232px] flex-col gap-3">
|
<div data-tauri-drag-region className="h-11 w-full shrink-0" />
|
||||||
<div className="scrollbar-hide flex flex-col gap-5 overflow-y-auto pb-20">
|
<div className="scrollbar-hide flex h-full flex-1 flex-col gap-2 overflow-y-auto pb-32">
|
||||||
<div className="flex flex-col gap-0.5 px-1.5">
|
<div className="inline-flex items-center gap-2 border-l-2 border-transparent pl-4">
|
||||||
<div className="px-2.5">
|
<Link
|
||||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-white/50">
|
to="/"
|
||||||
Settings
|
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-white/10"
|
||||||
</h3>
|
>
|
||||||
</div>
|
<ArrowLeftIcon className="h-4 w-4 text-white/50" />
|
||||||
<div className="flex flex-col">
|
</Link>
|
||||||
<NavLink
|
<h3 className="text-[11px] font-bold uppercase tracking-widest text-white/50">
|
||||||
to="/settings/general"
|
Settings
|
||||||
className={({ isActive }) =>
|
</h3>
|
||||||
twMerge(
|
</div>
|
||||||
'flex h-9 items-center gap-2.5 rounded-md px-2.5',
|
<div className="flex flex-col pr-2">
|
||||||
isActive
|
<NavLink
|
||||||
? 'bg-white/10 text-white backdrop-blur-xl'
|
to="/settings/"
|
||||||
: 'text-white/80'
|
className={({ isActive }) =>
|
||||||
)
|
twMerge(
|
||||||
}
|
'flex h-10 items-center gap-2.5 rounded-r-lg border-l-2 pl-4 pr-2',
|
||||||
>
|
isActive
|
||||||
<span className="font-medium">General</span>
|
? 'border-fuchsia-500 bg-white/5 text-white'
|
||||||
</NavLink>
|
: 'border-transparent text-white/80'
|
||||||
<NavLink
|
)
|
||||||
to="/settings/shortcuts"
|
}
|
||||||
className={({ isActive }) =>
|
>
|
||||||
twMerge(
|
<span className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded bg-white/10 backdrop-blur-xl">
|
||||||
'flex h-9 items-center gap-2.5 rounded-md px-2.5',
|
<SettingsIcon className="h-4 w-4 text-white" />
|
||||||
isActive
|
</span>
|
||||||
? 'bg-white/10 text-white backdrop-blur-xl'
|
<span className="font-medium">General</span>
|
||||||
: 'text-white/80'
|
</NavLink>
|
||||||
)
|
<NavLink
|
||||||
}
|
to="/settings/backup"
|
||||||
>
|
className={({ isActive }) =>
|
||||||
<span className="font-medium">Shortcuts</span>
|
twMerge(
|
||||||
</NavLink>
|
'flex h-10 items-center gap-2.5 rounded-r-lg border-l-2 pl-4 pr-2',
|
||||||
<NavLink
|
isActive
|
||||||
to="/settings/account"
|
? 'border-fuchsia-500 bg-white/5 text-white'
|
||||||
className={({ isActive }) =>
|
: 'border-transparent text-white/80'
|
||||||
twMerge(
|
)
|
||||||
'flex h-9 items-center gap-2.5 rounded-md px-2.5',
|
}
|
||||||
isActive
|
>
|
||||||
? 'bg-white/10 text-white backdrop-blur-xl'
|
<span className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded bg-white/10 backdrop-blur-xl">
|
||||||
: 'text-white/80'
|
<SecureIcon className="h-4 w-4 text-white" />
|
||||||
)
|
</span>
|
||||||
}
|
<span className="font-medium">Backup</span>
|
||||||
>
|
</NavLink>
|
||||||
<span className="font-medium">Account</span>
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ import { ChatsList } from '@app/chats/components/list';
|
|||||||
|
|
||||||
import { ActiveAccount } from '@shared/accounts/active';
|
import { ActiveAccount } from '@shared/accounts/active';
|
||||||
import { ComposerModal } from '@shared/composer';
|
import { ComposerModal } from '@shared/composer';
|
||||||
import { BellIcon, NavArrowDownIcon, SettingsIcon, SpaceIcon } from '@shared/icons';
|
import { BellIcon, NavArrowDownIcon, SpaceIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { useSidebar } from '@stores/sidebar';
|
import { useSidebar } from '@stores/sidebar';
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ export function Navigation() {
|
|||||||
const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]);
|
const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full w-[232px] bg-black/80">
|
<div className="relative flex h-full w-[232px] flex-col bg-black/80">
|
||||||
<div data-tauri-drag-region className="h-11 w-full" />
|
<div data-tauri-drag-region className="h-11 w-full shrink-0" />
|
||||||
<div className="scrollbar-hide flex h-full flex-col gap-6 overflow-y-auto pb-32">
|
<div className="scrollbar-hide flex h-full flex-1 flex-col gap-6 overflow-y-auto pb-32">
|
||||||
<div className="flex flex-col pr-2">
|
<div className="flex flex-col pr-2">
|
||||||
<ComposerModal />
|
<ComposerModal />
|
||||||
<NavLink
|
<NavLink
|
||||||
@ -53,24 +53,6 @@ export function Navigation() {
|
|||||||
</span>
|
</span>
|
||||||
Notifications
|
Notifications
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink
|
|
||||||
to="/settings"
|
|
||||||
preventScrollReset={true}
|
|
||||||
className={({ isActive }) =>
|
|
||||||
twMerge(
|
|
||||||
'flex h-10 items-center gap-2.5 rounded-r-lg border-l-2 pl-4 pr-2',
|
|
||||||
isActive
|
|
||||||
? 'border-fuchsia-500 bg-white/5 text-white'
|
|
||||||
: 'border-transparent text-white/80'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded bg-white/10 backdrop-blur-xl">
|
|
||||||
<SettingsIcon className="h-4 w-4 text-white" />
|
|
||||||
</span>
|
|
||||||
Settings
|
|
||||||
</NavLink>
|
|
||||||
<ActiveAccount />
|
|
||||||
</div>
|
</div>
|
||||||
<Collapsible.Root open={chats} onOpenChange={toggleChats}>
|
<Collapsible.Root open={chats} onOpenChange={toggleChats}>
|
||||||
<div className="flex flex-col gap-1 pr-2">
|
<div className="flex flex-col gap-1 pr-2">
|
||||||
@ -95,6 +77,9 @@ export function Navigation() {
|
|||||||
</div>
|
</div>
|
||||||
</Collapsible.Root>
|
</Collapsible.Root>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="shrink-0">
|
||||||
|
<ActiveAccount />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ export function MoreActions({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
</Tooltip.Portal>
|
</Tooltip.Portal>
|
||||||
</Tooltip.Root>
|
</Tooltip.Root>
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
<DropdownMenu.Content className="flex w-[200px] flex-col overflow-hidden rounded-md bg-white/10 p-2 backdrop-blur-3xl focus:outline-none">
|
<DropdownMenu.Content className="flex w-[200px] flex-col overflow-hidden rounded-xl bg-white/10 p-2 backdrop-blur-3xl focus:outline-none">
|
||||||
<DropdownMenu.Item asChild>
|
<DropdownMenu.Item asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
Loading…
Reference in New Issue
Block a user