fix: editor crash on open

This commit is contained in:
reya 2024-05-28 14:43:34 +07:00
parent 5ca9444358
commit 32f3315344
7 changed files with 666 additions and 511 deletions

View File

@ -1,18 +1,11 @@
import { CheckCircleIcon, InfoCircleIcon, CancelCircleIcon } from "@lume/icons"; import { CheckCircleIcon, InfoCircleIcon, CancelCircleIcon } from "@lume/icons";
import type { Interests, Metadata, Settings } from "@lume/types"; import type { Settings } from "@lume/types";
import { Spinner } from "@lume/ui"; import { Spinner } from "@lume/ui";
import type { QueryClient } from "@tanstack/react-query"; import type { QueryClient } from "@tanstack/react-query";
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router"; import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
import type { Platform } from "@tauri-apps/plugin-os"; import type { Platform } from "@tauri-apps/plugin-os";
import type { Descendant } from "slate";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
type EditorElement = {
type: string;
children: Descendant[];
eventId?: string;
};
interface RouterContext { interface RouterContext {
// System // System
queryClient: QueryClient; queryClient: QueryClient;
@ -21,13 +14,8 @@ interface RouterContext {
locale?: string; locale?: string;
// Settings // Settings
settings?: Settings; settings?: Settings;
interests?: Interests; // Accounts
// Profile
accounts?: string[]; accounts?: string[];
profile?: Metadata;
isNewUser?: boolean;
// Editor
initialValue?: EditorElement[];
} }
export const Route = createRootRouteWithContext<RouterContext>()({ export const Route = createRootRouteWithContext<RouterContext>()({

View File

@ -13,7 +13,7 @@ export function MediaButton({ className }: { className?: string }) {
const editor = useSlateStatic(); const editor = useSlateStatic();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const uploadToNostrBuild = async () => { const upload = async () => {
try { try {
// start loading // start loading
setLoading(true); setLoading(true);
@ -66,7 +66,7 @@ export function MediaButton({ className }: { className?: string }) {
<Tooltip.Trigger asChild> <Tooltip.Trigger asChild>
<button <button
type="button" type="button"
onClick={() => uploadToNostrBuild()} onClick={() => upload()}
disabled={loading} disabled={loading}
className={cn("inline-flex items-center justify-center", className)} className={cn("inline-flex items-center justify-center", className)}
> >

View File

@ -60,7 +60,12 @@ export function MentionButton({ className }: { className?: string }) {
</Tooltip.Provider> </Tooltip.Provider>
<DropdownMenu.Portal> <DropdownMenu.Portal>
<DropdownMenu.Content className="flex w-[220px] h-[220px] scrollbar-none flex-col overflow-y-auto rounded-xl bg-black py-1 shadow-md shadow-neutral-500/20 focus:outline-none dark:bg-white"> <DropdownMenu.Content className="flex w-[220px] h-[220px] scrollbar-none flex-col overflow-y-auto rounded-xl bg-black py-1 shadow-md shadow-neutral-500/20 focus:outline-none dark:bg-white">
{contacts.map((contact) => ( {contacts.length < 1 ? (
<div className="w-full h-full flex items-center justify-center">
<p className="text-sm text-white">Contact List is empty.</p>
</div>
) : (
contacts.map((contact) => (
<DropdownMenu.Item <DropdownMenu.Item
key={contact} key={contact}
onClick={() => select(contact)} onClick={() => select(contact)}
@ -73,7 +78,8 @@ export function MentionButton({ className }: { className?: string }) {
</User.Root> </User.Root>
</User.Provider> </User.Provider>
</DropdownMenu.Item> </DropdownMenu.Item>
))} ))
)}
<DropdownMenu.Arrow className="fill-neutral-950 dark:fill-neutral-50" /> <DropdownMenu.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Portal> </DropdownMenu.Portal>

View File

@ -3,13 +3,13 @@ import { cn } from "@lume/utils";
import * as Tooltip from "@radix-ui/react-tooltip"; import * as Tooltip from "@radix-ui/react-tooltip";
import type { Dispatch, SetStateAction } from "react"; import type { Dispatch, SetStateAction } from "react";
export function NsfwToggle({ export function WarningToggle({
nsfw, warning,
setNsfw, setWarning,
className, className,
}: { }: {
nsfw: boolean; warning: boolean;
setNsfw: Dispatch<SetStateAction<boolean>>; setWarning: Dispatch<SetStateAction<boolean>>;
className?: string; className?: string;
}) { }) {
return ( return (
@ -18,11 +18,11 @@ export function NsfwToggle({
<Tooltip.Trigger asChild> <Tooltip.Trigger asChild>
<button <button
type="button" type="button"
onClick={() => setNsfw((prev) => !prev)} onClick={() => setWarning((prev) => !prev)}
className={cn( className={cn(
"inline-flex items-center justify-center", "inline-flex items-center justify-center",
className, className,
nsfw ? "bg-blue-500 text-white" : "", warning ? "bg-blue-500 text-white" : "",
)} )}
> >
<NsfwIcon className="size-4" /> <NsfwIcon className="size-4" />

View File

@ -1,4 +1,4 @@
import { ComposeFilledIcon, TrashIcon } from "@lume/icons"; import { ComposeFilledIcon } from "@lume/icons";
import { Spinner } from "@lume/ui"; import { Spinner } from "@lume/ui";
import { import {
cn, cn,
@ -8,7 +8,6 @@ import {
sendNativeNotification, sendNativeNotification,
} from "@lume/utils"; } from "@lume/utils";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { nip19 } from "nostr-tools";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { type Descendant, Node, Transforms, createEditor } from "slate"; import { type Descendant, Node, Transforms, createEditor } from "slate";
@ -22,16 +21,22 @@ import {
withReact, withReact,
} from "slate-react"; } from "slate-react";
import { MediaButton } from "./-components/media"; import { MediaButton } from "./-components/media";
import { NsfwToggle } from "./-components/nsfw";
import { MentionButton } from "./-components/mention"; import { MentionButton } from "./-components/mention";
import { MentionNote } from "@/components/note/mentions/note";
import { LumeEvent } from "@lume/system"; import { LumeEvent } from "@lume/system";
import { WarningToggle } from "./-components/warning";
import { MentionNote } from "@/components/note/mentions/note";
type EditorSearch = { type EditorSearch = {
reply_to: string; reply_to: string;
quote: boolean; quote: boolean;
}; };
type EditorElement = {
type: string;
children: Descendant[];
eventId?: string;
};
export const Route = createFileRoute("/editor/")({ export const Route = createFileRoute("/editor/")({
validateSearch: (search: Record<string, string>): EditorSearch => { validateSearch: (search: Record<string, string>): EditorSearch => {
return { return {
@ -39,43 +44,23 @@ export const Route = createFileRoute("/editor/")({
quote: search.quote === "true" || false, quote: search.quote === "true" || false,
}; };
}, },
beforeLoad: async ({ search }) => {
return {
initialValue: search.quote
? [
{
type: "paragraph",
children: [{ text: "" }],
},
{
type: "event",
eventId: `nostr:${nip19.noteEncode(search.reply_to)}`,
children: [{ text: "" }],
},
{
type: "paragraph",
children: [{ text: "" }],
},
]
: [
{
type: "paragraph",
children: [{ text: "" }],
},
],
};
},
component: Screen, component: Screen,
}); });
const initialValue: EditorElement[] = [
{
type: "paragraph",
children: [{ text: "" }],
},
];
function Screen() { function Screen() {
const { reply_to, quote } = Route.useSearch(); const search = Route.useSearch();
const { initialValue } = Route.useRouteContext();
const [t] = useTranslation(); const [t] = useTranslation();
const [editorValue, setEditorValue] = useState(initialValue); const [editorValue, setEditorValue] = useState(initialValue);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [nsfw, setNsfw] = useState(false); const [warning, setWarning] = useState(false);
const [editor] = useState(() => const [editor] = useState(() =>
withMentions(withNostrEvent(withImages(withReact(createEditor())))), withMentions(withNostrEvent(withImages(withReact(createEditor())))),
); );
@ -116,7 +101,12 @@ function Screen() {
setLoading(true); setLoading(true);
const content = serialize(editor.children); const content = serialize(editor.children);
const eventId = await LumeEvent.publish(content, reply_to, quote); const eventId = await LumeEvent.publish(
content,
search.reply_to,
search.quote,
warning,
);
if (eventId) { if (eventId) {
await sendNativeNotification( await sendNativeNotification(
@ -137,15 +127,15 @@ function Screen() {
}; };
return ( return (
<div className="w-full h-full"> <div className="w-full h-full flex flex-col">
<Slate editor={editor} initialValue={editorValue}> <Slate editor={editor} initialValue={editorValue}>
<div <div
data-tauri-drag-region data-tauri-drag-region
className="flex h-14 w-full shrink-0 items-center justify-end gap-2 px-2 border-b border-black/10 dark:border-white/10" className="shrink-0 flex h-14 w-full items-center justify-end gap-2 px-2 border-b border-black/10 dark:border-white/10"
> >
<NsfwToggle <WarningToggle
nsfw={nsfw} warning={warning}
setNsfw={setNsfw} setWarning={setWarning}
className="size-8 rounded-full bg-black/10 hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20" className="size-8 rounded-full bg-black/10 hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20"
/> />
<MentionButton className="size-8 rounded-full bg-black/10 hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20" /> <MentionButton className="size-8 rounded-full bg-black/10 hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20" />
@ -163,13 +153,13 @@ function Screen() {
{t("global.post")} {t("global.post")}
</button> </button>
</div> </div>
<div className="flex h-full w-full flex-1 flex-col"> <div className="flex-1 overflow-y-auto flex flex-col">
{reply_to && !quote ? ( {search.reply_to ? (
<div className="px-4 py-2"> <div className="px-4 py-2">
<MentionNote eventId={reply_to} /> <MentionNote eventId={search.reply_to} />
</div> </div>
) : null} ) : null}
<div className="overflow-y-auto p-4"> <div className="overflow-y-auto scrollbar-none p-4">
<Editable <Editable
key={JSON.stringify(editorValue)} key={JSON.stringify(editorValue)}
autoFocus={true} autoFocus={true}
@ -178,7 +168,7 @@ function Screen() {
spellCheck={false} spellCheck={false}
renderElement={(props) => <Element {...props} />} renderElement={(props) => <Element {...props} />}
placeholder={ placeholder={
reply_to ? "Type your reply..." : t("editor.placeholder") search.reply_to ? "Type your reply..." : t("editor.placeholder")
} }
className="focus:outline-none" className="focus:outline-none"
/> />
@ -252,35 +242,24 @@ const withImages = (editor: ReactEditor) => {
return editor; return editor;
}; };
const Image = ({ attributes, children, element }) => { const Image = ({ attributes, element, children }) => {
const editor = useSlateStatic(); const editor = useSlateStatic();
const path = ReactEditor.findPath(editor as ReactEditor, element);
const selected = useSelected(); const selected = useSelected();
const focused = useFocused(); const focused = useFocused();
const path = ReactEditor.findPath(editor as ReactEditor, element);
return ( return (
<div {...attributes}> <div {...attributes}>
{children} {children}
<div contentEditable={false} className="relative my-2">
<img <img
src={element.url} src={element.url}
alt={element.url} alt={element.url}
className={cn( className={cn(
"h-auto w-full rounded-lg border border-neutral-100 object-cover ring-2 dark:border-neutral-900", "my-2 h-auto w-1/2 rounded-lg object-cover ring-2 outline outline-1 -outline-offset-1 outline-black/15",
selected && focused ? "ring-blue-500" : "ring-transparent", selected && focused ? "ring-blue-500" : "ring-transparent",
)} )}
contentEditable={false}
/>
<button
type="button"
contentEditable={false}
onClick={() => Transforms.removeNodes(editor, { at: path })} onClick={() => Transforms.removeNodes(editor, { at: path })}
className="absolute right-2 top-2 inline-flex size-8 items-center justify-center rounded-lg bg-red-500 text-white hover:bg-red-600" />
>
<TrashIcon className="size-4" />
</button>
</div>
</div> </div>
); );
}; };

View File

@ -1,377 +1,568 @@
// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. // This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually.
export const commands = { export const commands = {
async getRelays() : Promise<Result<Relays, null>> { async getRelays(): Promise<Result<Relays, null>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_relays") }; return { status: "ok", data: await TAURI_INVOKE("get_relays") };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async connectRelay(relay: string) : Promise<Result<boolean, null>> { async connectRelay(relay: string): Promise<Result<boolean, null>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("connect_relay", { relay }) }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("connect_relay", { relay }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async removeRelay(relay: string) : Promise<Result<boolean, null>> { async removeRelay(relay: string): Promise<Result<boolean, null>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("remove_relay", { relay }) }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("remove_relay", { relay }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getAccounts() : Promise<Result<string, string>> { async getAccounts(): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_accounts") }; return { status: "ok", data: await TAURI_INVOKE("get_accounts") };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async createAccount() : Promise<Result<Account, null>> { async createAccount(): Promise<Result<Account, null>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("create_account") }; return { status: "ok", data: await TAURI_INVOKE("create_account") };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async saveAccount(nsec: string, password: string) : Promise<Result<string, string>> { async saveAccount(
try { nsec: string,
return { status: "ok", data: await TAURI_INVOKE("save_account", { nsec, password }) }; password: string,
} catch (e) { ): Promise<Result<string, string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("save_account", { nsec, password }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getEncryptedKey(npub: string, password: string) : Promise<Result<string, string>> { async getEncryptedKey(
try { npub: string,
return { status: "ok", data: await TAURI_INVOKE("get_encrypted_key", { npub, password }) }; password: string,
} catch (e) { ): Promise<Result<string, string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("get_encrypted_key", { npub, password }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async nostrConnect(npub: string, uri: string) : Promise<Result<string, string>> { async nostrConnect(
try { npub: string,
return { status: "ok", data: await TAURI_INVOKE("nostr_connect", { npub, uri }) }; uri: string,
} catch (e) { ): Promise<Result<string, string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("nostr_connect", { npub, uri }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async loadAccount(npub: string) : Promise<Result<boolean, string>> { async loadAccount(npub: string): Promise<Result<boolean, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("load_account", { npub }) }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("load_account", { npub }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async eventToBech32(id: string, relays: string[]) : Promise<Result<string, null>> { async eventToBech32(
try { id: string,
return { status: "ok", data: await TAURI_INVOKE("event_to_bech32", { id, relays }) }; relays: string[],
} catch (e) { ): Promise<Result<string, null>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("event_to_bech32", { id, relays }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async userToBech32(key: string, relays: string[]) : Promise<Result<string, null>> { async userToBech32(
try { key: string,
return { status: "ok", data: await TAURI_INVOKE("user_to_bech32", { key, relays }) }; relays: string[],
} catch (e) { ): Promise<Result<string, null>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("user_to_bech32", { key, relays }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async toNpub(hex: string) : Promise<Result<string, null>> { async toNpub(hex: string): Promise<Result<string, null>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("to_npub", { hex }) }; return { status: "ok", data: await TAURI_INVOKE("to_npub", { hex }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async verifyNip05(key: string, nip05: string) : Promise<Result<boolean, string>> { async verifyNip05(
try { key: string,
return { status: "ok", data: await TAURI_INVOKE("verify_nip05", { key, nip05 }) }; nip05: string,
} catch (e) { ): Promise<Result<boolean, string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("verify_nip05", { key, nip05 }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async runNotification(accounts: string[]) : Promise<Result<null, null>> { async runNotification(accounts: string[]): Promise<Result<null, null>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("run_notification", { accounts }) }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("run_notification", { accounts }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getActivities(account: string, kind: string) : Promise<Result<string[], string>> { async getActivities(
try { account: string,
return { status: "ok", data: await TAURI_INVOKE("get_activities", { account, kind }) }; kind: string,
} catch (e) { ): Promise<Result<string[], string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("get_activities", { account, kind }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getCurrentUserProfile() : Promise<Result<string, string>> { async getCurrentUserProfile(): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_current_user_profile") }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("get_current_user_profile"),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getProfile(id: string) : Promise<Result<string, string>> { async getProfile(id: string): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_profile", { id }) }; return { status: "ok", data: await TAURI_INVOKE("get_profile", { id }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getContactList() : Promise<Result<string[], string>> { async getContactList(): Promise<Result<string[], string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_contact_list") }; return { status: "ok", data: await TAURI_INVOKE("get_contact_list") };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async setContactList(pubkeys: string[]) : Promise<Result<boolean, string>> { async setContactList(pubkeys: string[]): Promise<Result<boolean, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("set_contact_list", { pubkeys }) }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("set_contact_list", { pubkeys }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async createProfile(name: string, displayName: string, about: string, picture: string, banner: string, nip05: string, lud16: string, website: string) : Promise<Result<string, string>> { async createProfile(
try { name: string,
return { status: "ok", data: await TAURI_INVOKE("create_profile", { name, displayName, about, picture, banner, nip05, lud16, website }) }; displayName: string,
} catch (e) { about: string,
if(e instanceof Error) throw e; picture: string,
banner: string,
nip05: string,
lud16: string,
website: string,
): Promise<Result<string, string>> {
try {
return {
status: "ok",
data: await TAURI_INVOKE("create_profile", {
name,
displayName,
about,
picture,
banner,
nip05,
lud16,
website,
}),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async follow(id: string, alias: string | null) : Promise<Result<string, string>> { async follow(
try { id: string,
return { status: "ok", data: await TAURI_INVOKE("follow", { id, alias }) }; alias: string | null,
} catch (e) { ): Promise<Result<string, string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("follow", { id, alias }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async unfollow(id: string) : Promise<Result<string, string>> { async unfollow(id: string): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("unfollow", { id }) }; return { status: "ok", data: await TAURI_INVOKE("unfollow", { id }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getNstore(key: string) : Promise<Result<string, string>> { async getNstore(key: string): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_nstore", { key }) }; return { status: "ok", data: await TAURI_INVOKE("get_nstore", { key }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async setNstore(key: string, content: string) : Promise<Result<string, string>> { async setNstore(
try { key: string,
return { status: "ok", data: await TAURI_INVOKE("set_nstore", { key, content }) }; content: string,
} catch (e) { ): Promise<Result<string, string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("set_nstore", { key, content }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async setNwc(uri: string) : Promise<Result<boolean, string>> { async setNwc(uri: string): Promise<Result<boolean, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("set_nwc", { uri }) }; return { status: "ok", data: await TAURI_INVOKE("set_nwc", { uri }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async loadNwc() : Promise<Result<boolean, string>> { async loadNwc(): Promise<Result<boolean, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("load_nwc") }; return { status: "ok", data: await TAURI_INVOKE("load_nwc") };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getBalance() : Promise<Result<string, string>> { async getBalance(): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_balance") }; return { status: "ok", data: await TAURI_INVOKE("get_balance") };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async zapProfile(id: string, amount: string, message: string) : Promise<Result<boolean, string>> { async zapProfile(
try { id: string,
return { status: "ok", data: await TAURI_INVOKE("zap_profile", { id, amount, message }) }; amount: string,
} catch (e) { message: string,
if(e instanceof Error) throw e; ): Promise<Result<boolean, string>> {
try {
return {
status: "ok",
data: await TAURI_INVOKE("zap_profile", { id, amount, message }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async zapEvent(id: string, amount: string, message: string) : Promise<Result<boolean, string>> { async zapEvent(
try { id: string,
return { status: "ok", data: await TAURI_INVOKE("zap_event", { id, amount, message }) }; amount: string,
} catch (e) { message: string,
if(e instanceof Error) throw e; ): Promise<Result<boolean, string>> {
try {
return {
status: "ok",
data: await TAURI_INVOKE("zap_event", { id, amount, message }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async friendToFriend(npub: string) : Promise<Result<boolean, string>> { async friendToFriend(npub: string): Promise<Result<boolean, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("friend_to_friend", { npub }) }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("friend_to_friend", { npub }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getEvent(id: string) : Promise<Result<string, string>> { async getEvent(id: string): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_event", { id }) }; return { status: "ok", data: await TAURI_INVOKE("get_event", { id }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getReplies(id: string) : Promise<Result<string[], string>> { async getReplies(id: string): Promise<Result<string[], string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_replies", { id }) }; return { status: "ok", data: await TAURI_INVOKE("get_replies", { id }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getEventsBy(publicKey: string, asOf: string | null) : Promise<Result<string[], string>> { async getEventsBy(
try { publicKey: string,
return { status: "ok", data: await TAURI_INVOKE("get_events_by", { publicKey, asOf }) }; asOf: string | null,
} catch (e) { ): Promise<Result<string[], string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("get_events_by", { publicKey, asOf }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getLocalEvents(pubkeys: string[], until: string | null) : Promise<Result<string[], string>> { async getLocalEvents(
try { pubkeys: string[],
return { status: "ok", data: await TAURI_INVOKE("get_local_events", { pubkeys, until }) }; until: string | null,
} catch (e) { ): Promise<Result<string[], string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("get_local_events", { pubkeys, until }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getGlobalEvents(until: string | null) : Promise<Result<string[], string>> { async getGlobalEvents(
try { until: string | null,
return { status: "ok", data: await TAURI_INVOKE("get_global_events", { until }) }; ): Promise<Result<string[], string>> {
} catch (e) { try {
if(e instanceof Error) throw e; return {
status: "ok",
data: await TAURI_INVOKE("get_global_events", { until }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getHashtagEvents(hashtags: string[], until: string | null) : Promise<Result<string[], string>> { async getHashtagEvents(
try { hashtags: string[],
return { status: "ok", data: await TAURI_INVOKE("get_hashtag_events", { hashtags, until }) }; until: string | null,
} catch (e) { ): Promise<Result<string[], string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("get_hashtag_events", { hashtags, until }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async publish(content: string, tags: string[][]) : Promise<Result<string, string>> { async publish(
try { content: string,
return { status: "ok", data: await TAURI_INVOKE("publish", { content, tags }) }; tags: string[][],
} catch (e) { ): Promise<Result<string, string>> {
if(e instanceof Error) throw e; try {
return {
status: "ok",
data: await TAURI_INVOKE("publish", { content, tags }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async repost(raw: string) : Promise<Result<string, string>> { async repost(raw: string): Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("repost", { raw }) }; return { status: "ok", data: await TAURI_INVOKE("repost", { raw }) };
} catch (e) { } catch (e) {
if(e instanceof Error) throw e; if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async showInFolder(path: string) : Promise<void> { async showInFolder(path: string): Promise<void> {
await TAURI_INVOKE("show_in_folder", { path }); await TAURI_INVOKE("show_in_folder", { path });
}, },
async createColumn(label: string, x: number, y: number, width: number, height: number, url: string) : Promise<Result<string, string>> { async createColumn(
try { label: string,
return { status: "ok", data: await TAURI_INVOKE("create_column", { label, x, y, width, height, url }) }; x: number,
} catch (e) { y: number,
if(e instanceof Error) throw e; width: number,
height: number,
url: string,
): Promise<Result<string, string>> {
try {
return {
status: "ok",
data: await TAURI_INVOKE("create_column", {
label,
x,
y,
width,
height,
url,
}),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async closeColumn(label: string) : Promise<Result<boolean, null>> { async closeColumn(label: string): Promise<Result<boolean, null>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("close_column", { label }) }; return {
} catch (e) { status: "ok",
if(e instanceof Error) throw e; data: await TAURI_INVOKE("close_column", { label }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async repositionColumn(label: string, x: number, y: number) : Promise<Result<null, string>> { async repositionColumn(
try { label: string,
return { status: "ok", data: await TAURI_INVOKE("reposition_column", { label, x, y }) }; x: number,
} catch (e) { y: number,
if(e instanceof Error) throw e; ): Promise<Result<null, string>> {
try {
return {
status: "ok",
data: await TAURI_INVOKE("reposition_column", { label, x, y }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async resizeColumn(label: string, width: number, height: number) : Promise<Result<null, string>> { async resizeColumn(
try { label: string,
return { status: "ok", data: await TAURI_INVOKE("resize_column", { label, width, height }) }; width: number,
} catch (e) { height: number,
if(e instanceof Error) throw e; ): Promise<Result<null, string>> {
try {
return {
status: "ok",
data: await TAURI_INVOKE("resize_column", { label, width, height }),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async openWindow(label: string, title: string, url: string, width: number, height: number) : Promise<Result<null, string>> { async openWindow(
try { label: string,
return { status: "ok", data: await TAURI_INVOKE("open_window", { label, title, url, width, height }) }; title: string,
} catch (e) { url: string,
if(e instanceof Error) throw e; width: number,
height: number,
): Promise<Result<null, string>> {
try {
return {
status: "ok",
data: await TAURI_INVOKE("open_window", {
label,
title,
url,
width,
height,
}),
};
} catch (e) {
if (e instanceof Error) throw e;
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async setBadge(count: number) : Promise<void> { async setBadge(count: number): Promise<void> {
await TAURI_INVOKE("set_badge", { count }); await TAURI_INVOKE("set_badge", { count });
} },
} };
/** user-defined types **/ /** user-defined types **/
export type Account = { npub: string; nsec: string } export type Account = { npub: string; nsec: string };
export type Relays = { connected: string[]; read: string[] | null; write: string[] | null; both: string[] | null } export type Relays = {
connected: string[];
read: string[] | null;
write: string[] | null;
both: string[] | null;
};
/** tauri-specta globals **/ /** tauri-specta globals **/
import { invoke as TAURI_INVOKE } from "@tauri-apps/api/core"; import { invoke as TAURI_INVOKE } from "@tauri-apps/api/core";
import * as TAURI_API_EVENT from "@tauri-apps/api/event"; import * as TAURI_API_EVENT from "@tauri-apps/api/event";
import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webviewWindow"; import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webviewWindow";
type __EventObj__<T> = { type __EventObj__<T> = {
listen: ( listen: (
cb: TAURI_API_EVENT.EventCallback<T> cb: TAURI_API_EVENT.EventCallback<T>,
) => ReturnType<typeof TAURI_API_EVENT.listen<T>>; ) => ReturnType<typeof TAURI_API_EVENT.listen<T>>;
once: ( once: (
cb: TAURI_API_EVENT.EventCallback<T> cb: TAURI_API_EVENT.EventCallback<T>,
) => ReturnType<typeof TAURI_API_EVENT.once<T>>; ) => ReturnType<typeof TAURI_API_EVENT.once<T>>;
emit: T extends null emit: T extends null
? (payload?: T) => ReturnType<typeof TAURI_API_EVENT.emit> ? (payload?: T) => ReturnType<typeof TAURI_API_EVENT.emit>
@ -383,7 +574,7 @@ export type Result<T, E> =
| { status: "error"; error: E }; | { status: "error"; error: E };
function __makeEvents__<T extends Record<string, any>>( function __makeEvents__<T extends Record<string, any>>(
mappings: Record<keyof T, string> mappings: Record<keyof T, string>,
) { ) {
return new Proxy( return new Proxy(
{} as unknown as { {} as unknown as {
@ -413,8 +604,6 @@ function __makeEvents__<T extends Record<string, any>>(
}, },
}); });
}, },
} },
); );
} }

View File

@ -60,17 +60,10 @@ export const insertMention = (
name: contact.profile.name || contact.profile.display_name || "anon", name: contact.profile.name || contact.profile.display_name || "anon",
children: [text], children: [text],
}; };
const extraText = [
{
type: "paragraph",
children: [text],
},
];
// @ts-ignore, idk // @ts-ignore, idk
ReactEditor.focus(editor); ReactEditor.focus(editor);
Transforms.insertNodes(editor, mention); Transforms.insertNodes(editor, mention);
Transforms.insertNodes(editor, extraText);
}; };
export const insertNostrEvent = ( export const insertNostrEvent = (