mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 03:03:31 +00:00
polish
This commit is contained in:
parent
8a92813211
commit
d35c64e28d
@ -16,6 +16,7 @@
|
||||
"@floating-ui/react": "^0.23.1",
|
||||
"@headlessui/react": "^1.7.15",
|
||||
"@nostr-dev-kit/ndk": "0.6.0",
|
||||
"@radix-ui/react-popover": "^1.0.6",
|
||||
"@radix-ui/react-tooltip": "^1.0.6",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@tanstack/react-virtual": "3.0.0-beta.54",
|
||||
|
173
pnpm-lock.yaml
173
pnpm-lock.yaml
@ -10,6 +10,9 @@ dependencies:
|
||||
'@nostr-dev-kit/ndk':
|
||||
specifier: 0.6.0
|
||||
version: 0.6.0(typescript@4.9.5)
|
||||
'@radix-ui/react-popover':
|
||||
specifier: ^1.0.6
|
||||
version: 1.0.6(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.0.6
|
||||
version: 1.0.6(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -714,6 +717,43 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.14)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.5
|
||||
'@types/react': 18.2.14
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==}
|
||||
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.22.5
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.14)(react@18.2.0)
|
||||
'@types/react': 18.2.14
|
||||
'@types/react-dom': 18.2.6
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-id@1.0.1(@types/react@18.2.14)(react@18.2.0):
|
||||
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
|
||||
peerDependencies:
|
||||
@ -729,6 +769,41 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popover@1.0.6(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA==}
|
||||
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.22.5
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.14)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.14)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.14)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.14)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.14)(react@18.2.0)
|
||||
'@types/react': 18.2.14
|
||||
'@types/react-dom': 18.2.6
|
||||
aria-hidden: 1.2.3
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-remove-scroll: 2.5.5(@types/react@18.2.14)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==}
|
||||
peerDependencies:
|
||||
@ -2158,6 +2233,10 @@ packages:
|
||||
resolution: {integrity: sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==}
|
||||
dev: false
|
||||
|
||||
/detect-node-es@1.1.0:
|
||||
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||
dev: false
|
||||
|
||||
/didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
dev: true
|
||||
@ -2827,6 +2906,11 @@ packages:
|
||||
has-symbols: 1.0.3
|
||||
dev: false
|
||||
|
||||
/get-nonce@1.0.1:
|
||||
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/get-stream@6.0.1:
|
||||
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
||||
engines: {node: '>=10'}
|
||||
@ -3118,6 +3202,12 @@ packages:
|
||||
side-channel: 1.0.4
|
||||
dev: false
|
||||
|
||||
/invariant@2.2.4:
|
||||
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
dev: false
|
||||
|
||||
/ip-regex@4.3.0:
|
||||
resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==}
|
||||
engines: {node: '>=8'}
|
||||
@ -4297,6 +4387,41 @@ packages:
|
||||
react-fast-compare: 3.2.2
|
||||
dev: false
|
||||
|
||||
/react-remove-scroll-bar@2.3.4(@types/react@18.2.14)(react@18.2.0):
|
||||
resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.2.14
|
||||
react: 18.2.0
|
||||
react-style-singleton: 2.2.1(@types/react@18.2.14)(react@18.2.0)
|
||||
tslib: 2.6.0
|
||||
dev: false
|
||||
|
||||
/react-remove-scroll@2.5.5(@types/react@18.2.14)(react@18.2.0):
|
||||
resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.2.14
|
||||
react: 18.2.0
|
||||
react-remove-scroll-bar: 2.3.4(@types/react@18.2.14)(react@18.2.0)
|
||||
react-style-singleton: 2.2.1(@types/react@18.2.14)(react@18.2.0)
|
||||
tslib: 2.6.0
|
||||
use-callback-ref: 1.3.0(@types/react@18.2.14)(react@18.2.0)
|
||||
use-sidecar: 1.1.2(@types/react@18.2.14)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-router-dom@6.14.0(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-YEwlApKwzMMMbGbhh+Q7MsloTldcwMgHxUY/1g0uA62+B1hZo2jsybCWIDCL8zvIDB1FA0pBKY9chHbZHt+2dQ==}
|
||||
engines: {node: '>=14'}
|
||||
@ -4325,6 +4450,23 @@ packages:
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: false
|
||||
|
||||
/react-style-singleton@2.2.1(@types/react@18.2.14)(react@18.2.0):
|
||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.2.14
|
||||
get-nonce: 1.0.1
|
||||
invariant: 2.2.4
|
||||
react: 18.2.0
|
||||
tslib: 2.6.0
|
||||
dev: false
|
||||
|
||||
/react-virtuoso@4.3.11(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-0YrCvQ5GsIKRcN34GxrzhSJGuMNI+hGxWci5cTVuPQ8QWTEsrKfCyqm7YNBMmV3pu7onG1YVUBo86CyCXdejXg==}
|
||||
engines: {node: '>=10'}
|
||||
@ -5117,6 +5259,37 @@ packages:
|
||||
tlds: 1.240.0
|
||||
dev: false
|
||||
|
||||
/use-callback-ref@1.3.0(@types/react@18.2.14)(react@18.2.0):
|
||||
resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.2.14
|
||||
react: 18.2.0
|
||||
tslib: 2.6.0
|
||||
dev: false
|
||||
|
||||
/use-sidecar@1.1.2(@types/react@18.2.14)(react@18.2.0):
|
||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.2.14
|
||||
detect-node-es: 1.1.0
|
||||
react: 18.2.0
|
||||
tslib: 2.6.0
|
||||
dev: false
|
||||
|
||||
/use-sync-external-store@1.2.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
|
||||
peerDependencies:
|
||||
|
19
src/app.tsx
19
src/app.tsx
@ -13,12 +13,17 @@ import { ChannelScreen } from "@app/channel";
|
||||
import { ChatScreen } from "@app/chat";
|
||||
import { ErrorScreen } from "@app/error";
|
||||
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";
|
||||
import { AppLayout } from "@shared/appLayout";
|
||||
import { AuthLayout } from "@shared/authLayout";
|
||||
import { Protected } from "@shared/protected";
|
||||
import { SettingsLayout } from "@shared/settingsLayout";
|
||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
@ -72,6 +77,20 @@ const router = createBrowserRouter([
|
||||
{ path: "channel/:id", element: <ChannelScreen /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
element: (
|
||||
<Protected>
|
||||
<SettingsLayout />
|
||||
</Protected>
|
||||
),
|
||||
children: [
|
||||
{ path: "general", element: <GeneralSettingsScreen /> },
|
||||
{ path: "shortcuts", element: <ShortcutsSettingsScreen /> },
|
||||
{ path: "account", element: <AccountSettingsScreen /> },
|
||||
{ path: "update", element: <UpdateSettingsScreen /> },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
export default function App() {
|
||||
|
@ -12,9 +12,7 @@ export function ChatsListItem({ data }: { data: any }) {
|
||||
return (
|
||||
<div className="inline-flex h-9 items-center gap-2.5 rounded-md px-2.5">
|
||||
<div className="relative h-6 w-6 shrink-0 animate-pulse rounded bg-zinc-800" />
|
||||
<div>
|
||||
<div className="h-2.5 w-2/3 animate-pulse truncate rounded bg-zinc-800 text-base font-medium" />
|
||||
</div>
|
||||
<div className="h-2.5 w-2/3 animate-pulse rounded bg-zinc-800" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -40,10 +38,10 @@ export function ChatsListItem({ data }: { data: any }) {
|
||||
</div>
|
||||
<div className="w-full inline-flex items-center justify-between">
|
||||
<div className="inline-flex items-baseline gap-1">
|
||||
<h5 className="max-w-[9rem] truncate font-medium text-zinc-200">
|
||||
<h5 className="max-w-[10rem] truncate font-medium text-zinc-200">
|
||||
{user?.nip05 ||
|
||||
user?.displayName ||
|
||||
user?.name ||
|
||||
user?.displayName ||
|
||||
shortenKey(data.sender_pubkey)}
|
||||
</h5>
|
||||
</div>
|
||||
|
7
src/app/settings/account.tsx
Normal file
7
src/app/settings/account.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export function AccountSettingsScreen() {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>Account</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
7
src/app/settings/general.tsx
Normal file
7
src/app/settings/general.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export function GeneralSettingsScreen() {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>General</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
7
src/app/settings/shortcuts.tsx
Normal file
7
src/app/settings/shortcuts.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export function ShortcutsSettingsScreen() {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>Shortcuts</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
7
src/app/settings/update.tsx
Normal file
7
src/app/settings/update.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export function UpdateSettingsScreen() {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<h1>Update</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,19 +1,32 @@
|
||||
import { getNotesByPubkey } from "@libs/storage";
|
||||
import { NDKFilter } from "@nostr-dev-kit/ndk";
|
||||
import { Note } from "@shared/notes/note";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { dateToUnix, getHourAgo } from "@utils/date";
|
||||
import { LumeEvent } from "@utils/types";
|
||||
import { useContext } from "react";
|
||||
|
||||
export function UserFeed({ pubkey }: { pubkey: string }) {
|
||||
const ndk = useContext(RelayContext);
|
||||
const { status, data } = useQuery(["user-feed", pubkey], async () => {
|
||||
return await getNotesByPubkey(pubkey);
|
||||
const now = new Date();
|
||||
const filter: NDKFilter = {
|
||||
kinds: [1],
|
||||
authors: [pubkey],
|
||||
since: dateToUnix(getHourAgo(48, now)),
|
||||
};
|
||||
const events = await ndk.fetchEvents(filter);
|
||||
return [...events];
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-[400px] px-2">
|
||||
<div className="w-full max-w-[400px] px-2 pb-10">
|
||||
{status === "loading" ? (
|
||||
<p>Loading...</p>
|
||||
<div className="px-3">
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
) : (
|
||||
data.map((note: LumeEvent) => <Note key={note.event_id} event={note} />)
|
||||
data.map((note: LumeEvent) => <Note key={note.id} event={note} />)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -145,7 +145,7 @@ export function UserScreen() {
|
||||
} font-medium inline-flex items-center gap-2 h-10 border-t`}
|
||||
>
|
||||
<ThreadsIcon className="w-4 h-4" />
|
||||
Posts
|
||||
Activities from 48 hours ago
|
||||
</button>
|
||||
)}
|
||||
</Tab>
|
||||
|
@ -400,7 +400,21 @@ export async function createBlock(kind: number, title: string, content: any) {
|
||||
);
|
||||
}
|
||||
|
||||
// remove block
|
||||
export async function removeBlock(id: string) {
|
||||
const db = await connect();
|
||||
return await db.execute(`DELETE FROM blocks WHERE id = "${id}";`);
|
||||
}
|
||||
|
||||
// logout
|
||||
export async function removeAll() {
|
||||
const db = await connect();
|
||||
await db.execute(`UPDATE settings SET value = "0" WHERE key = "last_login";`);
|
||||
await db.execute("DELETE FROM replies;");
|
||||
await db.execute("DELETE FROM notes;");
|
||||
await db.execute("DELETE FROM blacklist;");
|
||||
await db.execute("DELETE FROM blocks;");
|
||||
await db.execute("DELETE FROM chats;");
|
||||
await db.execute("DELETE FROM accounts;");
|
||||
return true;
|
||||
}
|
||||
|
@ -39,4 +39,6 @@ export * from "./cmd";
|
||||
export * from "./verticalDots";
|
||||
export * from "./signal";
|
||||
export * from "./unverified";
|
||||
export * from "./settings";
|
||||
export * from "./logout";
|
||||
// @endindex
|
||||
|
24
src/shared/icons/logout.tsx
Normal file
24
src/shared/icons/logout.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
export function LogoutIcon(
|
||||
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="M20.25 12H9m11.25 0l-4.5 4.5m4.5-4.5l-4.5-4.5m-4.5 12.75h-6.5a1 1 0 01-1-1V4.75a1 1 0 011-1h6.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
29
src/shared/icons/settings.tsx
Normal file
29
src/shared/icons/settings.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
export function SettingsIcon(
|
||||
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="M8.552 5.37l-1.793-.414a1 1 0 00-.932.267l-.604.604a1 1 0 00-.267.932l.414 1.793a1 1 0 01-.42 1.056l-1.755 1.17a1 1 0 00-.445.832v.78a1 1 0 00.445.832l1.755 1.17a1 1 0 01.42 1.056l-.414 1.793a1 1 0 00.267.932l.604.604a1 1 0 00.932.267l1.793-.414a1 1 0 011.056.42l1.17 1.755a1 1 0 00.832.445h.78a1 1 0 00.832-.445l1.17-1.755a1 1 0 011.056-.42l1.793.414a1 1 0 00.932-.267l.604-.604a1 1 0 00.267-.932l-.414-1.793a1 1 0 01.42-1.056l1.755-1.17a1 1 0 00.445-.832v-.78a1 1 0 00-.445-.832l-1.755-1.17a1 1 0 01-.42-1.056l.414-1.793a1 1 0 00-.267-.932l-.604-.604a1 1 0 00-.932-.267l-1.793.414a1 1 0 01-1.056-.42l-1.17-1.755a1 1 0 00-.832-.445h-.78a1 1 0 00-.832.445L9.608 4.95a1 1 0 01-1.056.42z"
|
||||
/>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
d="M14.75 12a2.75 2.75 0 11-5.5 0 2.75 2.75 0 015.5 0z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
123
src/shared/logout.tsx
Normal file
123
src/shared/logout.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { removeAll } from "@libs/storage";
|
||||
import { CancelIcon, LogoutIcon } from "@shared/icons";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { relaunch } from "@tauri-apps/api/process";
|
||||
import { Fragment, useState } from "react";
|
||||
|
||||
export function Logout() {
|
||||
const queryClient = useQueryClient();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
// reset database
|
||||
await removeAll();
|
||||
// reset react query
|
||||
queryClient.clear();
|
||||
// navigate
|
||||
await relaunch();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openModal()}
|
||||
aria-label="Logout"
|
||||
className="inline-flex items-center justify-center w-9 h-9 rounded-md border-t bg-zinc-800 border-zinc-700/50 transform active:translate-y-1"
|
||||
>
|
||||
<LogoutIcon className="w-4 h-4 text-zinc-400" />
|
||||
</button>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
|
||||
</Transition.Child>
|
||||
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col rounded-xl border-t border-zinc-800/50 bg-zinc-900">
|
||||
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-none text-zinc-100"
|
||||
>
|
||||
Are you sure!
|
||||
</Dialog.Title>
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900"
|
||||
>
|
||||
<CancelIcon
|
||||
width={20}
|
||||
height={20}
|
||||
className="text-zinc-300"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<Dialog.Description className="text-sm leading-tight text-zinc-400">
|
||||
<p className="mb-2">
|
||||
When logout, all local data will be wiped, and restart
|
||||
app then you need to start onboarding process again when
|
||||
you log in.
|
||||
</p>
|
||||
<p>
|
||||
In the next version, Lume will support multi account,
|
||||
then you can switch between all account s instead of
|
||||
logout
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-full w-full flex-col items-end justify-center overflow-y-auto px-5 py-2.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
className="inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => logout()}
|
||||
className="inline-flex h-9 items-center justify-center rounded-md bg-red-500 px-3 text-sm font-medium text-zinc-100 hover:bg-red-600"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,68 +1,30 @@
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { getActiveAccount } from "@libs/storage";
|
||||
import { ActiveAccount } from "@shared/accounts/active";
|
||||
import { VerticalDotsIcon } from "@shared/icons";
|
||||
import { RelayManager } from "@shared/relayManager";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
import { SettingsIcon } from "@shared/icons";
|
||||
import { Logout } from "@shared/logout";
|
||||
import { Notification } from "@shared/notification";
|
||||
import { useAccount } from "@utils/hooks/useAccount";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function LumeBar() {
|
||||
const { status, data: activeAccount } = useQuery(
|
||||
["activeAccount"],
|
||||
async () => {
|
||||
return await getActiveAccount();
|
||||
},
|
||||
);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const toggleMenu = () => {
|
||||
setOpen((isOpen) => !isOpen);
|
||||
};
|
||||
const { status, account } = useAccount();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 rounded-xl p-2 border-t border-zinc-800/50 bg-zinc-900/80 backdrop-blur-md">
|
||||
<div className="rounded-xl p-2 border-t border-zinc-800/50 bg-zinc-900/80 backdrop-blur-md">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{status === "loading" ? (
|
||||
<div className="group relative flex h-9 w-9 shrink animate-pulse items-center justify-center rounded-md bg-zinc-900" />
|
||||
) : (
|
||||
<ActiveAccount data={activeAccount} />
|
||||
)}
|
||||
<RelayManager />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleMenu()}
|
||||
className="inline-flex items-center justify-center w-5 h-5 rounded hover:bg-zinc-800"
|
||||
{status === "loading" ? (
|
||||
<div className="group relative flex h-9 w-9 shrink animate-pulse items-center justify-center rounded-md bg-zinc-900" />
|
||||
) : (
|
||||
<ActiveAccount data={account} />
|
||||
)}
|
||||
<Notification />
|
||||
<Link
|
||||
to="/settings/general"
|
||||
className="inline-flex items-center justify-center w-9 h-9 rounded-md border-t bg-zinc-800 border-zinc-700/50 transform active:translate-y-1"
|
||||
>
|
||||
<VerticalDotsIcon className="w-4 h-4 text-zinc-100" />
|
||||
</button>
|
||||
<SettingsIcon className="w-4 h-4 text-zinc-400" />
|
||||
</Link>
|
||||
<Logout />
|
||||
</div>
|
||||
<Transition
|
||||
show={open}
|
||||
enter="transition-transform ease-in-out duration-75"
|
||||
enterFrom="translate-y-16"
|
||||
enterTo="translate-y-0"
|
||||
leave="transition-transform ease-in-out duration-150"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-16"
|
||||
className="flex flex-col items-start justify-start gap-1 pt-1.5 border-t border-zinc-800 transform"
|
||||
>
|
||||
<Link
|
||||
to="/app/settings"
|
||||
className="w-full py-2 px-2 rounded hover:bg-zinc-800 text-zinc-100 text-start text-sm"
|
||||
>
|
||||
Settings
|
||||
</Link>
|
||||
<Link
|
||||
to="/app/logout"
|
||||
className="w-full py-2 px-2 rounded hover:bg-zinc-800 text-zinc-100 text-start text-sm"
|
||||
>
|
||||
Logout
|
||||
</Link>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -7,14 +7,10 @@ import { LumeBar } from "@shared/lumeBar";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function Navigation({ reverse = false }: { reverse?: boolean }) {
|
||||
export function Navigation() {
|
||||
return (
|
||||
<div
|
||||
className={`relative flex w-[232px] flex-col gap-3 ${
|
||||
reverse ? "border-l" : "border-r"
|
||||
} border-zinc-900`}
|
||||
>
|
||||
<AppHeader reverse={reverse} />
|
||||
<div className="relative flex w-[232px] flex-col gap-3 border-r border-zinc-900">
|
||||
<AppHeader />
|
||||
<div className="pb-20 flex flex-col gap-5 overflow-y-auto scrollbar-hide">
|
||||
<div className="inlin-lflex h-8 px-3.5">
|
||||
<Composer />
|
||||
@ -117,7 +113,7 @@ export function Navigation({ reverse = false }: { reverse?: boolean }) {
|
||||
)}
|
||||
</Disclosure>
|
||||
</div>
|
||||
<div className="absolute bottom-3 left-0 px-8 w-full">
|
||||
<div className="absolute bottom-3 left-0 px-10 w-full">
|
||||
<LumeBar />
|
||||
</div>
|
||||
</div>
|
||||
|
58
src/shared/notification.tsx
Normal file
58
src/shared/notification.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { BellIcon } from "@shared/icons";
|
||||
import { Fragment, useState } from "react";
|
||||
|
||||
export function Notification() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openModal()}
|
||||
aria-label="Notification"
|
||||
className="inline-flex items-center justify-center w-9 h-9 rounded-md border-t bg-zinc-800 border-zinc-700/50 transform active:translate-y-1"
|
||||
>
|
||||
<BellIcon className="w-4 h-4 text-zinc-400" />
|
||||
</button>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
|
||||
</Transition.Child>
|
||||
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col gap-2 rounded-lg border-t border-zinc-800/50 bg-zinc-900">
|
||||
<p>OK</p>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { SignalIcon } from "@shared/icons";
|
||||
|
||||
export function RelayManager() {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Relay manager"
|
||||
className="inline-flex items-center justify-center w-9 h-9 rounded-md border-t bg-zinc-800 border-zinc-700/50 transform active:translate-y-1"
|
||||
>
|
||||
<SignalIcon className="w-4 h-4 text-zinc-400" />
|
||||
</button>
|
||||
);
|
||||
}
|
74
src/shared/settingsLayout.tsx
Normal file
74
src/shared/settingsLayout.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import { AppHeader } from "@shared/appHeader";
|
||||
import { NavLink, Outlet, ScrollRestoration } from "react-router-dom";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function SettingsLayout() {
|
||||
return (
|
||||
<div className="flex w-screen h-screen">
|
||||
<div className="relative flex flex-row shrink-0">
|
||||
<div className="relative flex w-[232px] flex-col gap-3 border-r border-zinc-900">
|
||||
<AppHeader />
|
||||
<div className="pb-20 flex flex-col gap-5 overflow-y-auto scrollbar-hide">
|
||||
<div className="flex flex-col gap-0.5 px-1.5">
|
||||
<div className="px-2.5">
|
||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">
|
||||
Settings
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<NavLink
|
||||
to="/settings/general"
|
||||
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">General</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/settings/shortcuts"
|
||||
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">Shortcuts</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/settings/account"
|
||||
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">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>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full h-full">
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user