chore: clean up

This commit is contained in:
2024-04-03 07:29:46 +07:00
parent 89bb8d88f6
commit 763cb10e85
42 changed files with 104 additions and 532 deletions

View File

@@ -1,5 +1,3 @@
import { useArk } from "@lume/ark";
import { ArkProvider } from "./ark";
import { QueryClient } from "@tanstack/react-query";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import React, { StrictMode } from "react";
@@ -13,6 +11,7 @@ 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 queryClient = new QueryClient({
defaultOptions: {
@@ -27,6 +26,7 @@ const persister = createSyncStoragePersister({
storage: window.localStorage,
});
const ark = new Ark();
const platformName = await platform();
const osLocale = (await locale()).slice(0, 2);
@@ -34,10 +34,10 @@ const osLocale = (await locale()).slice(0, 2);
const router = createRouter({
routeTree,
context: {
ark: undefined!,
platform: platformName,
locale: osLocale,
settings: null,
ark,
queryClient,
},
});
@@ -49,17 +49,8 @@ declare module "@tanstack/react-router" {
}
}
function InnerApp() {
const ark = useArk();
return <RouterProvider router={router} context={{ ark }} />;
}
function App() {
return (
<ArkProvider>
<InnerApp />
</ArkProvider>
);
return <RouterProvider router={router} />;
}
// biome-ignore lint/style/noNonNullAssertion: idk

View File

@@ -1,7 +0,0 @@
import { Ark, ArkContext } from "@lume/ark";
import { PropsWithChildren, useMemo } from "react";
export const ArkProvider = ({ children }: PropsWithChildren<object>) => {
const ark = useMemo(() => new Ark(), []);
return <ArkContext.Provider value={ark}>{children}</ArkContext.Provider>;
};

View File

@@ -1,11 +1,14 @@
import { useArk } from "@lume/ark";
import { Account } from "@lume/types";
import { User } from "@lume/ui";
import { useNavigate, useParams } from "@tanstack/react-router";
import {
useNavigate,
useParams,
useRouteContext,
} from "@tanstack/react-router";
import { useEffect, useState } from "react";
export function Accounts() {
const ark = useArk();
const { ark } = useRouteContext({ strict: false });
const params = useParams({ strict: false });
const [accounts, setAccounts] = useState<Account[]>(null);
@@ -36,7 +39,7 @@ export function Accounts() {
}
function Inactive({ pubkey }: { pubkey: string }) {
const ark = useArk();
const { ark } = useRouteContext({ strict: false });
const navigate = useNavigate();
const changeAccount = async (npub: string) => {

View File

@@ -1,6 +1,6 @@
import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
import { cn } from "@lume/utils";
import { useRouteContext } from "@tanstack/react-router";
import { Dispatch, ReactNode, SetStateAction, useState } from "react";
import { toast } from "sonner";
@@ -13,7 +13,7 @@ export function AvatarUploader({
children: ReactNode;
className?: string;
}) {
const ark = useArk();
const { ark } = useRouteContext({ strict: false });
const [loading, setLoading] = useState(false);
const uploadAvatar = async () => {

View File

@@ -1,121 +0,0 @@
import { ArrowRightIcon, CancelIcon } from "@lume/icons";
import * as Dialog from "@radix-ui/react-dialog";
import { Link, useParams } from "@tanstack/react-router";
import { invoke } from "@tauri-apps/api/core";
import { useState } from "react";
import { toast } from "sonner";
export function BackupDialog() {
// @ts-ignore, magic!!!
const { account } = useParams({ strict: false });
const [key, setKey] = useState(null);
const [passphase, setPassphase] = useState("");
const [loading, setLoading] = useState(false);
const encryptKey = async () => {
try {
setLoading(true);
const encrypted: string = await invoke("get_encrypted_key", {
npub: account,
password: passphase,
});
if (encrypted) {
setKey(encrypted);
}
setLoading(false);
} catch (e) {
setLoading(false);
toast.error(String(e));
}
};
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-full items-center justify-center rounded-lg bg-neutral-900 text-sm font-medium leading-tight text-neutral-100 hover:bg-neutral-800"
>
Claim & Backup
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/30 backdrop-blur dark:bg-white/30" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Dialog.Close className="absolute right-5 top-5 flex w-12 flex-col items-center justify-center gap-1 text-white">
<CancelIcon className="size-8" />
<span className="text-sm font-medium uppercase text-neutral-400 dark:text-neutral-600">
Esc
</span>
</Dialog.Close>
<div className="relative flex h-min w-full max-w-xl flex-col gap-8 rounded-xl bg-white p-5 dark:bg-black">
<div className="flex flex-col">
<h3 className="text-lg font-semibold">
This is your account key
</h3>
<p>
It's use for login to Lume or other Nostr clients. You will lost
access to your account if you lose this key.
</p>
</div>
<div className="flex flex-col gap-5">
<div className="flex flex-col gap-2">
<label htmlFor="nsec">Set a passphase to secure your key</label>
<div className="relative">
<input
name="passphase"
type="password"
value={passphase}
onChange={(e) => 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"
/>
</div>
</div>
{key ? (
<div className="flex flex-col gap-2">
<label htmlFor="nsec">
Copy this key and keep it in safe place
</label>
<input
name="nsec"
type="text"
value={key}
readOnly
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"
/>
</div>
) : null}
</div>
<div className="flex flex-col gap-3">
{!key ? (
<button
type="button"
onClick={encryptKey}
disabled={loading}
className="inline-flex h-11 w-full items-center justify-between gap-1.5 rounded-lg bg-blue-500 px-5 font-medium text-white hover:bg-blue-600"
>
<div className="size-5" />
<div>Submit</div>
<ArrowRightIcon className="size-5" />
</button>
) : (
<Link
to="/$account/home/local"
params={{ account }}
search={{ guest: false }}
className="inline-flex h-11 w-full items-center justify-center gap-1.5 rounded-lg bg-blue-500 px-5 font-medium text-white hover:bg-blue-600"
>
I've safely store my account key
</Link>
)}
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@@ -1,12 +1,11 @@
import { useArk } from "@lume/ark";
import { User } from "@lume/ui";
import { getBitcoinDisplayValues } from "@lume/utils";
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 ark = useArk();
const value = useMemo(() => getBitcoinDisplayValues(balance), [balance]);
useEffect(() => {

View File

@@ -1,125 +0,0 @@
import { useArk } from "@lume/ark";
import { ArrowRightIcon, CancelIcon } from "@lume/icons";
import * as Dialog from "@radix-ui/react-dialog";
import { useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import { toast } from "sonner";
export function LoginDialog() {
const ark = useArk();
const navigate = useNavigate();
const [nsec, setNsec] = useState("");
const [passphase, setPassphase] = useState("");
const login = async () => {
try {
if (!nsec.length) {
return toast.info("You must enter a valid nsec or ncrypto");
}
if (nsec.startsWith("ncrypto") && !passphase.length) {
return toast.warning("You must provide a passphase for ncrypto key");
}
const save = await ark.save_account(nsec, passphase);
if (save) {
navigate({ to: "/", search: { guest: false } });
}
} catch (e) {
toast.error(String(e));
}
};
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-full items-center justify-center rounded-lg bg-neutral-900 text-sm font-medium leading-tight text-neutral-100 hover:bg-neutral-800"
>
Add account
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/30 backdrop-blur dark:bg-white/30" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Dialog.Close className="absolute right-5 top-5 flex w-12 flex-col items-center justify-center gap-1 text-white">
<CancelIcon className="size-8" />
<span className="text-sm font-medium uppercase text-neutral-400 dark:text-neutral-600">
Esc
</span>
</Dialog.Close>
<div className="relative flex h-min w-full max-w-xl flex-col gap-8 rounded-xl bg-white p-5 dark:bg-black">
<div className="flex flex-col gap-1.5">
<h3 className="text-lg font-semibold">Add new account with</h3>
<div className="flex h-11 items-center overflow-hidden rounded-lg bg-neutral-100 p-1 dark:bg-neutral-900">
<button
type="button"
className="h-full flex-1 rounded-md bg-white text-sm font-medium dark:bg-black"
>
nsec
</button>
<button
type="button"
className="flex h-full flex-1 flex-col items-center justify-center rounded-md text-sm font-medium"
>
<span className="leading-tight">nsecBunker</span>
<span className="text-xs font-normal leading-tight text-neutral-700 dark:text-neutral-300">
coming soon
</span>
</button>
<button
type="button"
className="flex h-full flex-1 flex-col items-center justify-center rounded-md text-sm font-medium"
>
<span className="leading-tight">Address</span>
<span className="text-xs font-normal leading-tight text-neutral-700 dark:text-neutral-300">
coming soon
</span>
</button>
</div>
</div>
<div className="flex flex-col gap-5">
<div className="flex flex-col gap-2">
<label htmlFor="nsec">
Enter sign in key start with nsec or ncrypto
</label>
<input
name="nsec"
type="text"
placeholder="nsec or ncrypto..."
value={nsec}
onChange={(e) => setNsec(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"
/>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="nsec">Passphase (optional)</label>
<input
name="passphase"
type="password"
value={passphase}
onChange={(e) => 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"
/>
</div>
</div>
<div className="flex flex-col gap-3">
<button
type="button"
onClick={login}
className="inline-flex h-11 w-full items-center justify-between gap-1.5 rounded-lg bg-blue-500 px-5 font-medium text-white hover:bg-blue-600"
>
<div className="size-5" />
<div>Add account</div>
<ArrowRightIcon className="size-5" />
</button>
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@@ -3,8 +3,8 @@ import { Event } from "@lume/types";
import { cn } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { useArk } from "@lume/ark";
import { Note, User } from "@lume/ui";
import { useRouteContext } from "@tanstack/react-router";
export function RepostNote({
event,
@@ -13,8 +13,7 @@ export function RepostNote({
event: Event;
className?: string;
}) {
const ark = useArk();
const { ark } = useRouteContext({ strict: false });
const { t } = useTranslation();
const {
isLoading,

View File

@@ -2,16 +2,14 @@ import { ComposeFilledIcon, PlusIcon } from "@lume/icons";
import { Outlet, createFileRoute, useNavigate } from "@tanstack/react-router";
import { cn } from "@lume/utils";
import { Accounts } from "@/components/accounts";
import { useArk } from "@lume/ark";
export const Route = createFileRoute("/$account")({
component: App,
});
function App() {
const ark = useArk();
const navigate = useNavigate();
const { platform } = Route.useRouteContext();
const { ark, platform } = Route.useRouteContext();
return (
<div className="flex h-screen w-screen flex-col">

View File

@@ -1,5 +1,4 @@
import { AvatarUploader } from "@/components/avatarUploader";
import { useArk } from "@lume/ark";
import { LoaderIcon, PlusIcon } from "@lume/icons";
import { Metadata } from "@lume/types";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
@@ -16,11 +15,11 @@ export const Route = createFileRoute("/auth/new/profile")({
});
function Screen() {
const ark = useArk();
const keys = Route.useLoaderData();
const navigate = useNavigate();
const { t } = useTranslation();
const { ark } = Route.useRouteContext();
const { register, handleSubmit } = useForm();
const [picture, setPicture] = useState<string>("");

View File

@@ -1,8 +1,6 @@
import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
export const Route = createLazyFileRoute("/auth/privkey")({
@@ -10,10 +8,9 @@ export const Route = createLazyFileRoute("/auth/privkey")({
});
function Screen() {
const ark = useArk();
const { ark } = Route.useRouteContext();
const navigate = useNavigate();
const [t] = useTranslation();
const [key, setKey] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);

View File

@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
import * as Switch from "@radix-ui/react-switch";
import { useEffect, useState } from "react";
import { Settings } from "@lume/types";
import { useArk } from "@lume/ark";
import {
isPermissionGranted,
requestPermission,
@@ -16,12 +15,12 @@ export const Route = createLazyFileRoute("/auth/settings")({
});
function Screen() {
const ark = useArk();
const navigate = useNavigate();
// @ts-ignore, magic!!!
const { account } = Route.useSearch();
const { t } = useTranslation();
const { ark } = Route.useRouteContext();
const [settings, setSettings] = useState<Settings>({
notification: false,

View File

@@ -1,4 +1,3 @@
import { useArk } from "@lume/ark";
import { AddMediaIcon, LoaderIcon } from "@lume/icons";
import { cn, insertImage, isImagePath } from "@lume/utils";
import { useEffect, useState } from "react";
@@ -6,9 +5,10 @@ import { useSlateStatic } from "slate-react";
import { toast } from "sonner";
import { getCurrent } from "@tauri-apps/api/window";
import { UnlistenFn } from "@tauri-apps/api/event";
import { useRouteContext } from "@tanstack/react-router";
export function MediaButton({ className }: { className?: string }) {
const ark = useArk();
const { ark } = useRouteContext({ strict: false });
const editor = useSlateStatic();
const [loading, setLoading] = useState(false);

View File

@@ -1,4 +1,3 @@
import { useArk } from "@lume/ark";
import { LoaderIcon, TrashIcon } from "@lume/icons";
import {
Portal,
@@ -61,6 +60,7 @@ export const Route = createFileRoute("/editor/")({
function Screen() {
// @ts-ignore, useless
const { reply_to, quote } = Route.useSearch();
const { ark } = Route.useRouteContext();
let initialValue: EditorElement[];
@@ -89,7 +89,6 @@ function Screen() {
];
}
const ark = useArk();
const ref = useRef<HTMLDivElement | null>();
const contacts = useSuspenseQuery(contactQueryOptions).data as Contact[];

View File

@@ -1,10 +1,10 @@
import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
import { cn } from "@lume/utils";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { EventWithReplies } from "@lume/types";
import { Reply } from "./reply";
import { useRouteContext } from "@tanstack/react-router";
export function ReplyList({
eventId,
@@ -13,8 +13,7 @@ export function ReplyList({
eventId: string;
className?: string;
}) {
const ark = useArk();
const { ark } = useRouteContext({ strict: false });
const [t] = useTranslation();
const [data, setData] = useState<null | EventWithReplies[]>(null);

View File

@@ -1,4 +1,3 @@
import { useArk } from "@lume/ark";
import { LoaderIcon, PlusIcon } from "@lume/icons";
import { User } from "@lume/ui";
import { Link } from "@tanstack/react-router";
@@ -45,7 +44,6 @@ export const Route = createFileRoute("/")({
});
function Screen() {
const ark = useArk();
const navigate = useNavigate();
const context = Route.useRouteContext();
@@ -53,8 +51,8 @@ function Screen() {
const select = async (npub: string) => {
setLoading(true);
const loadAccount = await ark.load_selected_account(npub);
context.settings = await ark.get_settings(npub);
const loadAccount = await context.ark.load_selected_account(npub);
context.settings = await context.ark.get_settings(npub);
if (loadAccount) {
navigate({
@@ -85,7 +83,7 @@ function Screen() {
</div>
) : (
<>
{ark.accounts.map((account) => (
{context.ark.accounts.map((account) => (
<button
type="button"
key={account.npub}

View File

@@ -1,10 +1,10 @@
import { RepostNote } from "@/components/repost";
import { Suggest } from "@/components/suggest";
import { TextNote } from "@/components/text";
import { useEvents } from "@lume/ark";
import { LoaderIcon, ArrowRightCircleIcon, InfoIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { Column } from "@lume/ui";
import { useInfiniteQuery } from "@tanstack/react-query";
import { createLazyFileRoute } from "@tanstack/react-router";
import { useTranslation } from "react-i18next";
import { Virtualizer } from "virtua";
@@ -16,9 +16,24 @@ export const Route = createLazyFileRoute("/newsfeed")({
export function Screen() {
// @ts-ignore, just work!!!
const { id, name, account } = Route.useSearch();
const { ark } = Route.useRouteContext();
const { t } = useTranslation();
const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } =
useEvents("local", account);
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ["local", account],
initialPageParam: 0,
queryFn: async ({ pageParam }: { pageParam: number }) => {
const events = await ark.get_events("local", 20, pageParam, true);
return events;
},
getNextPageParam: (lastPage) => {
const lastEvent = lastPage?.at(-1);
if (!lastEvent) return;
return lastEvent.created_at - 1;
},
select: (data) => data?.pages.flatMap((page) => page),
refetchOnWindowFocus: false,
});
const renderItem = (event: Event) => {
if (!event) return;

View File

@@ -1,4 +1,3 @@
import { useArk } from "@lume/ark";
import { ArrowRightIcon, ZapIcon } from "@lume/icons";
import { Container } from "@lume/ui";
import { createLazyFileRoute } from "@tanstack/react-router";
@@ -9,7 +8,7 @@ export const Route = createLazyFileRoute("/nwc")({
});
function Screen() {
const ark = useArk();
const { ark } = Route.useRouteContext();
const [uri, setUri] = useState("");
const [isDone, setIsDone] = useState(false);

View File

@@ -1,13 +1,13 @@
import { TextNote } from "@/components/text";
import { RepostNote } from "@/components/repost";
import { useArk } from "@lume/ark";
import { ArrowRightCircleIcon, InfoIcon, LoaderIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { FETCH_LIMIT } from "@lume/utils";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useRouteContext } from "@tanstack/react-router";
export function EventList({ id }: { id: string }) {
const ark = useArk();
const { ark } = useRouteContext({ strict: false });
const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ["events", id],

View File

@@ -1,5 +1,4 @@
import { Balance } from "@/components/balance";
import { useArk } from "@lume/ark";
import { Box, Container, User } from "@lume/ui";
import { createLazyFileRoute } from "@tanstack/react-router";
import { useState } from "react";
@@ -16,6 +15,7 @@ export const Route = createLazyFileRoute("/zap/$id")({
function Screen() {
const { t } = useTranslation();
const { ark } = Route.useRouteContext();
const { id } = Route.useParams();
// @ts-ignore, magic !!!
const { pubkey, account } = Route.useSearch();
@@ -25,8 +25,6 @@ function Screen() {
const [isCompleted, setIsCompleted] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const ark = useArk();
const submit = async () => {
try {
// start loading