From a4aef25adb518582bbeec827b301a55207584bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E5=AE=AE=E8=93=AE?= <123083837+reyamir@users.noreply.github.com> Date: Fri, 3 May 2024 15:15:48 +0700 Subject: [PATCH] final design (#184) * feat: redesign * feat: update other columns to new design * chore: small fixes * fix: better manage external webview * feat: redesign note * feat: update ui * chore: update * chore: update * chore: polish ui * chore: update auth ui * feat: finalize note design * chore: small fixes * feat: add window management in rust * chore: format * feat: update ui for event screen * feat: update event screen * feat: final --- apps/desktop2/package.json | 118 +- apps/desktop2/postcss.config.cjs | 8 +- apps/desktop2/src/app.tsx | 79 +- apps/desktop2/src/components/accounts.tsx | 69 - .../src/components/avatarUploader.tsx | 65 +- apps/desktop2/src/components/balance.tsx | 64 +- apps/desktop2/src/components/col.tsx | 209 +- apps/desktop2/src/components/conversation.tsx | 55 + apps/desktop2/src/components/quote.tsx | 47 + apps/desktop2/src/components/repost.tsx | 156 +- apps/desktop2/src/components/text.tsx | 62 +- apps/desktop2/src/components/toolbar.tsx | 16 +- apps/desktop2/src/routes/$account.home.tsx | 294 +- apps/desktop2/src/routes/$account.tsx | 159 +- apps/desktop2/src/routes/__root.tsx | 66 +- apps/desktop2/src/routes/antenas.tsx | 119 - apps/desktop2/src/routes/auth.lazy.tsx | 16 +- apps/desktop2/src/routes/auth/new/backup.tsx | 342 +-- apps/desktop2/src/routes/auth/new/profile.tsx | 244 +- .../desktop2/src/routes/auth/privkey.lazy.tsx | 148 +- apps/desktop2/src/routes/auth/remote.lazy.tsx | 116 +- apps/desktop2/src/routes/auth/settings.tsx | 384 +-- apps/desktop2/src/routes/create-group.tsx | 217 +- .../src/routes/editor/-components/media.tsx | 148 +- .../src/routes/editor/-components/nsfw.tsx | 64 +- apps/desktop2/src/routes/editor/index.tsx | 734 +++-- .../src/routes/events/$eventId.lazy.tsx | 71 - apps/desktop2/src/routes/events/$eventId.tsx | 71 + .../src/routes/events/-components/reply.tsx | 73 +- .../routes/events/-components/replyList.tsx | 77 +- .../routes/events/-components/subReply.tsx | 47 +- apps/desktop2/src/routes/foryou.tsx | 262 +- apps/desktop2/src/routes/global.tsx | 244 +- apps/desktop2/src/routes/group.tsx | 257 +- apps/desktop2/src/routes/index.tsx | 216 +- apps/desktop2/src/routes/interests.tsx | 205 +- apps/desktop2/src/routes/landing/index.tsx | 149 +- apps/desktop2/src/routes/newsfeed.tsx | 279 +- apps/desktop2/src/routes/nwc.lazy.tsx | 98 +- apps/desktop2/src/routes/open.lazy.tsx | 79 +- apps/desktop2/src/routes/search.tsx | 251 +- apps/desktop2/src/routes/settings.tsx | 186 +- apps/desktop2/src/routes/settings/backup.tsx | 212 +- apps/desktop2/src/routes/settings/general.tsx | 278 +- apps/desktop2/src/routes/settings/user.tsx | 334 +-- .../desktop2/src/routes/settings/zap.lazy.tsx | 168 +- apps/desktop2/src/routes/store.community.tsx | 28 +- apps/desktop2/src/routes/store.official.tsx | 112 +- apps/desktop2/src/routes/store.tsx | 99 +- apps/desktop2/src/routes/trending.notes.tsx | 102 +- apps/desktop2/src/routes/trending.tsx | 109 +- apps/desktop2/src/routes/trending.users.tsx | 118 +- .../src/routes/users/$pubkey.lazy.tsx | 66 +- .../routes/users/-components/eventList.tsx | 128 +- apps/desktop2/src/routes/zap.$id.lazy.tsx | 209 +- apps/desktop2/tailwind.config.js | 16 +- apps/desktop2/tsconfig.json | 20 +- apps/web/astro.config.mjs | 6 +- apps/web/package.json | 8 +- apps/web/tsconfig.json | 4 +- biome.json | 3 + package.json | 2 +- packages/ark/package.json | 6 +- packages/ark/src/ark.ts | 215 +- packages/ark/src/hooks/useEvent.ts | 4 +- packages/ark/src/hooks/usePreview.ts | 2 +- packages/ark/src/hooks/useProfile.ts | 4 +- packages/icons/index.ts | 1 + packages/icons/package.json | 26 +- packages/icons/src/addMedia.tsx | 22 +- packages/icons/src/addWidget.tsx | 42 +- packages/icons/src/advancedSettings.tsx | 40 +- packages/icons/src/alby.tsx | 146 +- packages/icons/src/announcement.tsx | 34 +- packages/icons/src/arrowDown.tsx | 40 +- packages/icons/src/arrowLeft.tsx | 22 +- packages/icons/src/arrowRight.tsx | 22 +- packages/icons/src/arrowRightCircle.tsx | 40 +- packages/icons/src/arrowUp.tsx | 2 +- packages/icons/src/arrowUpSquare.tsx | 2 +- packages/icons/src/article.tsx | 26 +- packages/icons/src/bell.tsx | 24 +- packages/icons/src/bellFilled.tsx | 24 +- packages/icons/src/bold.tsx | 42 +- packages/icons/src/cancel.tsx | 20 +- packages/icons/src/cancelCircle.tsx | 20 +- packages/icons/src/chats.tsx | 2 +- packages/icons/src/check.tsx | 24 +- packages/icons/src/checkCircle.tsx | 24 +- packages/icons/src/chevronDown.tsx | 26 +- packages/icons/src/chevronRight.tsx | 40 +- packages/icons/src/chevronUp.tsx | 2 +- packages/icons/src/cmd.tsx | 40 +- packages/icons/src/community.tsx | 36 +- packages/icons/src/compose.tsx | 2 +- packages/icons/src/composeFilled.tsx | 24 +- packages/icons/src/copy.tsx | 42 +- packages/icons/src/dark.tsx | 42 +- packages/icons/src/dots.tsx | 44 +- packages/icons/src/download.tsx | 26 +- packages/icons/src/edit.tsx | 42 +- packages/icons/src/editInterest.tsx | 2 +- packages/icons/src/empty.tsx | 123 +- packages/icons/src/enter.tsx | 42 +- packages/icons/src/expand.tsx | 26 +- packages/icons/src/explore.tsx | 44 +- packages/icons/src/explore2.tsx | 40 +- packages/icons/src/eyeOff.tsx | 36 +- packages/icons/src/eyeOn.tsx | 36 +- packages/icons/src/feed.tsx | 42 +- packages/icons/src/file.tsx | 40 +- packages/icons/src/focus.tsx | 52 +- packages/icons/src/follow.tsx | 42 +- packages/icons/src/follows.tsx | 42 +- packages/icons/src/foryou.tsx | 2 +- packages/icons/src/global.tsx | 22 +- packages/icons/src/gossip.tsx | 38 +- packages/icons/src/groupFeeds.tsx | 26 +- packages/icons/src/handArrowDown.tsx | 50 +- packages/icons/src/hashtag.tsx | 2 +- packages/icons/src/heading1.tsx | 42 +- packages/icons/src/heading2.tsx | 42 +- packages/icons/src/heading3.tsx | 42 +- packages/icons/src/heartbeat.tsx | 42 +- packages/icons/src/hide.tsx | 36 +- packages/icons/src/home.tsx | 22 +- packages/icons/src/homeFilled.tsx | 16 +- packages/icons/src/horizontalDots.tsx | 30 +- packages/icons/src/image.tsx | 36 +- packages/icons/src/info.tsx | 62 +- packages/icons/src/infoCircle.tsx | 24 +- packages/icons/src/italic.tsx | 42 +- packages/icons/src/key.tsx | 22 +- packages/icons/src/laurel.tsx | 22 +- packages/icons/src/light.tsx | 42 +- packages/icons/src/like.tsx | 31 +- packages/icons/src/link.tsx | 22 +- packages/icons/src/local.tsx | 24 +- packages/icons/src/logout.tsx | 2 +- packages/icons/src/lume.tsx | 20 +- packages/icons/src/media.tsx | 48 +- packages/icons/src/mention.tsx | 42 +- packages/icons/src/mute.tsx | 48 +- packages/icons/src/navArrowDown.tsx | 2 +- packages/icons/src/newColumn.tsx | 2 +- packages/icons/src/nsfw.tsx | 22 +- packages/icons/src/plus.tsx | 20 +- packages/icons/src/plusCircle.tsx | 42 +- packages/icons/src/quote.tsx | 22 +- packages/icons/src/refresh.tsx | 30 +- packages/icons/src/remote.tsx | 22 +- packages/icons/src/reply.tsx | 20 +- packages/icons/src/replyMessage.tsx | 38 +- packages/icons/src/repost.tsx | 22 +- packages/icons/src/run.tsx | 34 +- packages/icons/src/search.tsx | 22 +- packages/icons/src/secure.tsx | 42 +- packages/icons/src/settings.tsx | 36 +- packages/icons/src/settingsFilled.tsx | 2 +- packages/icons/src/share.tsx | 2 +- packages/icons/src/signal.tsx | 42 +- packages/icons/src/space.tsx | 20 +- packages/icons/src/spaceFilled.tsx | 20 +- packages/icons/src/stars.tsx | 36 +- packages/icons/src/strangers.tsx | 42 +- packages/icons/src/system.tsx | 42 +- packages/icons/src/thread.tsx | 29 +- packages/icons/src/threads.tsx | 36 +- packages/icons/src/timeline.tsx | 34 +- packages/icons/src/trash.tsx | 16 +- packages/icons/src/trending.tsx | 42 +- packages/icons/src/unfollow.tsx | 42 +- packages/icons/src/unverified.tsx | 40 +- packages/icons/src/user.tsx | 24 +- packages/icons/src/userAdd.tsx | 34 +- packages/icons/src/userRemove.tsx | 34 +- packages/icons/src/verified.tsx | 15 +- packages/icons/src/verticalDots.tsx | 48 +- packages/icons/src/visit.tsx | 25 + packages/icons/src/world.tsx | 42 +- packages/icons/src/zap.tsx | 20 +- packages/icons/tsconfig.json | 12 +- packages/tailwindcss/package.json | 2 +- packages/tailwindcss/tailwind.config.js | 95 +- packages/tsconfig/package.json | 16 +- packages/ui/package.json | 16 +- packages/ui/src/box.tsx | 38 +- packages/ui/src/carousel.tsx | 84 + packages/ui/src/column/content.tsx | 21 - packages/ui/src/column/header.tsx | 96 - packages/ui/src/column/index.ts | 9 - packages/ui/src/column/root.tsx | 29 - packages/ui/src/container.tsx | 83 +- packages/ui/src/index.ts | 2 +- packages/ui/src/note/buttons/open.tsx | 31 + packages/ui/src/note/buttons/pin.tsx | 31 - packages/ui/src/note/buttons/reply.tsx | 56 +- packages/ui/src/note/buttons/repost.tsx | 166 +- packages/ui/src/note/buttons/zap.tsx | 61 +- packages/ui/src/note/child.tsx | 72 +- packages/ui/src/note/content.tsx | 223 +- packages/ui/src/note/contentLarge.tsx | 151 + packages/ui/src/note/index.ts | 6 +- packages/ui/src/note/mentions/hashtag.tsx | 7 +- packages/ui/src/note/mentions/invoice.tsx | 14 +- packages/ui/src/note/mentions/note.tsx | 129 +- packages/ui/src/note/mentions/user.tsx | 32 +- packages/ui/src/note/menu.tsx | 188 +- packages/ui/src/note/preview/image.tsx | 105 +- packages/ui/src/note/preview/images.tsx | 62 + packages/ui/src/note/preview/link.tsx | 152 +- packages/ui/src/note/preview/video.tsx | 24 +- packages/ui/src/note/preview/videos.tsx | 36 + .../ui/src/note/primitives/childReply.tsx | 2 +- packages/ui/src/note/primitives/reply.tsx | 2 +- packages/ui/src/note/primitives/repost.tsx | 206 +- packages/ui/src/note/primitives/skeleton.tsx | 42 +- packages/ui/src/note/primitives/text.tsx | 70 +- packages/ui/src/note/primitives/thread.tsx | 70 +- packages/ui/src/note/provider.tsx | 4 +- packages/ui/src/note/root.tsx | 20 +- packages/ui/src/note/thread.tsx | 68 +- packages/ui/src/note/user.tsx | 107 +- packages/ui/src/spinner.tsx | 78 +- packages/ui/src/user/about.tsx | 12 +- packages/ui/src/user/avatar.tsx | 52 +- packages/ui/src/user/followButton.tsx | 97 +- packages/ui/src/user/name.tsx | 26 +- packages/ui/src/user/nip05.tsx | 66 +- packages/ui/src/user/provider.tsx | 40 +- packages/ui/src/user/root.tsx | 2 +- packages/ui/src/user/time.tsx | 20 +- packages/utils/index.ts | 1 + packages/utils/package.json | 16 +- packages/utils/src/cn.ts | 2 +- packages/utils/src/editor.ts | 6 +- packages/utils/src/formater.ts | 12 +- packages/utils/src/hooks/useOpenGraph.ts | 4 +- packages/utils/src/nip01.ts | 6 +- packages/utils/src/parser.ts | 35 + pnpm-lock.yaml | 2583 +++++++---------- src-tauri/Cargo.lock | 289 +- src-tauri/capabilities/main.json | 135 +- src-tauri/gen/schemas/capabilities.json | 2 +- src-tauri/resources/system_columns.json | 4 +- src-tauri/src/commands/window.rs | 53 +- src-tauri/src/main.rs | 15 +- src-tauri/src/nostr/event.rs | 25 +- src-tauri/src/nostr/keys.rs | 16 + src-tauri/src/tray.rs | 1 + 250 files changed, 9360 insertions(+), 9235 deletions(-) delete mode 100644 apps/desktop2/src/components/accounts.tsx create mode 100644 apps/desktop2/src/components/conversation.tsx create mode 100644 apps/desktop2/src/components/quote.tsx delete mode 100644 apps/desktop2/src/routes/antenas.tsx delete mode 100644 apps/desktop2/src/routes/events/$eventId.lazy.tsx create mode 100644 apps/desktop2/src/routes/events/$eventId.tsx create mode 100644 packages/icons/src/visit.tsx create mode 100644 packages/ui/src/carousel.tsx delete mode 100644 packages/ui/src/column/content.tsx delete mode 100644 packages/ui/src/column/header.tsx delete mode 100644 packages/ui/src/column/index.ts delete mode 100644 packages/ui/src/column/root.tsx create mode 100644 packages/ui/src/note/buttons/open.tsx delete mode 100644 packages/ui/src/note/buttons/pin.tsx create mode 100644 packages/ui/src/note/contentLarge.tsx create mode 100644 packages/ui/src/note/preview/images.tsx create mode 100644 packages/ui/src/note/preview/videos.tsx create mode 100644 packages/utils/src/parser.ts diff --git a/apps/desktop2/package.json b/apps/desktop2/package.json index 391b924d..3ba4a585 100644 --- a/apps/desktop2/package.json +++ b/apps/desktop2/package.json @@ -1,61 +1,61 @@ { - "name": "@lume/desktop2", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "@lume/ark": "workspace:^", - "@lume/icons": "workspace:^", - "@lume/ui": "workspace:^", - "@lume/utils": "workspace:^", - "@radix-ui/react-checkbox": "^1.0.4", - "@radix-ui/react-collapsible": "^1.0.3", - "@radix-ui/react-dialog": "^1.0.5", - "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-popover": "^1.0.7", - "@radix-ui/react-switch": "^1.0.3", - "@radix-ui/react-tooltip": "^1.0.7", - "@tanstack/query-sync-storage-persister": "^5.31.0", - "@tanstack/react-query": "^5.31.0", - "@tanstack/react-query-persist-client": "^5.31.0", - "@tanstack/react-router": "^1.29.2", - "i18next": "^23.11.2", - "i18next-resources-to-backend": "^1.2.1", - "minidenticons": "^4.2.1", - "nanoid": "^5.0.7", - "nostr-tools": "^2.5.0", - "react": "^18.2.0", - "react-currency-input-field": "^3.8.0", - "react-dom": "^18.2.0", - "react-hook-form": "^7.51.3", - "react-hotkeys-hook": "^4.5.0", - "react-i18next": "^14.1.0", - "slate": "^0.102.0", - "slate-react": "^0.102.0", - "sonner": "^1.4.41", - "use-debounce": "^10.0.0", - "virtua": "^0.30.2" - }, - "devDependencies": { - "@lume/tailwindcss": "workspace:^", - "@lume/tsconfig": "workspace:^", - "@lume/types": "workspace:^", - "@tanstack/router-devtools": "^1.29.2", - "@tanstack/router-vite-plugin": "^1.30.0", - "@types/react": "^18.2.79", - "@types/react-dom": "^18.2.25", - "@vitejs/plugin-react-swc": "^3.6.0", - "autoprefixer": "^10.4.19", - "postcss": "^8.4.38", - "tailwindcss": "^3.4.3", - "typescript": "^5.4.5", - "vite": "^5.2.10", - "vite-plugin-top-level-await": "^1.4.1", - "vite-tsconfig-paths": "^4.3.2" - } + "name": "@lume/desktop2", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@lume/ark": "workspace:^", + "@lume/icons": "workspace:^", + "@lume/ui": "workspace:^", + "@lume/utils": "workspace:^", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/query-sync-storage-persister": "^5.32.0", + "@tanstack/react-query": "^5.32.0", + "@tanstack/react-query-persist-client": "^5.32.0", + "@tanstack/react-router": "1.29.2", + "i18next": "^23.11.3", + "i18next-resources-to-backend": "^1.2.1", + "minidenticons": "^4.2.1", + "nanoid": "^5.0.7", + "nostr-tools": "^2.5.1", + "react": "^18.3.1", + "react-currency-input-field": "^3.8.0", + "react-dom": "^18.3.1", + "react-hook-form": "^7.51.3", + "react-hotkeys-hook": "^4.5.0", + "react-i18next": "^14.1.1", + "slate": "^0.102.0", + "slate-react": "^0.102.0", + "sonner": "^1.4.41", + "use-debounce": "^10.0.0", + "virtua": "^0.30.2" + }, + "devDependencies": { + "@lume/tailwindcss": "workspace:^", + "@lume/tsconfig": "workspace:^", + "@lume/types": "workspace:^", + "@tanstack/router-devtools": "^1.31.3", + "@tanstack/router-vite-plugin": "^1.30.0", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react-swc": "^3.6.0", + "autoprefixer": "^10.4.19", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5.4.5", + "vite": "^5.2.10", + "vite-plugin-top-level-await": "^1.4.1", + "vite-tsconfig-paths": "^4.3.2" + } } diff --git a/apps/desktop2/postcss.config.cjs b/apps/desktop2/postcss.config.cjs index 12a703d9..e873f1a4 100644 --- a/apps/desktop2/postcss.config.cjs +++ b/apps/desktop2/postcss.config.cjs @@ -1,6 +1,6 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, }; diff --git a/apps/desktop2/src/app.tsx b/apps/desktop2/src/app.tsx index 71abfd06..cfbd86d1 100644 --- a/apps/desktop2/src/app.tsx +++ b/apps/desktop2/src/app.tsx @@ -1,69 +1,72 @@ +import { Ark } from "@lume/ark"; +import { CancelCircleIcon, CheckCircleIcon, InfoCircleIcon } from "@lume/icons"; +import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister"; import { QueryClient } from "@tanstack/react-query"; +import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client"; import { RouterProvider, createRouter } from "@tanstack/react-router"; +import { platform } from "@tauri-apps/plugin-os"; import React, { StrictMode } from "react"; import ReactDOM from "react-dom/client"; import { I18nextProvider } from "react-i18next"; +import { Toaster } from "sonner"; import "./app.css"; import i18n from "./locale"; -import { Toaster } from "sonner"; -import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client"; -import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister"; import { routeTree } from "./router.gen"; // auto generated file -import { CancelCircleIcon, CheckCircleIcon, InfoCircleIcon } from "@lume/icons"; -import { Ark } from "@lume/ark"; const ark = new Ark(); const queryClient = new QueryClient(); +const platformName = await platform(); const persister = createSyncStoragePersister({ - storage: window.localStorage, + storage: window.localStorage, }); // Set up a Router instance const router = createRouter({ - routeTree, - context: { - ark, - queryClient, - }, + routeTree, + context: { + ark, + queryClient, + platform: platformName, + }, }); // Register things for typesafety declare module "@tanstack/react-router" { - interface Register { - router: typeof router; - } + interface Register { + router: typeof router; + } } function App() { - return ; + return ; } // biome-ignore lint/style/noNonNullAssertion: idk const rootElement = document.getElementById("root")!; if (!rootElement.innerHTML) { - const root = ReactDOM.createRoot(rootElement); - root.render( - - - - , - info: , - error: , - }} - closeButton - theme="system" - /> - - - - , - ); + const root = ReactDOM.createRoot(rootElement); + root.render( + + + + , + info: , + error: , + }} + closeButton + theme="system" + /> + + + + , + ); } diff --git a/apps/desktop2/src/components/accounts.tsx b/apps/desktop2/src/components/accounts.tsx deleted file mode 100644 index 01919b59..00000000 --- a/apps/desktop2/src/components/accounts.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Account } from "@lume/types"; -import { User } from "@lume/ui"; -import { - useNavigate, - useParams, - useRouteContext, -} from "@tanstack/react-router"; -import { useEffect, useState } from "react"; - -export function Accounts() { - const { ark } = useRouteContext({ strict: false }); - const params = useParams({ strict: false }); - - const [accounts, setAccounts] = useState(null); - - useEffect(() => { - async function getAllAccounts() { - const data = await ark.get_all_accounts(); - if (data) setAccounts(data); - } - - getAllAccounts(); - }, []); - - return ( -
- {accounts - ? accounts.map((account) => - // @ts-ignore, useless - account.npub === params.account ? ( - - ) : ( - - ), - ) - : null} -
- ); -} - -function Inactive({ pubkey }: { pubkey: string }) { - const { ark } = useRouteContext({ strict: false }); - const navigate = useNavigate(); - - const changeAccount = async (npub: string) => { - const select = await ark.load_selected_account(npub); - if (select) navigate({ to: "/$account/home", params: { account: npub } }); - }; - - return ( - - ); -} - -function Active({ pubkey }: { pubkey: string }) { - return ( - - - - - - ); -} diff --git a/apps/desktop2/src/components/avatarUploader.tsx b/apps/desktop2/src/components/avatarUploader.tsx index 0ad39d7c..1851a2ed 100644 --- a/apps/desktop2/src/components/avatarUploader.tsx +++ b/apps/desktop2/src/components/avatarUploader.tsx @@ -1,42 +1,47 @@ import { Spinner } from "@lume/ui"; import { cn } from "@lume/utils"; import { useRouteContext } from "@tanstack/react-router"; -import { Dispatch, ReactNode, SetStateAction, useState } from "react"; +import { + type Dispatch, + type ReactNode, + type SetStateAction, + useState, +} from "react"; import { toast } from "sonner"; export function AvatarUploader({ - setPicture, - children, - className, + setPicture, + children, + className, }: { - setPicture: Dispatch>; - children: ReactNode; - className?: string; + setPicture: Dispatch>; + children: ReactNode; + className?: string; }) { - const { ark } = useRouteContext({ strict: false }); - const [loading, setLoading] = useState(false); + const { ark } = useRouteContext({ strict: false }); + const [loading, setLoading] = useState(false); - const uploadAvatar = async () => { - // start loading - setLoading(true); - try { - const image = await ark.upload(); - setPicture(image); - } catch (e) { - toast.error(String(e)); - } + const uploadAvatar = async () => { + // start loading + setLoading(true); + try { + const image = await ark.upload(); + setPicture(image); + } catch (e) { + toast.error(String(e)); + } - // stop loading - setLoading(false); - }; + // stop loading + setLoading(false); + }; - return ( - - ); + return ( + + ); } diff --git a/apps/desktop2/src/components/balance.tsx b/apps/desktop2/src/components/balance.tsx index f31adaba..139bc6ce 100644 --- a/apps/desktop2/src/components/balance.tsx +++ b/apps/desktop2/src/components/balance.tsx @@ -4,39 +4,39 @@ import { useRouteContext } from "@tanstack/react-router"; import { useEffect, useMemo, useState } from "react"; export function Balance({ account }: { account: string }) { - const { ark } = useRouteContext({ strict: false }); - const [balance, setBalance] = useState(0); - const value = useMemo(() => getBitcoinDisplayValues(balance), [balance]); + const { ark } = useRouteContext({ strict: false }); + const [balance, setBalance] = useState(0); + const value = useMemo(() => getBitcoinDisplayValues(balance), [balance]); - useEffect(() => { - async function getBalance() { - const val = await ark.get_balance(); - setBalance(val); - } + useEffect(() => { + async function getBalance() { + const val = await ark.get_balance(); + setBalance(val); + } - getBalance(); - }, []); + getBalance(); + }, []); - return ( -
-
-
-
- Your balance -
-
- ₿ {value.bitcoinFormatted} -
-
- - - - - -
-
- ); + return ( +
+
+
+
+ Your balance +
+
+ ₿ {value.bitcoinFormatted} +
+
+ + + + + +
+
+ ); } diff --git a/apps/desktop2/src/components/col.tsx b/apps/desktop2/src/components/col.tsx index ce131aea..c6e06b8a 100644 --- a/apps/desktop2/src/components/col.tsx +++ b/apps/desktop2/src/components/col.tsx @@ -1,83 +1,160 @@ -import { useEffect, useRef, useState } from "react"; +import { CancelIcon, CheckIcon } from "@lume/icons"; import type { LumeColumn } from "@lume/types"; +import { cn } from "@lume/utils"; import { invoke } from "@tauri-apps/api/core"; +import { getCurrent } from "@tauri-apps/api/webviewWindow"; +import { useEffect, useRef, useState } from "react"; export function Col({ - column, - account, - isScroll, - isResize, + column, + account, + isScroll, + isResize, }: { - column: LumeColumn; - account: string; - isScroll: boolean; - isResize: boolean; + column: LumeColumn; + account: string; + isScroll: boolean; + isResize: boolean; }) { - const container = useRef(null); - const [webview, setWebview] = useState(undefined); + const container = useRef(null); + const [webview, setWebview] = useState(undefined); - const repositionWebview = async () => { - if (webview && webview.length > 1) { - const newRect = container.current.getBoundingClientRect(); - await invoke("reposition_column", { - label: webview, - x: newRect.x, - y: newRect.y, - }); - } - }; + const repositionWebview = async () => { + if (webview && webview.length > 1) { + const newRect = container.current.getBoundingClientRect(); + await invoke("reposition_column", { + label: webview, + x: newRect.x, + y: newRect.y, + }); + } + }; - const resizeWebview = async () => { - if (webview && webview.length > 1) { - const newRect = container.current.getBoundingClientRect(); - await invoke("resize_column", { - label: webview, - width: newRect.width, - height: newRect.height, - }); - } - }; + const resizeWebview = async () => { + if (webview && webview.length > 1) { + const newRect = container.current.getBoundingClientRect(); + await invoke("resize_column", { + label: webview, + width: newRect.width, + height: newRect.height, + }); + } + }; - useEffect(() => { - resizeWebview(); - }, [isResize]); + useEffect(() => { + resizeWebview(); + }, [isResize]); - useEffect(() => { - if (isScroll) repositionWebview(); - }, [isScroll]); + useEffect(() => { + if (isScroll) repositionWebview(); + }, [isScroll]); - useEffect(() => { - (async () => { - if (webview && webview.length > 1) return; + useEffect(() => { + (async () => { + if (webview && webview.length > 1) return; - const rect = container.current.getBoundingClientRect(); - const windowLabel = `column-${column.label}`; - const url = - column.content + - `?account=${account}&label=${column.label}&name=${column.name}`; + const rect = container.current.getBoundingClientRect(); + const windowLabel = `column-${column.label}`; + const url = `${column.content}?account=${account}&label=${column.label}&name=${column.name}`; - // create new webview - const label: string = await invoke("create_column", { - label: windowLabel, - x: rect.x, - y: rect.y, - width: rect.width, - height: rect.height, - url, - }); + // create new webview + const label: string = await invoke("create_column", { + label: windowLabel, + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + url, + }); - setWebview(label); - })(); + setWebview(label); + })(); - // close webview when unmounted - return () => { - if (webview && webview.length > 1) { - invoke("close_column", { - label: webview, - }); - } - }; - }, [webview]); + // close webview when unmounted + return () => { + if (webview && webview.length > 1) { + invoke("close_column", { + label: webview, + }); + } + }; + }, [webview]); - return
; + return ( +
+
+ {column.label !== "open" ? ( +
+ ) : null} +
+
+
+ ); +} + +function Header({ label, name }: { label: string; name: string }) { + const [title, setTitle] = useState(name); + const [isChanged, setIsChanged] = useState(false); + + const saveNewTitle = async () => { + const mainWindow = getCurrent(); + await mainWindow.emit("columns", { type: "set_title", label, title }); + + // update search params + // @ts-ignore, hahaha + search.name = title; + + // reset state + setIsChanged(false); + }; + + const close = async () => { + const mainWindow = getCurrent(); + await mainWindow.emit("columns", { type: "remove", label }); + }; + + useEffect(() => { + if (title.length !== name.length) setIsChanged(true); + }, [title]); + + return ( +
+
+
+
+
setTitle(e.currentTarget.textContent)} + className="text-sm font-medium focus:outline-none" + > + {name} +
+ {isChanged ? ( + + ) : null} +
+
+ +
+ ); } diff --git a/apps/desktop2/src/components/conversation.tsx b/apps/desktop2/src/components/conversation.tsx new file mode 100644 index 00000000..212577e8 --- /dev/null +++ b/apps/desktop2/src/components/conversation.tsx @@ -0,0 +1,55 @@ +import { ThreadIcon } from "@lume/icons"; +import type { Event } from "@lume/types"; +import { Note } from "@lume/ui"; +import { cn } from "@lume/utils"; +import { useRouteContext } from "@tanstack/react-router"; + +export function Conversation({ + event, + className, +}: { + event: Event; + className?: string; +}) { + const { ark } = useRouteContext({ strict: false }); + const thread = ark.parse_event_thread({ + content: event.content, + tags: event.tags, + }); + + return ( + + +
+ {thread?.rootEventId ? ( + + ) : null} +
+
+ + Thread +
+
+
+ {thread?.replyEventId ? ( + + ) : null} +
+
+ +
+ +
+
+
+ +
+ + + ); +} diff --git a/apps/desktop2/src/components/quote.tsx b/apps/desktop2/src/components/quote.tsx new file mode 100644 index 00000000..10854dc5 --- /dev/null +++ b/apps/desktop2/src/components/quote.tsx @@ -0,0 +1,47 @@ +import { QuoteIcon } from "@lume/icons"; +import type { Event } from "@lume/types"; +import { Note } from "@lume/ui"; +import { cn } from "@lume/utils"; + +export function Quote({ + event, + className, +}: { + event: Event; + className?: string; +}) { + const quoteEventId = event.tags.find( + (tag) => tag[0] === "q" || tag[3] === "mention", + )?.[1]; + + return ( + + +
+ +
+
+ + Quote +
+
+
+
+
+ +
+ +
+
+
+ +
+ + + ); +} diff --git a/apps/desktop2/src/components/repost.tsx b/apps/desktop2/src/components/repost.tsx index a6bd28ca..455a134f 100644 --- a/apps/desktop2/src/components/repost.tsx +++ b/apps/desktop2/src/components/repost.tsx @@ -1,91 +1,83 @@ -import { RepostIcon } from "@lume/icons"; -import { Event } from "@lume/types"; +import type { Event } from "@lume/types"; +import { Note, Spinner, User } from "@lume/ui"; import { cn } from "@lume/utils"; import { useQuery } from "@tanstack/react-query"; -import { useTranslation } from "react-i18next"; -import { Note, Spinner, User } from "@lume/ui"; import { useRouteContext } from "@tanstack/react-router"; export function RepostNote({ - event, - className, + event, + className, }: { - event: Event; - className?: string; + event: Event; + className?: string; }) { - const { ark, settings } = useRouteContext({ strict: false }); - const { t } = useTranslation(); - const { - isLoading, - isError, - data: repostEvent, - } = useQuery({ - queryKey: ["repost", event.id], - queryFn: async () => { - try { - if (event.content.length > 50) { - const embed: Event = JSON.parse(event.content); - return embed; - } - const id = event.tags.find((el) => el[0] === "e")?.[1]; - if (id) return await ark.get_event(id); - } catch (e) { - throw new Error(e); - } - }, - refetchOnWindowFocus: false, - refetchOnMount: false, - }); + const { ark } = useRouteContext({ strict: false }); + const { + isLoading, + isError, + data: repostEvent, + } = useQuery({ + queryKey: ["repost", event.id], + queryFn: async () => { + try { + if (event.content.length > 50) { + const embed: Event = JSON.parse(event.content); + return embed; + } - return ( - - - -
- -
-
- -
- - {t("note.reposted")} -
-
-
-
- {isLoading ? ( - - ) : isError ? ( -
-

Event not found

-
- ) : ( - -
- -
-
-
- -
-
- - - - {settings.zap ? : null} -
- -
-
-
-
- - )} - - ); + const id = event.tags.find((el) => el[0] === "e")?.[1]; + const repostEvent = await ark.get_event(id); + + return repostEvent; + } catch (e) { + throw new Error(e); + } + }, + refetchOnWindowFocus: false, + refetchOnMount: false, + }); + + return ( + + + +
+ Reposted by +
+ +
+
+ {isLoading ? ( +
+ + Loading event... +
+ ) : isError || !repostEvent ? ( +
+ Event not found within your current relay set +
+ ) : ( + + +
+ + +
+ +
+ + + + +
+
+
+ )} +
+ ); } diff --git a/apps/desktop2/src/components/text.tsx b/apps/desktop2/src/components/text.tsx index 3c8b70c9..60032a3a 100644 --- a/apps/desktop2/src/components/text.tsx +++ b/apps/desktop2/src/components/text.tsx @@ -1,42 +1,34 @@ -import { Event } from "@lume/types"; +import type { Event } from "@lume/types"; import { Note } from "@lume/ui"; import { cn } from "@lume/utils"; -import { useRouteContext } from "@tanstack/react-router"; export function TextNote({ - event, - className, + event, + className, }: { - event: Event; - className?: string; + event: Event; + className?: string; }) { - const { settings } = useRouteContext({ strict: false }); - - return ( - - - -
-
-
- - -
-
- - - {settings.zap ? : null} -
- -
-
-
- - - ); + return ( + + +
+ + +
+ +
+ + + + +
+
+
+ ); } diff --git a/apps/desktop2/src/components/toolbar.tsx b/apps/desktop2/src/components/toolbar.tsx index 117d572c..d403407c 100644 --- a/apps/desktop2/src/components/toolbar.tsx +++ b/apps/desktop2/src/components/toolbar.tsx @@ -1,15 +1,15 @@ -import { ReactNode } from "@tanstack/react-router"; +import type { ReactNode } from "@tanstack/react-router"; import { useLayoutEffect, useState } from "react"; import { createPortal } from "react-dom"; export function Toolbar({ children }: { children: ReactNode }) { - const [domReady, setDomReady] = useState(false); + const [domReady, setDomReady] = useState(false); - useLayoutEffect(() => { - setDomReady(true); - }, []); + useLayoutEffect(() => { + setDomReady(true); + }, []); - return domReady - ? createPortal(children, document.getElementById("toolbar")) - : null; + return domReady + ? createPortal(children, document.getElementById("toolbar")) + : null; } diff --git a/apps/desktop2/src/routes/$account.home.tsx b/apps/desktop2/src/routes/$account.home.tsx index 5e424234..576b74e7 100644 --- a/apps/desktop2/src/routes/$account.home.tsx +++ b/apps/desktop2/src/routes/$account.home.tsx @@ -2,7 +2,6 @@ import { Col } from "@/components/col"; import { Toolbar } from "@/components/toolbar"; import { ArrowLeftIcon, ArrowRightIcon } from "@lume/icons"; import type { EventColumns, LumeColumn } from "@lume/types"; -import { Spinner } from "@lume/ui"; import { createFileRoute } from "@tanstack/react-router"; import { listen } from "@tauri-apps/api/event"; import { resolveResource } from "@tauri-apps/api/path"; @@ -14,180 +13,173 @@ import { useDebouncedCallback } from "use-debounce"; import { VList, type VListHandle } from "virtua"; export const Route = createFileRoute("/$account/home")({ - component: Screen, - pendingComponent: Pending, - beforeLoad: async ({ context }) => { - const ark = context.ark; - const resourcePath = await resolveResource("resources/system_columns.json"); - const systemColumns: LumeColumn[] = JSON.parse( - await readTextFile(resourcePath), - ); - const userColumns = await ark.get_columns(); + beforeLoad: async ({ context }) => { + const ark = context.ark; + const resourcePath = await resolveResource("resources/system_columns.json"); + const systemColumns: LumeColumn[] = JSON.parse( + await readTextFile(resourcePath), + ); + const userColumns = await ark.get_columns(); - return { - storedColumns: !userColumns.length ? systemColumns : userColumns, - }; - }, + return { + storedColumns: !userColumns.length ? systemColumns : userColumns, + }; + }, + component: Screen, }); function Screen() { - const vlistRef = useRef(null); + const vlistRef = useRef(null); - const { account } = Route.useParams(); - const { ark, storedColumns } = Route.useRouteContext(); + const { account } = Route.useParams(); + const { ark, storedColumns } = Route.useRouteContext(); - const [selectedIndex, setSelectedIndex] = useState(-1); - const [columns, setColumns] = useState(storedColumns); - const [isScroll, setIsScroll] = useState(false); - const [isResize, setIsResize] = useState(false); + const [selectedIndex, setSelectedIndex] = useState(-1); + const [columns, setColumns] = useState(storedColumns); + const [isScroll, setIsScroll] = useState(false); + const [isResize, setIsResize] = useState(false); - const goLeft = () => { - const prevIndex = Math.max(selectedIndex - 1, 0); - setSelectedIndex(prevIndex); - vlistRef.current.scrollToIndex(prevIndex, { - align: "center", - }); - }; + const goLeft = () => { + const prevIndex = Math.max(selectedIndex - 1, 0); + setSelectedIndex(prevIndex); + vlistRef.current.scrollToIndex(prevIndex, { + align: "center", + }); + }; - const goRight = () => { - const nextIndex = Math.min(selectedIndex + 1, columns.length - 1); - setSelectedIndex(nextIndex); - vlistRef.current.scrollToIndex(nextIndex, { - align: "center", - }); - }; + const goRight = () => { + const nextIndex = Math.min(selectedIndex + 1, columns.length - 1); + setSelectedIndex(nextIndex); + vlistRef.current.scrollToIndex(nextIndex, { + align: "center", + }); + }; - const add = useDebouncedCallback((column: LumeColumn) => { - // update col label - column.label = `${column.label}-${nanoid()}`; + const add = useDebouncedCallback((column: LumeColumn) => { + // update col label + column.label = `${column.label}-${nanoid()}`; - // create new cols - const cols = [...columns]; - const openColIndex = cols.findIndex((col) => col.label === "open"); - const newCols = [ - ...cols.slice(0, openColIndex), - column, - ...cols.slice(openColIndex), - ]; + // create new cols + const cols = [...columns]; + const openColIndex = cols.findIndex((col) => col.label === "open"); + const newCols = [ + ...cols.slice(0, openColIndex), + column, + ...cols.slice(openColIndex), + ]; - setColumns(newCols); - setSelectedIndex(cols.length - 1); + setColumns(newCols); + setSelectedIndex(newCols.length); + setIsScroll(true); - // scroll to the newest column - vlistRef.current.scrollToIndex(cols.length - 1, { - align: "end", - }); - }, 150); + // scroll to the newest column + vlistRef.current.scrollToIndex(newCols.length - 1, { + align: "end", + }); + }, 150); - const remove = useDebouncedCallback((label: string) => { - setColumns((state) => state.filter((t) => t.label !== label)); - setSelectedIndex(columns.length - 1); + const remove = useDebouncedCallback((label: string) => { + const newCols = columns.filter((t) => t.label !== label); - // scroll to the first column - vlistRef.current.scrollToIndex(0, { - align: "start", - }); - }, 150); + setColumns(newCols); + setSelectedIndex(newCols.length); + setIsScroll(true); - const updateName = useDebouncedCallback((label: string, title: string) => { - const currentColIndex = columns.findIndex((col) => col.label === label); + // scroll to the first column + vlistRef.current.scrollToIndex(newCols.length - 1, { + align: "start", + }); + }, 150); - const updatedCol = Object.assign({}, columns[currentColIndex]); - updatedCol.name = title; + const updateName = useDebouncedCallback((label: string, title: string) => { + const currentColIndex = columns.findIndex((col) => col.label === label); - const newCols = columns.slice(); - newCols[currentColIndex] = updatedCol; + const updatedCol = Object.assign({}, columns[currentColIndex]); + updatedCol.name = title; - setColumns(newCols); - }, 150); + const newCols = columns.slice(); + newCols[currentColIndex] = updatedCol; - const startResize = useDebouncedCallback( - () => setIsResize((prev) => !prev), - 150, - ); + setColumns(newCols); + }, 150); - useEffect(() => { - // save state - ark.set_columns(columns); - }, [columns]); + const startResize = useDebouncedCallback( + () => setIsResize((prev) => !prev), + 150, + ); - useEffect(() => { - let unlistenColEvent: Awaited> | undefined = - undefined; - let unlistenWindowResize: Awaited> | undefined = - undefined; + useEffect(() => { + // save state + ark.set_columns(columns); + }, [columns]); - (async () => { - if (unlistenColEvent && unlistenWindowResize) return; + useEffect(() => { + let unlistenColEvent: Awaited> | undefined = + undefined; + let unlistenWindowResize: Awaited> | undefined = + undefined; - unlistenColEvent = await listen("columns", (data) => { - if (data.payload.type === "add") add(data.payload.column); - if (data.payload.type === "remove") remove(data.payload.label); - if (data.payload.type === "set_title") - updateName(data.payload.label, data.payload.title); - }); + (async () => { + if (unlistenColEvent && unlistenWindowResize) return; - unlistenWindowResize = await getCurrent().listen("tauri://resize", () => { - startResize(); - }); - })(); + unlistenColEvent = await listen("columns", (data) => { + if (data.payload.type === "add") add(data.payload.column); + if (data.payload.type === "remove") remove(data.payload.label); + if (data.payload.type === "set_title") + updateName(data.payload.label, data.payload.title); + }); - return () => { - if (unlistenColEvent) unlistenColEvent(); - if (unlistenWindowResize) unlistenWindowResize(); - }; - }, []); + unlistenWindowResize = await getCurrent().listen("tauri://resize", () => { + startResize(); + }); + })(); - return ( -
- setIsScroll(true)} - onScrollEnd={() => setIsScroll(false)} - className="scrollbar-none h-full w-full overflow-x-auto focus:outline-none" - > - {columns.map((column) => ( - - ))} - - -
- - -
-
-
- ); -} - -function Pending() { - return ( -
- -
- ); + return () => { + if (unlistenColEvent) unlistenColEvent(); + if (unlistenWindowResize) unlistenWindowResize(); + }; + }, []); + + return ( +
+ setIsScroll(true)} + onScrollEnd={() => setIsScroll(false)} + className="scrollbar-none h-full w-full overflow-x-auto focus:outline-none" + > + {columns.map((column) => ( + + ))} + + +
+ + +
+
+
+ ); } diff --git a/apps/desktop2/src/routes/$account.tsx b/apps/desktop2/src/routes/$account.tsx index 8d38b74b..740810f6 100644 --- a/apps/desktop2/src/routes/$account.tsx +++ b/apps/desktop2/src/routes/$account.tsx @@ -1,62 +1,113 @@ import { ComposeFilledIcon, PlusIcon, SearchIcon } from "@lume/icons"; -import { Outlet, createFileRoute, useNavigate } from "@tanstack/react-router"; +import type { Account } from "@lume/types"; +import { User } from "@lume/ui"; import { cn } from "@lume/utils"; -import { Accounts } from "@/components/accounts"; -import { platform } from "@tauri-apps/plugin-os"; +import { Outlet, createFileRoute } from "@tanstack/react-router"; +import { useEffect, useState } from "react"; export const Route = createFileRoute("/$account")({ - component: App, - beforeLoad: async () => { - const platformName = await platform(); - return { platform: platformName }; - }, + component: Screen, }); -function App() { - const navigate = useNavigate(); - const { ark, platform } = Route.useRouteContext(); +function Screen() { + const navigate = Route.useNavigate(); + const { ark, platform } = Route.useRouteContext(); - return ( -
-
-
- - - -
-
- -
-
-
-
- -
-
- ); + return ( +
+
+
+ + + +
+
+ +
+
+
+
+ +
+
+ ); +} + +export function Accounts() { + const navigate = Route.useNavigate(); + const { ark } = Route.useRouteContext(); + const { account } = Route.useParams(); + + const [accounts, setAccounts] = useState([]); + + const changeAccount = async (npub: string) => { + if (npub === account) return; + const select = await ark.load_selected_account(npub); + if (select) + return navigate({ to: "/$account/home", params: { account: npub } }); + }; + + useEffect(() => { + async function getAllAccounts() { + const data = await ark.get_all_accounts(); + if (data) setAccounts(data); + } + + getAllAccounts(); + }, []); + + return ( +
+ {accounts.map((user) => ( + + ))} +
+ ); } diff --git a/apps/desktop2/src/routes/__root.tsx b/apps/desktop2/src/routes/__root.tsx index 3935c98a..5f173e0d 100644 --- a/apps/desktop2/src/routes/__root.tsx +++ b/apps/desktop2/src/routes/__root.tsx @@ -1,48 +1,44 @@ -import { Outlet, createRootRouteWithContext } from "@tanstack/react-router"; -import { type Ark } from "@lume/ark"; -import { type QueryClient } from "@tanstack/react-query"; -import { type Platform } from "@tauri-apps/plugin-os"; -import type { - Account, - Contact, - Interests, - Metadata, - Settings, -} from "@lume/types"; +import type { Ark } from "@lume/ark"; +import type { Account, Interests, Metadata, Settings } from "@lume/types"; import { Spinner } from "@lume/ui"; -import { type Descendant } from "slate"; +import type { QueryClient } from "@tanstack/react-query"; +import { Outlet, createRootRouteWithContext } from "@tanstack/react-router"; +import type { Platform } from "@tauri-apps/plugin-os"; +import type { Descendant } from "slate"; type EditorElement = { - type: string; - children: Descendant[]; - eventId?: string; + type: string; + children: Descendant[]; + eventId?: string; }; interface RouterContext { - ark: Ark; - queryClient: QueryClient; - platform?: Platform; - locale?: string; - settings?: Settings; - interests?: Interests; - accounts?: Account[]; - initialValue?: EditorElement[]; - profile?: Metadata; - contacts?: Contact[]; + // System + ark: Ark; + queryClient: QueryClient; + // App info + platform?: Platform; + locale?: string; + // Settings + settings?: Settings; + interests?: Interests; + // Profile + accounts?: Account[]; + profile?: Metadata; + // Editor + initialValue?: EditorElement[]; } export const Route = createRootRouteWithContext()({ - component: () => , - pendingComponent: Pending, - wrapInSuspense: true, + component: () => , + pendingComponent: Pending, + wrapInSuspense: true, }); function Pending() { - return ( -
- -
- ); + return ( +
+ +
+ ); } diff --git a/apps/desktop2/src/routes/antenas.tsx b/apps/desktop2/src/routes/antenas.tsx deleted file mode 100644 index c6318c43..00000000 --- a/apps/desktop2/src/routes/antenas.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { RepostNote } from "@/components/repost"; -import { TextNote } from "@/components/text"; -import { ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons"; -import { ColumnRouteSearch, Event, Kind } from "@lume/types"; -import { Column, Spinner } from "@lume/ui"; -import { useInfiniteQuery } from "@tanstack/react-query"; -import { Link, createFileRoute } from "@tanstack/react-router"; -import { Virtualizer } from "virtua"; - -export const Route = createFileRoute("/antenas")({ - validateSearch: (search: Record): ColumnRouteSearch => { - return { - account: search.account, - label: search.label, - name: search.name, - }; - }, - component: Screen, -}); - -export function Screen() { - const { label, name, account } = Route.useSearch(); - const { ark } = Route.useRouteContext(); - const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = - useInfiniteQuery({ - queryKey: [name, account], - initialPageParam: 0, - queryFn: async ({ pageParam }: { pageParam: number }) => { - const events = await ark.get_events(20, pageParam); - return events; - }, - getNextPageParam: (lastPage) => { - const lastEvent = lastPage?.at(-1); - return lastEvent ? lastEvent.created_at - 1 : null; - }, - select: (data) => data?.pages.flatMap((page) => page), - refetchOnWindowFocus: false, - }); - - const renderItem = (event: Event) => { - if (!event) return; - switch (event.kind) { - case Kind.Repost: - return ; - default: - return ; - } - }; - - return ( - - - - {isLoading ? ( -
- -
- ) : !data.length ? ( - - ) : ( - - {data.map((item) => renderItem(item))} - - )} -
- {hasNextPage ? ( - - ) : null} -
-
-
- ); -} - -function Empty() { - return ( -
-
-
-
-
-

Your newsfeed is empty

-

- Here are few suggestions to get started. -

-
-
- - - Show trending notes - - - - Discover trending users - -
-
- ); -} diff --git a/apps/desktop2/src/routes/auth.lazy.tsx b/apps/desktop2/src/routes/auth.lazy.tsx index d7a0cd4e..0b2c36c6 100644 --- a/apps/desktop2/src/routes/auth.lazy.tsx +++ b/apps/desktop2/src/routes/auth.lazy.tsx @@ -2,15 +2,15 @@ import { Box, Container } from "@lume/ui"; import { Outlet, createLazyFileRoute } from "@tanstack/react-router"; export const Route = createLazyFileRoute("/auth")({ - component: Screen, + component: Screen, }); function Screen() { - return ( - - - - - - ); + return ( + + + + + + ); } diff --git a/apps/desktop2/src/routes/auth/new/backup.tsx b/apps/desktop2/src/routes/auth/new/backup.tsx index 293c6105..50c3545e 100644 --- a/apps/desktop2/src/routes/auth/new/backup.tsx +++ b/apps/desktop2/src/routes/auth/new/backup.tsx @@ -1,191 +1,191 @@ +import { CheckIcon } from "@lume/icons"; +import type { AppRouteSearch } from "@lume/types"; import { displayNsec } from "@lume/utils"; +import * as Checkbox from "@radix-ui/react-checkbox"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { invoke } from "@tauri-apps/api/core"; import { writeText } from "@tauri-apps/plugin-clipboard-manager"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; -import * as Checkbox from "@radix-ui/react-checkbox"; -import { CheckIcon } from "@lume/icons"; -import { AppRouteSearch } from "@lume/types"; export const Route = createFileRoute("/auth/new/backup")({ - validateSearch: (search: Record): AppRouteSearch => { - return { - account: search.account, - }; - }, - component: Screen, + validateSearch: (search: Record): AppRouteSearch => { + return { + account: search.account, + }; + }, + component: Screen, }); function Screen() { - const { account } = Route.useSearch(); - const { t } = useTranslation(); + const { account } = Route.useSearch(); + const { t } = useTranslation(); - const [key, setKey] = useState(null); - const [passphase, setPassphase] = useState(""); - const [copied, setCopied] = useState(false); - const [confirm, setConfirm] = useState({ c1: false, c2: false, c3: false }); + const [key, setKey] = useState(null); + const [passphase, setPassphase] = useState(""); + const [copied, setCopied] = useState(false); + const [confirm, setConfirm] = useState({ c1: false, c2: false, c3: false }); - const navigate = useNavigate(); + const navigate = useNavigate(); - const submit = async () => { - try { - if (key) { - if (!confirm.c1 || !confirm.c2 || !confirm.c3) { - return toast.warning("You need to confirm before continue"); - } else { - return navigate({ - to: "/auth/settings", - search: { account }, - }); - } - } + const submit = async () => { + try { + if (key) { + if (!confirm.c1 || !confirm.c2 || !confirm.c3) { + return toast.warning("You need to confirm before continue"); + } - const encrypted: string = await invoke("get_encrypted_key", { - npub: account, - password: passphase, - }); + return navigate({ + to: "/auth/settings", + search: { account }, + }); + } - setKey(encrypted); - } catch (e) { - toast.error(String(e)); - } - }; + const encrypted: string = await invoke("get_encrypted_key", { + npub: account, + password: passphase, + }); - const copyKey = async () => { - try { - await writeText(key); - setCopied(true); - } catch (e) { - toast.error(e); - } - }; + setKey(encrypted); + } catch (e) { + toast.error(String(e)); + } + }; - return ( -
-
-

Backup your sign in keys

-

- It's use for login to Lume or other Nostr clients. You will lost - access to your account if you lose this key. -

-
-
-
- -
- setPassphase(e.target.value)} - className="h-11 w-full resize-none rounded-lg border-transparent bg-neutral-100 placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-100 dark:bg-neutral-900 dark:focus:ring-blue-900" - /> -
-
- {key ? ( - <> -
- -
- - -
-
-
-
Before you continue:
-
-
- - setConfirm((state) => ({ ...state, c1: !state.c1 })) - } - className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-neutral-900" - id="confirm1" - > - - - - - -
-
- - setConfirm((state) => ({ ...state, c2: !state.c2 })) - } - className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-neutral-900" - id="confirm2" - > - - - - - -
-
- - setConfirm((state) => ({ ...state, c3: !state.c3 })) - } - className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-neutral-900" - id="confirm3" - > - - - - - -
-
-
- - ) : null} -
- -
-
-
- ); + const copyKey = async () => { + try { + await writeText(key); + setCopied(true); + } catch (e) { + toast.error(e); + } + }; + + return ( +
+
+

Backup your sign in keys

+

+ It's use for login to Lume or other Nostr clients. You will lost + access to your account if you lose this key. +

+
+
+
+ +
+ setPassphase(e.target.value)} + className="w-full h-11 rounded-lg border-transparent bg-neutral-100 px-3 placeholder:text-neutral-600 focus:border-blue-500 focus:ring-0 dark:bg-white/10 dark:placeholder:text-neutral-400" + /> +
+
+ {key ? ( + <> +
+ +
+ + +
+
+
+
Before you continue:
+
+
+ + setConfirm((state) => ({ ...state, c1: !state.c1 })) + } + className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-white/10 dark:hover:bg-white/20" + id="confirm1" + > + + + + + +
+
+ + setConfirm((state) => ({ ...state, c2: !state.c2 })) + } + className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-white/10 dark:hover:bg-white/20" + id="confirm2" + > + + + + + +
+
+ + setConfirm((state) => ({ ...state, c3: !state.c3 })) + } + className="flex size-6 appearance-none items-center justify-center rounded-md bg-neutral-100 outline-none dark:bg-white/10 dark:hover:bg-white/20" + id="confirm3" + > + + + + + +
+
+
+ + ) : null} +
+ +
+
+
+ ); } diff --git a/apps/desktop2/src/routes/auth/new/profile.tsx b/apps/desktop2/src/routes/auth/new/profile.tsx index 553b0f42..d026cee1 100644 --- a/apps/desktop2/src/routes/auth/new/profile.tsx +++ b/apps/desktop2/src/routes/auth/new/profile.tsx @@ -1,6 +1,6 @@ import { AvatarUploader } from "@/components/avatarUploader"; import { PlusIcon } from "@lume/icons"; -import { Metadata } from "@lume/types"; +import type { Metadata } from "@lume/types"; import { Spinner } from "@lume/ui"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; @@ -9,135 +9,135 @@ import { useTranslation } from "react-i18next"; import { toast } from "sonner"; export const Route = createFileRoute("/auth/new/profile")({ - component: Screen, - loader: ({ context }) => { - return context.ark.create_keys(); - }, + component: Screen, + loader: ({ context }) => { + return context.ark.create_keys(); + }, }); function Screen() { - const keys = Route.useLoaderData(); - const navigate = useNavigate(); + const keys = Route.useLoaderData(); + const navigate = useNavigate(); - const { t } = useTranslation(); - const { ark } = Route.useRouteContext(); - const { register, handleSubmit } = useForm(); + const { t } = useTranslation(); + const { ark } = Route.useRouteContext(); + const { register, handleSubmit } = useForm(); - const [picture, setPicture] = useState(""); - const [loading, setLoading] = useState(false); + const [picture, setPicture] = useState(""); + const [loading, setLoading] = useState(false); - const onSubmit = async (data: { - name: string; - about: string; - website: string; - }) => { - setLoading(true); + const onSubmit = async (data: { + name: string; + about: string; + website: string; + }) => { + setLoading(true); - try { - // Save account keys - const save = await ark.save_account(keys.nsec); + try { + // Save account keys + const save = await ark.save_account(keys.nsec); - // Then create profile - if (save) { - const profile: Metadata = { ...data, picture }; - const eventId = await ark.create_profile(profile); + // Then create profile + if (save) { + const profile: Metadata = { ...data, picture }; + const eventId = await ark.create_profile(profile); - if (eventId) { - navigate({ - to: "/auth/new/backup", - search: { account: keys.npub }, - replace: true, - }); - } - } - } catch (e) { - setLoading(false); - toast.error(String(e)); - } - }; + if (eventId) { + navigate({ + to: "/auth/new/backup", + search: { account: keys.npub }, + replace: true, + }); + } + } + } catch (e) { + setLoading(false); + toast.error(String(e)); + } + }; - return ( -
-
-

Let's set up your profile.

-
-
-
- {picture ? ( - avatar - ) : null} - - - -
-
-
-
- - -
-
- - -
-
- -