update settings page
This commit is contained in:
@@ -16,7 +16,6 @@ import { Root } from "@app/root";
|
||||
import { AccountSettingsScreen } from "@app/settings/account";
|
||||
import { GeneralSettingsScreen } from "@app/settings/general";
|
||||
import { ShortcutsSettingsScreen } from "@app/settings/shortcuts";
|
||||
import { UpdateSettingsScreen } from "@app/settings/update";
|
||||
import { SpaceScreen } from "@app/space";
|
||||
import { TrendingScreen } from "@app/trending";
|
||||
import { UserScreen } from "@app/user";
|
||||
@@ -88,7 +87,6 @@ const router = createBrowserRouter([
|
||||
{ path: "general", element: <GeneralSettingsScreen /> },
|
||||
{ path: "shortcuts", element: <ShortcutsSettingsScreen /> },
|
||||
{ path: "account", element: <AccountSettingsScreen /> },
|
||||
{ path: "update", element: <UpdateSettingsScreen /> },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -143,7 +143,8 @@ export function Root() {
|
||||
const chats = await fetchChats();
|
||||
// const channels = await fetchChannelMessages();
|
||||
if (chats) {
|
||||
await updateLastLogin(dateToUnix());
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
await updateLastLogin(now);
|
||||
navigate("/app/space", { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,84 @@
|
||||
import { EyeOffIcon, EyeOnIcon } from "@shared/icons";
|
||||
import { useAccount } from "@utils/hooks/useAccount";
|
||||
import { useState } from "react";
|
||||
|
||||
export function AccountSettingsScreen() {
|
||||
const { status, account } = useAccount();
|
||||
const [type, setType] = useState("password");
|
||||
|
||||
const showPrivateKey = () => {
|
||||
if (type === "password") {
|
||||
setType("text");
|
||||
} else {
|
||||
setType("password");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>Account</h1>
|
||||
<div className="w-full h-full px-3 pt-12">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-lg font-semibold text-zinc-100">Account</h1>
|
||||
<div className="">
|
||||
{status === "loading" ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-base font-semibold text-zinc-400">
|
||||
Public Key
|
||||
</label>
|
||||
<input
|
||||
readOnly
|
||||
value={account.pubkey}
|
||||
className="relative w-2/3 rounded-lg py-3 pl-3.5 pr-11 !outline-none placeholder:text-zinc-400 bg-zinc-800 text-zinc-100"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-base font-semibold text-zinc-400">
|
||||
Npub
|
||||
</label>
|
||||
<input
|
||||
readOnly
|
||||
value={account.npub}
|
||||
className="relative w-2/3 rounded-lg py-3 pl-3.5 pr-11 !outline-none placeholder:text-zinc-400 bg-zinc-800 text-zinc-100"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-base font-semibold text-zinc-400">
|
||||
Private Key
|
||||
</label>
|
||||
<div className="relative w-2/3">
|
||||
<input
|
||||
readOnly
|
||||
type={type}
|
||||
value={account.privkey}
|
||||
className="relative w-full rounded-lg py-3 pl-3.5 pr-11 !outline-none placeholder:text-zinc-400 bg-zinc-800 text-zinc-100"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => showPrivateKey()}
|
||||
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700"
|
||||
>
|
||||
{type === "password" ? (
|
||||
<EyeOffIcon
|
||||
width={20}
|
||||
height={20}
|
||||
className="text-zinc-500 group-hover:text-zinc-100"
|
||||
/>
|
||||
) : (
|
||||
<EyeOnIcon
|
||||
width={20}
|
||||
height={20}
|
||||
className="text-zinc-500 group-hover:text-zinc-100"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
61
src/app/settings/components/autoStart.tsx
Normal file
61
src/app/settings/components/autoStart.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Switch } from "@headlessui/react";
|
||||
import { getSetting, updateSetting } from "@libs/storage";
|
||||
import { useEffect, useState } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { disable, enable, isEnabled } from "tauri-plugin-autostart-api";
|
||||
|
||||
export function AutoStartSetting() {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
|
||||
const toggle = async () => {
|
||||
if (!enabled) {
|
||||
await enable();
|
||||
await updateSetting("auto_start", 1);
|
||||
console.log(`registered for autostart? ${await isEnabled()}`);
|
||||
} else {
|
||||
await disable();
|
||||
await updateSetting("auto_start", 0);
|
||||
}
|
||||
setEnabled(!enabled);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function getAppSetting() {
|
||||
const setting = await getSetting("auto_start");
|
||||
if (parseInt(setting) === 0) {
|
||||
setEnabled(false);
|
||||
} else {
|
||||
setEnabled(true);
|
||||
}
|
||||
}
|
||||
getAppSetting();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">
|
||||
Auto start
|
||||
</span>
|
||||
<span className="leading-none text-sm text-zinc-400">
|
||||
Auto start at login
|
||||
</span>
|
||||
</div>
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onChange={toggle}
|
||||
className={twMerge(
|
||||
"relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-fuchsia-500 focus:ring-offset-2",
|
||||
enabled ? "bg-fuchsia-500" : "bg-zinc-700",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={twMerge(
|
||||
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-zinc-900 shadow ring-0 transition duration-200 ease-in-out",
|
||||
enabled ? "translate-x-5" : "translate-x-0",
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
src/app/settings/components/cacheTime.tsx
Normal file
43
src/app/settings/components/cacheTime.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { getSetting, updateSetting } from "@libs/storage";
|
||||
import { CheckCircleIcon } from "@shared/icons";
|
||||
import { useState } from "react";
|
||||
|
||||
const setting = await getSetting("cache_time");
|
||||
const cacheTime = setting;
|
||||
|
||||
export function CacheTimeSetting() {
|
||||
const [time, setTime] = useState(cacheTime);
|
||||
|
||||
const update = async () => {
|
||||
await updateSetting("cache_time", time);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">
|
||||
Cache time
|
||||
</span>
|
||||
<span className="leading-none text-sm text-zinc-400">
|
||||
The length of time before inactive data gets removed from the cache
|
||||
</span>
|
||||
</div>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<input
|
||||
value={time}
|
||||
onChange={(e) => setTime(e.currentTarget.value)}
|
||||
autoCapitalize="none"
|
||||
autoCorrect="none"
|
||||
className="w-24 h-8 rounded-md px-2 bg-zinc-800 text-zinc-300 text-right font-medium focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => update()}
|
||||
className="w-8 h-8 inline-flex items-center justify-center font-medium bg-zinc-800 hover:bg-fuchsia-500 rounded-md"
|
||||
>
|
||||
<CheckCircleIcon className="w-4 h-4 text-zinc-100" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
26
src/app/settings/components/version.tsx
Normal file
26
src/app/settings/components/version.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { RefreshIcon } from "@shared/icons";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
|
||||
const appVersion = await getVersion();
|
||||
|
||||
export function VersionSetting() {
|
||||
return (
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">Version</span>
|
||||
<span className="leading-none text-sm text-zinc-400">
|
||||
You're using latest version
|
||||
</span>
|
||||
</div>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<span className="text-zinc-300 font-medium">{appVersion}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="w-8 h-8 inline-flex items-center justify-center font-medium bg-zinc-800 hover:bg-fuchsia-500 rounded-md"
|
||||
>
|
||||
<RefreshIcon className="w-4 h-4 text-zinc-100" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,20 @@
|
||||
import { AutoStartSetting } from "@app/settings/components/autoStart";
|
||||
import { CacheTimeSetting } from "@app/settings/components/cacheTime";
|
||||
import { VersionSetting } from "@app/settings/components/version";
|
||||
|
||||
export function GeneralSettingsScreen() {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>General</h1>
|
||||
<div className="w-full h-full px-3 pt-12">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-lg font-semibold text-zinc-100">General</h1>
|
||||
<div className="w-full bg-zinc-900 border-t border-zinc-800/50 rounded-xl">
|
||||
<div className="w-full h-full flex flex-col divide-y divide-zinc-800">
|
||||
<AutoStartSetting />
|
||||
<CacheTimeSetting />
|
||||
<VersionSetting />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,110 @@
|
||||
import { CommandIcon } from "@shared/icons";
|
||||
|
||||
export function ShortcutsSettingsScreen() {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>Shortcuts</h1>
|
||||
<div className="w-full h-full px-3 pt-12">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-lg font-semibold text-zinc-100">Shortcuts</h1>
|
||||
<div className="w-full bg-zinc-900 border-t border-zinc-800/50 rounded-xl">
|
||||
<div className="w-full h-full flex flex-col divide-y divide-zinc-800">
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">
|
||||
Open composer
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<CommandIcon
|
||||
width={12}
|
||||
height={12}
|
||||
className="text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<span className="text-zinc-500 text-sm leading-none">N</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">
|
||||
Add image block
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<CommandIcon
|
||||
width={12}
|
||||
height={12}
|
||||
className="text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<span className="text-zinc-500 text-sm leading-none">I</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">
|
||||
Add newsfeed block
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<CommandIcon
|
||||
width={12}
|
||||
height={12}
|
||||
className="text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<span className="text-zinc-500 text-sm leading-none">F</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">
|
||||
Open personal page
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<CommandIcon
|
||||
width={12}
|
||||
height={12}
|
||||
className="text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<span className="text-zinc-500 text-sm leading-none">P</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-5 py-4 inline-flex items-center justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="leading-none font-medium text-zinc-200">
|
||||
Open notification
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<CommandIcon
|
||||
width={12}
|
||||
height={12}
|
||||
className="text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
|
||||
<span className="text-zinc-500 text-sm leading-none">B</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export function UpdateSettingsScreen() {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>Update</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export async function prefetchEvents(
|
||||
});
|
||||
|
||||
relaySetSubscription.on("eose", () => {
|
||||
setTimeout(() => resolve(new Set(events.values())), 1200);
|
||||
setTimeout(() => resolve(new Set(events.values())), 3000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -325,13 +325,34 @@ export async function createChat(
|
||||
return sender_pubkey;
|
||||
}
|
||||
|
||||
// get setting
|
||||
export async function getSetting(key: string) {
|
||||
const db = await connect();
|
||||
const result = await db.select(
|
||||
`SELECT value FROM settings WHERE key = "${key}";`,
|
||||
);
|
||||
return result[0]?.value;
|
||||
}
|
||||
|
||||
// update setting
|
||||
export async function updateSetting(key: string, value: string | number) {
|
||||
const db = await connect();
|
||||
return await db.execute(
|
||||
`UPDATE settings SET value = "${value}" WHERE key = "${key}";`,
|
||||
);
|
||||
}
|
||||
|
||||
// get last login
|
||||
export async function getLastLogin() {
|
||||
const db = await connect();
|
||||
const result = await db.select(
|
||||
`SELECT value FROM settings WHERE key = "last_login";`,
|
||||
);
|
||||
return result[0]?.value;
|
||||
if (result[0]) {
|
||||
return parseInt(result[0].value);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// update last login
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import App from "./app";
|
||||
import { getSetting } from "@libs/storage";
|
||||
import { RelayProvider } from "@shared/relayProvider";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
const cacheTime = await getSetting("cache_time");
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
cacheTime: 1000 * 60 * 60 * 24,
|
||||
cacheTime: parseInt(cacheTime),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -8,7 +8,8 @@ export function VideoPreview({ urls }: { urls: string[] }) {
|
||||
key={url}
|
||||
url={url}
|
||||
width="100%"
|
||||
className="w-full h-auto border border-zinc-800/50 rounded-lg"
|
||||
height="auto"
|
||||
className="!h-auto object-fill rounded-lg overflow-hidden"
|
||||
controls={true}
|
||||
pip={true}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { initNDK } from "@libs/ndk";
|
||||
import { getSetting } from "@libs/storage";
|
||||
import NDK from "@nostr-dev-kit/ndk";
|
||||
import { FULL_RELAYS } from "@stores/constants";
|
||||
import { createContext } from "react";
|
||||
|
||||
export const RelayContext = createContext<NDK>(null);
|
||||
|
||||
const ndk = await initNDK(FULL_RELAYS);
|
||||
const relays = await getSetting("relays");
|
||||
const relaysArray = JSON.parse(relays);
|
||||
const ndk = await initNDK(relaysArray);
|
||||
|
||||
export function RelayProvider({ children }: { children: React.ReactNode }) {
|
||||
return <RelayContext.Provider value={ndk}>{children}</RelayContext.Provider>;
|
||||
|
||||
@@ -49,17 +49,6 @@ export function SettingsLayout() {
|
||||
>
|
||||
<span className="font-medium">Account</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/settings/update"
|
||||
className={({ isActive }) =>
|
||||
twMerge(
|
||||
"flex h-9 items-center gap-2.5 rounded-md px-2.5 text-zinc-200",
|
||||
isActive ? "bg-zinc-900/50" : "",
|
||||
)
|
||||
}
|
||||
>
|
||||
<span className="font-medium">Update</span>
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user