chore: clean up
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import { Spinner } from "@/components";
|
||||
import { Column } from "@/components/column";
|
||||
import { LumeWindow, NostrQuery } from "@/system";
|
||||
import { LumeWindow } from "@/system";
|
||||
import type { ColumnEvent, LumeColumn } from "@/types";
|
||||
import { ArrowLeft, ArrowRight, Plus, StackPlus } from "@phosphor-icons/react";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
@@ -24,7 +25,7 @@ export const Route = createLazyFileRoute("/$account/_app/home")({
|
||||
});
|
||||
|
||||
function Screen() {
|
||||
const initialColumnList = Route.useLoaderData();
|
||||
const { initialColumns } = Route.useRouteContext();
|
||||
|
||||
const [columns, setColumns] = useState<LumeColumn[]>([]);
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({
|
||||
@@ -105,6 +106,18 @@ function Screen() {
|
||||
event.preventDefault();
|
||||
}, 150);
|
||||
|
||||
const saveAllColumns = useDebouncedCallback(async () => {
|
||||
const key = "lume_v4:columns";
|
||||
const content = JSON.stringify(columns);
|
||||
const res = await commands.setLumeStore(key, content);
|
||||
|
||||
if (res.status === "ok") {
|
||||
return res.data;
|
||||
} else {
|
||||
console.log(res.error);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
useEffect(() => {
|
||||
if (emblaApi) {
|
||||
emblaApi.on("scroll", emitScrollEvent);
|
||||
@@ -120,14 +133,12 @@ function Screen() {
|
||||
}, [emblaApi, emitScrollEvent, emitResizeEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (columns) {
|
||||
NostrQuery.setColumns(columns).then(() => console.log("saved"));
|
||||
}
|
||||
if (columns) saveAllColumns();
|
||||
}, [columns]);
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(initialColumnList);
|
||||
}, [initialColumnList]);
|
||||
setColumns(initialColumns);
|
||||
}, [initialColumns]);
|
||||
|
||||
// Listen for keyboard event
|
||||
useEffect(() => {
|
||||
|
||||
@@ -3,16 +3,18 @@ import type { LumeColumn } from "@/types";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/$account/_app/home")({
|
||||
loader: async ({ context }) => {
|
||||
beforeLoad: async ({ context }) => {
|
||||
const key = "lume_v4:columns";
|
||||
const defaultColumns = context.systemColumns.filter((col) => col.default);
|
||||
const query = await commands.getLumeStore(key);
|
||||
|
||||
let initialColumns: LumeColumn[] = defaultColumns;
|
||||
|
||||
if (query.status === "ok") {
|
||||
const columns: LumeColumn[] = JSON.parse(query.data);
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
initialColumns = JSON.parse(query.data);
|
||||
return { initialColumns };
|
||||
}
|
||||
|
||||
return { initialColumns };
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import { NostrAccount } from "@/system";
|
||||
import { Button } from "@getalby/bitcoin-connect-react";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
@@ -11,8 +12,13 @@ export const Route = createLazyFileRoute("/$account/_settings/bitcoin-connect")(
|
||||
|
||||
function Screen() {
|
||||
const setNwcUri = async (uri: string) => {
|
||||
const cmd = await NostrAccount.setWallet(uri);
|
||||
if (cmd) getCurrentWebviewWindow().close();
|
||||
const res = await commands.setWallet(uri);
|
||||
|
||||
if (res.status === "ok") {
|
||||
await getCurrentWebviewWindow().close();
|
||||
} else {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import { cn } from "@/commons";
|
||||
import { type Profile, commands } from "@/commands.gen";
|
||||
import { cn, upload } from "@/commons";
|
||||
import { Spinner } from "@/components";
|
||||
import { NostrAccount, NostrQuery } from "@/system";
|
||||
import type { Metadata } from "@/types";
|
||||
import { Plus } from "@phosphor-icons/react";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
@@ -27,15 +25,16 @@ function Screen() {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [picture, setPicture] = useState<string>("");
|
||||
|
||||
const onSubmit = (data: Metadata) => {
|
||||
const onSubmit = (data: Profile) => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const newProfile: Metadata = { ...profile, ...data, picture };
|
||||
await NostrAccount.createProfile(newProfile);
|
||||
} catch (e) {
|
||||
await message(String(e), { title: "Profile", kind: "error" });
|
||||
return;
|
||||
const newProfile: Profile = { ...profile, ...data, picture };
|
||||
const res = await commands.setProfile(newProfile);
|
||||
|
||||
if (res.status === "error") {
|
||||
await message(res.error, { title: "Profile", kind: "error" });
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -220,17 +219,18 @@ function AvatarUploader({
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const uploadAvatar = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const image = await NostrQuery.upload();
|
||||
setPicture(image);
|
||||
} catch (e) {
|
||||
setLoading(false);
|
||||
await message(String(e), { title: "Lume", kind: "error" });
|
||||
}
|
||||
const uploadAvatar = () => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const image = await upload();
|
||||
setPicture(image);
|
||||
} catch (e) {
|
||||
await message(String(e));
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -239,7 +239,7 @@ function AvatarUploader({
|
||||
onClick={() => uploadAvatar()}
|
||||
className={cn("size-4", className)}
|
||||
>
|
||||
{loading ? <Spinner className="size-4" /> : children}
|
||||
{isPending ? <Spinner className="size-4" /> : children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import type { Metadata } from "@/types";
|
||||
import { type Profile, commands } from "@/commands.gen";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/$account/_settings/profile")({
|
||||
@@ -7,7 +6,7 @@ export const Route = createFileRoute("/$account/_settings/profile")({
|
||||
const res = await commands.getProfile(params.account);
|
||||
|
||||
if (res.status === "ok") {
|
||||
const profile: Metadata = JSON.parse(res.data);
|
||||
const profile: Profile = JSON.parse(res.data);
|
||||
return { profile };
|
||||
} else {
|
||||
throw new Error(res.error);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import { NostrQuery } from "@/system";
|
||||
import { Plus, X } from "@phosphor-icons/react";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { message } from "@tauri-apps/plugin-dialog";
|
||||
@@ -16,6 +15,16 @@ function Screen() {
|
||||
const [newRelay, setNewRelay] = useState("");
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const removeRelay = async (relay: string) => {
|
||||
const res = await commands.removeRelay(relay);
|
||||
|
||||
if (res.status === "ok") {
|
||||
return res.data;
|
||||
} else {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
};
|
||||
|
||||
const addNewRelay = () => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
@@ -69,7 +78,7 @@ function Screen() {
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => NostrQuery.removeRelay(relay)}
|
||||
onClick={() => removeRelay(relay)}
|
||||
className="inline-flex items-center justify-center rounded-md size-7 hover:bg-black/10 dark:hover:bg-white/10"
|
||||
>
|
||||
<X className="size-4" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NostrAccount } from "@/system";
|
||||
import { commands } from "@/commands.gen";
|
||||
import { createLazyFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createLazyFileRoute("/$account/_settings/wallet")({
|
||||
@@ -10,10 +10,14 @@ function Screen() {
|
||||
const { balance } = Route.useRouteContext();
|
||||
|
||||
const disconnect = async () => {
|
||||
window.localStorage.removeItem("bc:config");
|
||||
await NostrAccount.removeWallet();
|
||||
const res = await commands.removeWallet();
|
||||
|
||||
return redirect({ to: "/$account/bitcoin-connect", params: { account } });
|
||||
if (res.status === "ok") {
|
||||
window.localStorage.removeItem("bc:config");
|
||||
return redirect({ to: "/$account/bitcoin-connect", params: { account } });
|
||||
} else {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import { upload } from "@/commons";
|
||||
import { Frame, GoBack, Spinner } from "@/components";
|
||||
import { NostrQuery } from "@/system";
|
||||
import { Plus } from "@phosphor-icons/react";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { message } from "@tauri-apps/plugin-dialog";
|
||||
@@ -20,7 +20,7 @@ function Screen() {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const uploadAvatar = async () => {
|
||||
const file = await NostrQuery.upload();
|
||||
const file = await upload();
|
||||
|
||||
if (file) {
|
||||
setPicture(file);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { NostrAccount } from "@/system";
|
||||
import { commands } from "@/commands.gen";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/columns/_layout/create-group")({
|
||||
loader: async () => {
|
||||
const contacts = await NostrAccount.getContactList();
|
||||
return contacts;
|
||||
const res = await commands.getContactList();
|
||||
|
||||
if (res.status === "ok") {
|
||||
return res.data;
|
||||
} else {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -56,9 +56,9 @@ function Screen() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => install(column)}
|
||||
className="text-xs uppercase font-semibold w-max h-7 pl-2.5 pr-2 hidden group-hover:inline-flex items-center justify-center rounded-full bg-neutral-200 hover:bg-blue-500 hover:text-white dark:bg-black/10"
|
||||
className="text-xs uppercase font-semibold w-16 h-7 hidden group-hover:inline-flex items-center justify-center rounded-full bg-neutral-200 hover:bg-blue-500 hover:text-white dark:bg-black/10"
|
||||
>
|
||||
Install
|
||||
Open
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Spinner } from "@/components";
|
||||
import { Conversation } from "@/components/conversation";
|
||||
import { Quote } from "@/components/quote";
|
||||
import { RepostNote } from "@/components/repost";
|
||||
import { TextNote } from "@/components/text";
|
||||
import { type LumeEvent, NostrQuery } from "@/system";
|
||||
import { type ColumnRouteSearch, Kind } from "@/types";
|
||||
import { commands } from "@/commands.gen";
|
||||
import { toLumeEvents } from "@/commons";
|
||||
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
||||
import type { LumeEvent } from "@/system";
|
||||
import { Kind } from "@/types";
|
||||
import { ArrowCircleRight } from "@phosphor-icons/react";
|
||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
@@ -13,17 +11,6 @@ import { useCallback, useRef } from "react";
|
||||
import { Virtualizer } from "virtua";
|
||||
|
||||
export const Route = createFileRoute("/columns/_layout/global")({
|
||||
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
||||
return {
|
||||
account: search.account,
|
||||
label: search.label,
|
||||
name: search.name,
|
||||
};
|
||||
},
|
||||
beforeLoad: async () => {
|
||||
const settings = await NostrQuery.getUserSettings();
|
||||
return { settings };
|
||||
},
|
||||
component: Screen,
|
||||
});
|
||||
|
||||
@@ -40,8 +27,14 @@ export function Screen() {
|
||||
queryKey: [label, account],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
||||
const events = await NostrQuery.getGlobalEvents(pageParam);
|
||||
return events;
|
||||
const until = pageParam > 0 ? pageParam.toString() : undefined;
|
||||
const res = await commands.getGlobalEvents(until);
|
||||
|
||||
if (res.status === "error") {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
|
||||
return toLumeEvents(res.data);
|
||||
},
|
||||
getNextPageParam: (lastPage) => lastPage?.at(-1)?.created_at - 1,
|
||||
select: (data) => data?.pages.flat(),
|
||||
@@ -57,15 +50,11 @@ export function Screen() {
|
||||
case Kind.Repost:
|
||||
return <RepostNote key={event.id} event={event} className="mb-3" />;
|
||||
default: {
|
||||
if (event.isConversation) {
|
||||
return (
|
||||
<Conversation key={event.id} className="mb-3" event={event} />
|
||||
);
|
||||
}
|
||||
if (event.isQuote) {
|
||||
return <Quote key={event.id} event={event} className="mb-3" />;
|
||||
} else {
|
||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
||||
}
|
||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -81,12 +70,10 @@ export function Screen() {
|
||||
<ScrollArea.Viewport ref={ref} className="h-full px-3 pb-3">
|
||||
<Virtualizer scrollRef={ref}>
|
||||
{isFetching && !isLoading && !isFetchingNextPage ? (
|
||||
<div className="flex items-center justify-center w-full mb-3 h-12 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Spinner className="size-5" />
|
||||
<span className="text-sm font-medium">
|
||||
Getting new notes...
|
||||
</span>
|
||||
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
||||
<div className="w-max h-8 pl-2 pr-3 inline-flex items-center justify-center gap-1.5 rounded-full shadow-lg text-sm font-medium text-white bg-black dark:text-black dark:bg-white">
|
||||
<Spinner className="size-4" />
|
||||
Getting new notes...
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -96,13 +83,13 @@ export function Screen() {
|
||||
<span className="text-sm font-medium">Loading...</span>
|
||||
</div>
|
||||
) : !data.length ? (
|
||||
<div className="flex items-center justify-center">
|
||||
Yo. You're catching up on all the things happening around you.
|
||||
<div className="mb-3 flex items-center justify-center h-20 text-sm rounded-xl bg-black/5 dark:bg-white/5">
|
||||
🎉 Yo. You're catching up on all latest notes.
|
||||
</div>
|
||||
) : (
|
||||
data.map((item) => renderItem(item))
|
||||
)}
|
||||
{data?.length && hasNextPage ? (
|
||||
{hasNextPage ? (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import { decodeZapInvoice, formatCreatedAt } from "@/commons";
|
||||
import { Spinner } from "@/components";
|
||||
import { Note } from "@/components/note";
|
||||
import { User } from "@/components/user";
|
||||
import { type LumeEvent, NostrQuery, useEvent } from "@/system";
|
||||
import { Kind } from "@/types";
|
||||
import { Note, Spinner, User } from "@/components";
|
||||
import { LumeEvent, useEvent } from "@/system";
|
||||
import { Kind, type NostrEvent } from "@/types";
|
||||
import { Info, Repeat } from "@phosphor-icons/react";
|
||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||
import * as Tabs from "@radix-ui/react-tabs";
|
||||
@@ -24,7 +23,15 @@ function Screen() {
|
||||
const { isLoading, data } = useQuery({
|
||||
queryKey: ["notification", account],
|
||||
queryFn: async () => {
|
||||
const events = await NostrQuery.getNotifications();
|
||||
const res = await commands.getNotifications();
|
||||
|
||||
if (res.status === "error") {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
|
||||
const data: NostrEvent[] = res.data.map((item) => JSON.parse(item));
|
||||
const events = data.map((ev) => new LumeEvent(ev));
|
||||
|
||||
return events;
|
||||
},
|
||||
select: (events) => {
|
||||
|
||||
@@ -1,72 +1,62 @@
|
||||
import { insertImage, isImagePath } from "@/commons";
|
||||
import { insertImage, isImagePath, upload } from "@/commons";
|
||||
import { Spinner } from "@/components";
|
||||
import { NostrQuery } from "@/system";
|
||||
import { Images } from "@phosphor-icons/react";
|
||||
import type { UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { message } from "@tauri-apps/plugin-dialog";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useTransition } from "react";
|
||||
import { useSlateStatic } from "slate-react";
|
||||
|
||||
export function MediaButton() {
|
||||
const editor = useSlateStatic();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const upload = async () => {
|
||||
try {
|
||||
// start loading
|
||||
setLoading(true);
|
||||
|
||||
const image = await NostrQuery.upload();
|
||||
insertImage(editor, image);
|
||||
|
||||
// reset loading
|
||||
setLoading(false);
|
||||
} catch (e) {
|
||||
setLoading(false);
|
||||
await message(String(e), { title: "Upload", kind: "error" });
|
||||
}
|
||||
const uploadMedia = () => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const image = await upload();
|
||||
return insertImage(editor, image);
|
||||
} catch (e) {
|
||||
await message(String(e), { title: "Upload", kind: "error" });
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let unlisten: UnlistenFn = undefined;
|
||||
const unlisten = getCurrentWindow().listen("tauri://file-drop", (event) => {
|
||||
startTransition(async () => {
|
||||
// @ts-ignore, lfg !!!
|
||||
const items: string[] = event.payload.paths;
|
||||
|
||||
async function listenFileDrop() {
|
||||
const window = getCurrentWindow();
|
||||
if (!unlisten) {
|
||||
unlisten = await window.listen("tauri://file-drop", async (event) => {
|
||||
// @ts-ignore, lfg !!!
|
||||
const items: string[] = event.payload.paths;
|
||||
// start loading
|
||||
setLoading(true);
|
||||
// upload all images
|
||||
for (const item of items) {
|
||||
if (isImagePath(item)) {
|
||||
const image = await NostrQuery.upload(item);
|
||||
insertImage(editor, image);
|
||||
}
|
||||
// upload all images
|
||||
for (const item of items) {
|
||||
if (isImagePath(item)) {
|
||||
const image = await upload(item);
|
||||
insertImage(editor, image);
|
||||
}
|
||||
// stop loading
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listenFileDrop();
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (unlisten) unlisten();
|
||||
unlisten.then((f) => f());
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => upload()}
|
||||
disabled={loading}
|
||||
onClick={() => uploadMedia()}
|
||||
disabled={isPending}
|
||||
className="inline-flex items-center h-8 gap-2 px-2.5 text-sm rounded-lg text-black/70 dark:text-white/70 w-max hover:bg-black/10 dark:hover:bg-white/10"
|
||||
>
|
||||
{loading ? <Spinner className="size-4" /> : <Images className="size-4" />}
|
||||
{isPending ? (
|
||||
<Spinner className="size-4" />
|
||||
) : (
|
||||
<Images className="size-4" />
|
||||
)}
|
||||
Add media
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { commands } from "@/commands.gen";
|
||||
import { checkForAppUpdates } from "@/commons";
|
||||
import { NostrAccount } from "@/system";
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
@@ -8,7 +8,7 @@ export const Route = createFileRoute("/")({
|
||||
await checkForAppUpdates(true);
|
||||
|
||||
// Get all accounts
|
||||
const accounts = await NostrAccount.getAccounts();
|
||||
const accounts = await commands.getAccounts();
|
||||
|
||||
if (accounts.length < 1) {
|
||||
throw redirect({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { User } from "@/components/user";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { message } from "@tauri-apps/plugin-dialog";
|
||||
import { useState } from "react";
|
||||
import { useState, useTransition } from "react";
|
||||
import CurrencyInput from "react-currency-input-field";
|
||||
|
||||
const DEFAULT_VALUES = [21, 50, 100, 200];
|
||||
@@ -17,28 +17,26 @@ function Screen() {
|
||||
const [amount, setAmount] = useState(21);
|
||||
const [content, setContent] = useState("");
|
||||
const [isCompleted, setIsCompleted] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
// start loading
|
||||
setIsLoading(true);
|
||||
const submit = () => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const val = await event.zap(amount, content);
|
||||
|
||||
// Zap
|
||||
const val = await event.zap(amount, content);
|
||||
|
||||
if (val) {
|
||||
setIsCompleted(true);
|
||||
// close current window
|
||||
await getCurrentWebviewWindow().close();
|
||||
if (val) {
|
||||
setIsCompleted(true);
|
||||
// close current window
|
||||
await getCurrentWebviewWindow().close();
|
||||
}
|
||||
} catch (e) {
|
||||
await message(String(e), {
|
||||
title: "Zap",
|
||||
kind: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
await message(String(e), {
|
||||
title: "Zap",
|
||||
kind: "error",
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -104,7 +102,7 @@ function Screen() {
|
||||
onClick={() => submit()}
|
||||
className="inline-flex items-center justify-center w-full h-10 font-medium rounded-xl bg-neutral-950 text-neutral-50 hover:bg-neutral-900 dark:bg-white/20 dark:hover:bg-white/30"
|
||||
>
|
||||
{isCompleted ? "Zapped" : isLoading ? "Processing..." : "Zap"}
|
||||
{isCompleted ? "Zapped" : isPending ? "Processing..." : "Zap"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
import { NostrQuery } from "@/system";
|
||||
import { commands } from "@/commands.gen";
|
||||
import { LumeEvent } from "@/system";
|
||||
import type { NostrEvent } from "@/types";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/zap/$id")({
|
||||
beforeLoad: async ({ params }) => {
|
||||
const event = await NostrQuery.getEvent(params.id);
|
||||
return { event };
|
||||
const res = await commands.getEvent(params.id);
|
||||
|
||||
if (res.status === "ok") {
|
||||
const data = res.data;
|
||||
const raw: NostrEvent = JSON.parse(data.raw);
|
||||
|
||||
if (data.parsed) {
|
||||
raw.meta = data.parsed;
|
||||
}
|
||||
|
||||
return { event: new LumeEvent(raw) };
|
||||
} else {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user