wip: settings screen
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-hover-card": "^1.0.7",
|
"@radix-ui/react-hover-card": "^1.0.7",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
"@radix-ui/react-toolbar": "^1.0.4",
|
"@radix-ui/react-toolbar": "^1.0.4",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
"@tanstack/react-query": "^5.8.1",
|
"@tanstack/react-query": "^5.8.1",
|
||||||
|
|||||||
44
pnpm-lock.yaml
generated
44
pnpm-lock.yaml
generated
@@ -38,6 +38,9 @@ dependencies:
|
|||||||
'@radix-ui/react-popover':
|
'@radix-ui/react-popover':
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
version: 1.0.7(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-switch':
|
||||||
|
specifier: ^1.0.3
|
||||||
|
version: 1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@radix-ui/react-toolbar':
|
'@radix-ui/react-toolbar':
|
||||||
specifier: ^1.0.4
|
specifier: ^1.0.4
|
||||||
version: 1.0.4(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
version: 1.0.4(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@@ -1438,6 +1441,33 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.23.2
|
||||||
|
'@radix-ui/primitive': 1.0.1
|
||||||
|
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.37)(react@18.2.0)
|
||||||
|
'@radix-ui/react-context': 1.0.1(@types/react@18.2.37)(react@18.2.0)
|
||||||
|
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.37)(react@18.2.0)
|
||||||
|
'@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.37)(react@18.2.0)
|
||||||
|
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.37)(react@18.2.0)
|
||||||
|
'@types/react': 18.2.37
|
||||||
|
'@types/react-dom': 18.2.15
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
|
/@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==}
|
resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1605,6 +1635,20 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@radix-ui/react-use-previous@1.0.1(@types/react@18.2.37)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.23.2
|
||||||
|
'@types/react': 18.2.37
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-use-rect@1.0.1(@types/react@18.2.37)(react@18.2.0):
|
/@radix-ui/react-use-rect@1.0.1(@types/react@18.2.37)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
|
resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -2612,7 +2612,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lume"
|
name = "lume"
|
||||||
version = "2.0.1"
|
version = "2.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"keyring",
|
"keyring",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
import { getVersion } from '@tauri-apps/api/app';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export function AboutScreen() {
|
export function AboutScreen() {
|
||||||
return <div></div>;
|
const [version, setVersion] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function loadVersion() {
|
||||||
|
const appVersion = await getVersion();
|
||||||
|
setVersion(appVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadVersion();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto w-full max-w-lg">
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<img src="/icon.png" alt="Lume's logo" className="w-16 shrink-0" />
|
||||||
|
<div>
|
||||||
|
<h1 className="text-xl font-semibold">Lume</h1>
|
||||||
|
<p className="text-neutral-700 dark:text-neutral-300">Version {version}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,26 @@
|
|||||||
export function AdvancedSettingScreen() {
|
export function AdvancedSettingScreen() {
|
||||||
return <div></div>;
|
return (
|
||||||
|
<div className="mx-auto w-full max-w-lg">
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">Event Caches</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="h-9 w-max rounded-lg bg-blue-500 px-2.5 text-white hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">User Caches</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="h-9 w-max rounded-lg bg-blue-500 px-2.5 text-white hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,37 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
export function BackupSettingScreen() {
|
export function BackupSettingScreen() {
|
||||||
return <div></div>;
|
const { db } = useStorage();
|
||||||
|
const [privkey, setPrivkey] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function loadPrivkey() {
|
||||||
|
const key = await db.secureLoad(db.account.pubkey);
|
||||||
|
if (key) setPrivkey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPrivkey();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto w-full max-w-lg">
|
||||||
|
<div className="mb-2 text-sm font-semibold">Private key</div>
|
||||||
|
<div>
|
||||||
|
{!privkey ? (
|
||||||
|
<div className="inline-flex h-24 w-full items-center justify-center rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||||
|
You've stored private key on Lume
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<textarea
|
||||||
|
readOnly
|
||||||
|
className="relative h-36 w-full resize-none rounded-lg bg-neutral-200 px-3 py-1 text-neutral-900 !outline-none placeholder:text-neutral-600 dark:bg-neutral-800 dark:text-neutral-100 dark:placeholder:text-neutral-400"
|
||||||
|
>
|
||||||
|
{privkey}
|
||||||
|
</textarea>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,172 @@
|
|||||||
|
import * as Switch from '@radix-ui/react-switch';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
|
import { DarkIcon, LightIcon, SystemModeIcon } from '@shared/icons';
|
||||||
|
|
||||||
export function GeneralSettingScreen() {
|
export function GeneralSettingScreen() {
|
||||||
return <div></div>;
|
const { db } = useStorage();
|
||||||
|
const [settings, setSettings] = useState({
|
||||||
|
autolaunch: false,
|
||||||
|
outbox: false,
|
||||||
|
media: true,
|
||||||
|
hashtag: true,
|
||||||
|
notification: true,
|
||||||
|
appearance: 'system',
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function loadSettings() {
|
||||||
|
const data = await db.getAllSettings();
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (item.key === 'autolaunch')
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
autolaunch: item.value === '1' ? true : false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (item.key === 'outbox')
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
outbox: item.value === '1' ? true : false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (item.key === 'media')
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
media: item.value === '1' ? true : false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (item.key === 'hashtag')
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
hashtag: item.value === '1' ? true : false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (item.key === 'notification')
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
notification: item.value === '1' ? true : false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (item.key === 'appearance')
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
appearance: item.value,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto w-full max-w-lg">
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="flex items-center gap-8">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">Startup</div>
|
||||||
|
<div className="text-sm">Launch Lume at Login</div>
|
||||||
|
</div>
|
||||||
|
<Switch.Root
|
||||||
|
checked={settings.autolaunch}
|
||||||
|
className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
|
||||||
|
>
|
||||||
|
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||||
|
</Switch.Root>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="flex items-center gap-8">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">Gossip</div>
|
||||||
|
<div className="text-sm">Use Outbox model</div>
|
||||||
|
</div>
|
||||||
|
<Switch.Root
|
||||||
|
checked={settings.outbox}
|
||||||
|
className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
|
||||||
|
>
|
||||||
|
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||||
|
</Switch.Root>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="flex items-center gap-8">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">Media</div>
|
||||||
|
<div className="text-sm">Automatically load media</div>
|
||||||
|
</div>
|
||||||
|
<Switch.Root
|
||||||
|
checked={settings.media}
|
||||||
|
className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
|
||||||
|
>
|
||||||
|
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||||
|
</Switch.Root>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="flex items-center gap-8">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">Hashtag</div>
|
||||||
|
<div className="text-sm">Hide all hashtags in content</div>
|
||||||
|
</div>
|
||||||
|
<Switch.Root
|
||||||
|
checked={settings.hashtag}
|
||||||
|
className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
|
||||||
|
>
|
||||||
|
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||||
|
</Switch.Root>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="flex items-center gap-8">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||||
|
Notification
|
||||||
|
</div>
|
||||||
|
<div className="text-sm">Automatically send notification</div>
|
||||||
|
</div>
|
||||||
|
<Switch.Root
|
||||||
|
checked={settings.notification}
|
||||||
|
className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
|
||||||
|
>
|
||||||
|
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||||
|
</Switch.Root>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full items-start gap-8">
|
||||||
|
<div className="w-24 shrink-0 text-end text-sm font-semibold">Appearance</div>
|
||||||
|
<div className="flex flex-1 gap-6">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex flex-col items-center justify-center gap-0.5"
|
||||||
|
>
|
||||||
|
<div className="inline-flex h-11 w-11 items-center justify-center rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||||
|
<LightIcon className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<p className="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
||||||
|
Light
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex flex-col items-center justify-center gap-0.5"
|
||||||
|
>
|
||||||
|
<div className="inline-flex h-11 w-11 items-center justify-center rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||||
|
<DarkIcon className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<p className="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
||||||
|
Dark
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex flex-col items-center justify-center gap-0.5"
|
||||||
|
>
|
||||||
|
<div className="inline-flex h-11 w-11 items-center justify-center rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||||
|
<SystemModeIcon className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<p className="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
||||||
|
System
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -435,6 +435,14 @@ export class LumeStorage {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAllSettings() {
|
||||||
|
const results: { key: string; value: string }[] = await this.db.select(
|
||||||
|
'SELECT * FROM settings ORDER BY id DESC;'
|
||||||
|
);
|
||||||
|
if (results.length < 1) return null;
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
public async getSettingValue(key: string) {
|
public async getSettingValue(key: string) {
|
||||||
const results: { key: string; value: string }[] = await this.db.select(
|
const results: { key: string; value: string }[] = await this.db.select(
|
||||||
'SELECT * FROM settings WHERE key = $1 ORDER BY id DESC LIMIT 1;',
|
'SELECT * FROM settings WHERE key = $1 ORDER BY id DESC LIMIT 1;',
|
||||||
|
|||||||
24
src/shared/icons/advancedSettings.tsx
Normal file
24
src/shared/icons/advancedSettings.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function AdvancedSettingsIcon(
|
||||||
|
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="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M13.75 7h-10m10 0a3.25 3.25 0 116.5 0 3.25 3.25 0 11-6.5 0zm6.5 10h-8m0 0a3.25 3.25 0 11-6.5 0m6.5 0a3.25 3.25 0 10-6.5 0m0 0h-2"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
src/shared/icons/dark.tsx
Normal file
22
src/shared/icons/dark.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function DarkIcon(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="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M21.248 11.811a6.5 6.5 0 01-9.06-9.06 9.25 9.25 0 109.06 9.06z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -78,3 +78,9 @@ export * from './heading2';
|
|||||||
export * from './heading3';
|
export * from './heading3';
|
||||||
export * from './bold';
|
export * from './bold';
|
||||||
export * from './italic';
|
export * from './italic';
|
||||||
|
export * from './user';
|
||||||
|
export * from './advancedSettings';
|
||||||
|
export * from './info';
|
||||||
|
export * from './light';
|
||||||
|
export * from './dark';
|
||||||
|
export * from './system';
|
||||||
|
|||||||
32
src/shared/icons/info.tsx
Normal file
32
src/shared/icons/info.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function InfoIcon(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="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M10.75 11H12v5.25M21.25 12a9.25 9.25 0 11-18.5 0 9.25 9.25 0 0118.5 0z"
|
||||||
|
></path>
|
||||||
|
<rect
|
||||||
|
width="1.25"
|
||||||
|
height="1.25"
|
||||||
|
x="11.375"
|
||||||
|
y="7.375"
|
||||||
|
fill="currentColor"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="0.25"
|
||||||
|
rx="0.625"
|
||||||
|
></rect>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
src/shared/icons/light.tsx
Normal file
22
src/shared/icons/light.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function LightIcon(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="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M11.998 3.29V1.769M5.84 18.158l-1.077 1.078m7.235 2.997v-1.524m7.235-15.944l-1.077 1.077M20.707 12h1.523m-4.074 6.159l1.077 1.077M1.766 12h1.523m1.474-7.235L5.84 5.842m9.87 2.446a5.25 5.25 0 11-7.424 7.424 5.25 5.25 0 017.424-7.424z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
src/shared/icons/system.tsx
Normal file
22
src/shared/icons/system.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function SystemModeIcon(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="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M3.75 12.25V12a8.25 8.25 0 1116.5 0v.25m-18.5 4h20.5m-15.5 4h10.5"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
21
src/shared/icons/user.tsx
Normal file
21
src/shared/icons/user.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function UserIcon(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"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M5.857 18.916C7.171 16.996 9.332 15.75 12 15.75c2.668 0 4.83 1.247 6.143 3.166m-12.286 0A9.215 9.215 0 0012 21.25c2.358 0 4.51-.882 6.143-2.334m-12.286 0a9.25 9.25 0 1112.286 0M15.25 10a3.25 3.25 0 11-6.5 0 3.25 3.25 0 016.5 0z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
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 { WindowTitlebar } from 'tauri-controls';
|
import { WindowTitlebar } from 'tauri-controls';
|
||||||
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
import { SecureIcon, SettingsIcon } from '@shared/icons';
|
import {
|
||||||
|
AdvancedSettingsIcon,
|
||||||
|
ArrowLeftIcon,
|
||||||
|
InfoIcon,
|
||||||
|
SecureIcon,
|
||||||
|
SettingsIcon,
|
||||||
|
UserIcon,
|
||||||
|
} from '@shared/icons';
|
||||||
|
|
||||||
export function SettingsLayout() {
|
export function SettingsLayout() {
|
||||||
const { db } = useStorage();
|
const { db } = useStorage();
|
||||||
@@ -17,77 +24,88 @@ export function SettingsLayout() {
|
|||||||
<div data-tauri-drag-region className="h-9" />
|
<div data-tauri-drag-region className="h-9" />
|
||||||
)}
|
)}
|
||||||
<div className="flex h-full min-h-0 w-full flex-col gap-8 overflow-y-auto pb-10">
|
<div className="flex h-full min-h-0 w-full flex-col gap-8 overflow-y-auto pb-10">
|
||||||
<div className="flex h-20 w-full items-end justify-center gap-0.5 border-b border-neutral-200 pb-2 dark:border-neutral-800">
|
<div className="flex h-20 w-full items-center justify-between border-b border-neutral-200 px-2 pb-2 dark:border-neutral-900">
|
||||||
<NavLink
|
<div>
|
||||||
to="/settings/"
|
<Link
|
||||||
className={({ isActive }) =>
|
to="/"
|
||||||
twMerge(
|
className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-neutral-100 dark:bg-neutral-900"
|
||||||
'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
|
<ArrowLeftIcon className="h-5 w-5" />
|
||||||
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
|
</Link>
|
||||||
: ''
|
</div>
|
||||||
)
|
<div className="flex items-center gap-0.5">
|
||||||
}
|
<NavLink
|
||||||
>
|
to="/settings/"
|
||||||
<SettingsIcon className="h-6 w-6" />
|
className={({ isActive }) =>
|
||||||
<p className="text-sm font-medium">User</p>
|
twMerge(
|
||||||
</NavLink>
|
'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',
|
||||||
<NavLink
|
isActive
|
||||||
to="/settings/general"
|
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
|
||||||
className={({ isActive }) =>
|
: ''
|
||||||
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'
|
<UserIcon className="h-6 w-6" />
|
||||||
: ''
|
<p className="text-sm font-medium">User</p>
|
||||||
)
|
</NavLink>
|
||||||
}
|
<NavLink
|
||||||
>
|
to="/settings/general"
|
||||||
<SettingsIcon className="h-6 w-6" />
|
className={({ isActive }) =>
|
||||||
<p className="text-sm font-medium">General</p>
|
twMerge(
|
||||||
</NavLink>
|
'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',
|
||||||
<NavLink
|
isActive
|
||||||
to="/settings/backup"
|
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
|
||||||
className={({ isActive }) =>
|
: ''
|
||||||
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'
|
<SettingsIcon className="h-6 w-6" />
|
||||||
: ''
|
<p className="text-sm font-medium">General</p>
|
||||||
)
|
</NavLink>
|
||||||
}
|
<NavLink
|
||||||
>
|
to="/settings/backup"
|
||||||
<SecureIcon className="h-6 w-6" />
|
className={({ isActive }) =>
|
||||||
<p className="text-sm font-medium">Backup</p>
|
twMerge(
|
||||||
</NavLink>
|
'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',
|
||||||
<NavLink
|
isActive
|
||||||
to="/settings/advanced"
|
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
|
||||||
className={({ isActive }) =>
|
: ''
|
||||||
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'
|
<SecureIcon className="h-6 w-6" />
|
||||||
: ''
|
<p className="text-sm font-medium">Backup</p>
|
||||||
)
|
</NavLink>
|
||||||
}
|
<NavLink
|
||||||
>
|
to="/settings/advanced"
|
||||||
<SettingsIcon className="h-6 w-6" />
|
className={({ isActive }) =>
|
||||||
<p className="text-sm font-medium">Advanced</p>
|
twMerge(
|
||||||
</NavLink>
|
'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',
|
||||||
<NavLink
|
isActive
|
||||||
to="/settings/about"
|
? 'bg-neutral-100 text-blue-500 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800'
|
||||||
className={({ isActive }) =>
|
: ''
|
||||||
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'
|
<AdvancedSettingsIcon className="h-6 w-6" />
|
||||||
: ''
|
<p className="text-sm font-medium">Advanced</p>
|
||||||
)
|
</NavLink>
|
||||||
}
|
<NavLink
|
||||||
>
|
to="/settings/about"
|
||||||
<SettingsIcon className="h-6 w-6" />
|
className={({ isActive }) =>
|
||||||
<p className="text-sm font-medium">About</p>
|
twMerge(
|
||||||
</NavLink>
|
'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'
|
||||||
|
: ''
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<InfoIcon className="h-6 w-6" />
|
||||||
|
<p className="text-sm font-medium">About</p>
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
<div />
|
||||||
</div>
|
</div>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
<ScrollRestoration />
|
<ScrollRestoration />
|
||||||
|
|||||||
Reference in New Issue
Block a user