mirror of
https://github.com/lumehq/lume.git
synced 2025-04-07 03:18:12 +02:00
chore: clean up
This commit is contained in:
parent
89bb8d88f6
commit
763cb10e85
@ -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
|
||||
|
@ -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>;
|
||||
};
|
@ -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) => {
|
||||
|
@ -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 () => {
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -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(() => {
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -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,
|
||||
|
@ -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">
|
||||
|
@ -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>("");
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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[];
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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],
|
||||
|
@ -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
|
||||
|
@ -1,4 +0,0 @@
|
||||
import { createContext } from "react";
|
||||
import { type Ark } from "./ark";
|
||||
|
||||
export const ArkContext = createContext<Ark | null>(undefined);
|
@ -1,10 +0,0 @@
|
||||
import { useContext } from "react";
|
||||
import { ArkContext } from "../context";
|
||||
|
||||
export const useArk = () => {
|
||||
const context = useContext(ArkContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useArk must be used within an ArkProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
@ -1,13 +1,18 @@
|
||||
import { Event } from "@lume/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useArk } from "./useArk";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export function useEvent(id: string) {
|
||||
const ark = useArk();
|
||||
const { isLoading, isError, data } = useQuery({
|
||||
queryKey: ["event", id],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const event = await ark.get_event(id);
|
||||
const eventId: string = id
|
||||
.replace("nostr:", "")
|
||||
.split("'")[0]
|
||||
.split(".")[0];
|
||||
const cmd: string = await invoke("get_event", { id: eventId });
|
||||
const event: Event = JSON.parse(cmd);
|
||||
return event;
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
|
@ -1,48 +0,0 @@
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { useArk } from "./useArk";
|
||||
|
||||
const FETCH_LIMIT = 20;
|
||||
const QUERY_KEY = "local";
|
||||
const DEDUP = true;
|
||||
|
||||
export function useEvents(key: string, account?: string) {
|
||||
const ark = useArk();
|
||||
const {
|
||||
data,
|
||||
isError,
|
||||
isLoading,
|
||||
isRefetching,
|
||||
isFetchingNextPage,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
} = useInfiniteQuery({
|
||||
queryKey: [key, account],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
||||
const events = await ark.get_events(
|
||||
QUERY_KEY,
|
||||
FETCH_LIMIT,
|
||||
pageParam,
|
||||
DEDUP,
|
||||
);
|
||||
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,
|
||||
});
|
||||
|
||||
return {
|
||||
data,
|
||||
isError,
|
||||
isLoading,
|
||||
isRefetching,
|
||||
isFetchingNextPage,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
};
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useArk } from "./useArk";
|
||||
import { Metadata } from "@lume/types";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export function useProfile(pubkey: string) {
|
||||
const ark = useArk();
|
||||
const {
|
||||
isLoading,
|
||||
isError,
|
||||
@ -11,8 +11,14 @@ export function useProfile(pubkey: string) {
|
||||
queryKey: ["user", pubkey],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const profile = await ark.get_profile(pubkey);
|
||||
return profile;
|
||||
const id = pubkey
|
||||
.replace("nostr:", "")
|
||||
.split("'")[0]
|
||||
.split(".")[0]
|
||||
.split(",")[0]
|
||||
.split("?")[0];
|
||||
const cmd: Metadata = await invoke("get_profile", { id });
|
||||
return cmd;
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
export * from "./ark";
|
||||
export * from "./context";
|
||||
export * from "./hooks/useArk";
|
||||
export * from "./hooks/useEvent";
|
||||
export * from "./hooks/useEvents";
|
||||
export * from "./hooks/useProfile";
|
||||
|
@ -1,53 +0,0 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export function AppHandler({ tag }: { tag: string[] }) {
|
||||
const ark = useArk();
|
||||
|
||||
const { isLoading, isError, data } = useQuery({
|
||||
queryKey: ["app-handler", tag[1]],
|
||||
queryFn: async () => {
|
||||
const ref = tag[1].split(":");
|
||||
const event = await ark.getEventByFilter({
|
||||
filter: {
|
||||
kinds: [Number(ref[0])],
|
||||
authors: [ref[1]],
|
||||
"#d": [ref[2]],
|
||||
},
|
||||
});
|
||||
|
||||
if (!event) return null;
|
||||
|
||||
const app = NDKAppHandlerEvent.from(event);
|
||||
return await app.fetchProfile();
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
<div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (isError || !data) {
|
||||
return <div>Error</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 rounded-md bg-neutral-200 p-2 hover:ring-1 hover:ring-blue-500 dark:bg-neutral-800">
|
||||
<img
|
||||
src={data?.picture || data?.image}
|
||||
alt={data.pubkey}
|
||||
decoding="async"
|
||||
className="h-9 w-9 shrink-0 rounded-lg bg-white object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50"
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<div className="max-w-[15rem] truncate font-semibold text-neutral-950 dark:text-neutral-50">
|
||||
{data.name}
|
||||
</div>
|
||||
<div className="line-clamp-1 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{data.about}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import { ArrowDownIcon, ArrowUpIcon, LoaderIcon } from "@lume/icons";
|
||||
import { ArrowDownIcon, LoaderIcon } from "@lume/icons";
|
||||
import { useState } from "react";
|
||||
import { useNoteContext } from "../provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { cn } from "@lume/utils";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function NoteDownvote() {
|
||||
const ark = useArk();
|
||||
const ark = useRouteContext({ strict: false });
|
||||
const event = useNoteContext();
|
||||
|
||||
const [t] = useTranslation();
|
||||
@ -48,7 +48,7 @@ export function NoteDownvote() {
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] dark:bg-neutral-50 dark:text-neutral-950">
|
||||
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade dark:bg-neutral-50 dark:text-neutral-950">
|
||||
{t("note.buttons.downvote")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
|
@ -2,12 +2,12 @@ import { ReplyIcon } from "@lume/icons";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNoteContext } from "../provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function NoteReply() {
|
||||
const ark = useArk();
|
||||
const event = useNoteContext();
|
||||
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@ -23,7 +23,7 @@ export function NoteReply() {
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] dark:bg-neutral-50 dark:text-neutral-950">
|
||||
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade dark:bg-neutral-50 dark:text-neutral-950">
|
||||
{t("note.menu.viewThread")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { LoaderIcon, QuoteIcon, ReplyIcon, RepostIcon } from "@lume/icons";
|
||||
import { LoaderIcon, QuoteIcon, RepostIcon } from "@lume/icons";
|
||||
import { cn } from "@lume/utils";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
@ -6,10 +6,10 @@ import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useNoteContext } from "../provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function NoteRepost() {
|
||||
const ark = useArk();
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const event = useNoteContext();
|
||||
|
||||
const [t] = useTranslation();
|
||||
@ -59,7 +59,7 @@ export function NoteRepost() {
|
||||
</Tooltip.Trigger>
|
||||
</DropdownMenu.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] dark:bg-neutral-50 dark:text-neutral-950">
|
||||
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade dark:bg-neutral-50 dark:text-neutral-950">
|
||||
{t("note.buttons.repost")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { ArrowUpIcon, LoaderIcon } from "@lume/icons";
|
||||
import { useState } from "react";
|
||||
import { useNoteContext } from "../provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { cn } from "@lume/utils";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function NoteUpvote() {
|
||||
const ark = useArk();
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const event = useNoteContext();
|
||||
|
||||
const [t] = useTranslation();
|
||||
@ -48,7 +48,7 @@ export function NoteUpvote() {
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] dark:bg-neutral-50 dark:text-neutral-950">
|
||||
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-neutral-50 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade dark:bg-neutral-50 dark:text-neutral-950">
|
||||
{t("note.buttons.upvote")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { ZapIcon } from "@lume/icons";
|
||||
import { toast } from "sonner";
|
||||
import { useNoteContext } from "../provider";
|
||||
import { useSearch } from "@tanstack/react-router";
|
||||
import { useRouteContext, useSearch } from "@tanstack/react-router";
|
||||
|
||||
export function NoteZap() {
|
||||
const ark = useArk();
|
||||
const event = useNoteContext();
|
||||
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const { account } = useSearch({ strict: false });
|
||||
|
||||
const zap = async () => {
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
VIDEOS,
|
||||
cn,
|
||||
} from "@lume/utils";
|
||||
import { NIP89 } from "./nip89";
|
||||
import { useNoteContext } from "./provider";
|
||||
import { ReactNode, useMemo } from "react";
|
||||
import { nanoid } from "nanoid";
|
||||
@ -136,10 +135,6 @@ export function NoteContent({
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (event.kind !== Kind.Text) {
|
||||
return <NIP89 className={className} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("select-text", className)}>
|
||||
<div className="content-break whitespace-pre-line text-balance leading-normal">
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { User } from "../../user";
|
||||
import { useArk, useEvent } from "@lume/ark";
|
||||
import { useEvent } from "@lume/ark";
|
||||
import { LinkIcon } from "@lume/icons";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function MentionNote({
|
||||
eventId,
|
||||
@ -10,8 +11,7 @@ export function MentionNote({
|
||||
eventId: string;
|
||||
openable?: boolean;
|
||||
}) {
|
||||
const ark = useArk();
|
||||
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const { t } = useTranslation();
|
||||
const { isLoading, isError, data } = useEvent(eventId);
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { useArk, useProfile } from "@lume/ark";
|
||||
import { useProfile } from "@lume/ark";
|
||||
import { displayNpub } from "@lume/utils";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function MentionUser({ pubkey }: { pubkey: string }) {
|
||||
const ark = useArk();
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const { isLoading, isError, profile } = useProfile(pubkey);
|
||||
|
||||
return (
|
||||
|
@ -3,13 +3,13 @@ import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNoteContext } from "./provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { toast } from "sonner";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function NoteMenu() {
|
||||
const ark = useArk();
|
||||
const event = useNoteContext();
|
||||
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const { t } = useTranslation();
|
||||
|
||||
const copyID = async () => {
|
||||
|
@ -1,55 +0,0 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AppHandler } from "./appHandler";
|
||||
import { useNoteContext } from "./provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
|
||||
export function NIP89({ className }: { className?: string }) {
|
||||
const ark = useArk();
|
||||
const event = useNoteContext();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { isLoading, isError, data } = useQuery({
|
||||
queryKey: ["app-recommend", event.id],
|
||||
queryFn: () => {
|
||||
return ark.getAppRecommend({
|
||||
unknownKind: event.kind.toString(),
|
||||
author: event.pubkey,
|
||||
});
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
<div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (isError || !data) {
|
||||
return <div>Error</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="flex flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||
<div className="inline-flex h-10 shrink-0 items-center justify-between border-b border-neutral-200 px-3 dark:border-neutral-800">
|
||||
<p className="text-sm font-medium text-amber-400">
|
||||
{t("nip89.unsupported")}
|
||||
</p>
|
||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{event.kind}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2 px-3 py-3">
|
||||
<span className="text-sm font-medium uppercase text-neutral-600 dark:text-neutral-400">
|
||||
{t("nip89.openWith")}
|
||||
</span>
|
||||
{data.map((item) => (
|
||||
<AppHandler key={item[1]} tag={item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -5,7 +5,7 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Note } from "..";
|
||||
import { User } from "../../user";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function RepostNote({
|
||||
event,
|
||||
@ -14,8 +14,7 @@ export function RepostNote({
|
||||
event: Event;
|
||||
className?: string;
|
||||
}) {
|
||||
const ark = useArk();
|
||||
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
isLoading,
|
||||
|
@ -2,11 +2,11 @@ import { cn } from "@lume/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Note } from ".";
|
||||
import { useNoteContext } from "./provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { LinkIcon } from "@lume/icons";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function NoteThread({ className }: { className?: string }) {
|
||||
const ark = useArk();
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const event = useNoteContext();
|
||||
const thread = ark.parse_event_thread({
|
||||
content: event.content,
|
||||
|
@ -2,10 +2,10 @@ import { cn } from "@lume/utils";
|
||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||
import { User } from "../user";
|
||||
import { useNoteContext } from "./provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function NoteUser({ className }: { className?: string }) {
|
||||
const ark = useArk();
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const event = useNoteContext();
|
||||
|
||||
return (
|
||||
@ -32,7 +32,7 @@ export function NoteUser({ className }: { className?: string }) {
|
||||
</User.Root>
|
||||
<HoverCard.Portal>
|
||||
<HoverCard.Content
|
||||
className="data-[side=bottom]:animate-slideUpAndFade w-[300px] rounded-xl bg-black p-3 data-[state=open]:transition-all dark:bg-white dark:shadow-none"
|
||||
className="w-[300px] rounded-xl bg-black p-3 data-[side=bottom]:animate-slideUpAndFade data-[state=open]:transition-all dark:bg-white dark:shadow-none"
|
||||
sideOffset={5}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
|
@ -1,12 +1,12 @@
|
||||
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 { useUserContext } from "./provider";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function UserFollowButton({ className }: { className?: string }) {
|
||||
const ark = useArk();
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const user = useUserContext();
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
@ -2,12 +2,12 @@ import { VerifiedIcon } from "@lume/icons";
|
||||
import { cn, displayLongHandle, displayNpub } from "@lume/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useUserContext } from "./provider";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
export function UserNip05({ className }: { className?: string }) {
|
||||
const ark = useArk();
|
||||
const user = useUserContext();
|
||||
|
||||
const { ark } = useRouteContext({ strict: false });
|
||||
const { isLoading, data: verified } = useQuery({
|
||||
queryKey: ["nip05", user?.pubkey],
|
||||
queryFn: async () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user