mirror of
https://github.com/luminous-devs/lume.git
synced 2024-10-01 09:21:07 +00:00
Customize Bootstrap Relays (#205)
* feat: add bootstrap relays file * feat: add save bootstrap relays command * feat: add customize bootstrap relays screen
This commit is contained in:
parent
b396c8a695
commit
90342c552f
132
apps/desktop2/src/routes/bootstrap-relays.tsx
Normal file
132
apps/desktop2/src/routes/bootstrap-relays.tsx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { CancelIcon, PlusIcon } from "@lume/icons";
|
||||||
|
import { NostrQuery } from "@lume/system";
|
||||||
|
import { Relay } from "@lume/types";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/bootstrap-relays")({
|
||||||
|
loader: async () => {
|
||||||
|
const bootstrapRelays = await NostrQuery.getBootstrapRelays();
|
||||||
|
return bootstrapRelays;
|
||||||
|
},
|
||||||
|
component: Screen,
|
||||||
|
});
|
||||||
|
|
||||||
|
function Screen() {
|
||||||
|
const bootstrapRelays = Route.useLoaderData();
|
||||||
|
const { register, reset, handleSubmit } = useForm();
|
||||||
|
|
||||||
|
const [relays, setRelays] = useState<Relay[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const removeRelay = (url: string) => {
|
||||||
|
setRelays((prev) => prev.filter((relay) => relay.url !== url));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async (data: { url: string; purpose: string }) => {
|
||||||
|
try {
|
||||||
|
const relay: Relay = { url: data.url, purpose: data.purpose };
|
||||||
|
setRelays((prev) => [...prev, relay]);
|
||||||
|
reset();
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(String(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
await NostrQuery.saveBootstrapRelays(relays);
|
||||||
|
} catch (e) {
|
||||||
|
setIsLoading(false);
|
||||||
|
toast.error(String(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRelays(bootstrapRelays);
|
||||||
|
}, [bootstrapRelays]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col justify-center items-center h-screen w-screen">
|
||||||
|
<div className="mx-auto max-w-sm lg:max-w-lg w-full">
|
||||||
|
<div className="h-11 text-center">
|
||||||
|
<h1 className="font-semibold">Customize Bootstrap Relays</h1>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full flex-col bg-white rounded-xl shadow-primary backdrop-blur-lg dark:bg-white/20 dark:ring-1 ring-neutral-800/50 px-2">
|
||||||
|
{relays.map((relay) => (
|
||||||
|
<div
|
||||||
|
key={relay.url}
|
||||||
|
className="flex justify-between items-center h-11"
|
||||||
|
>
|
||||||
|
<div className="inline-flex items-center gap-2 text-sm font-medium">
|
||||||
|
{relay.url}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{relay.purpose?.length ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="h-7 w-max rounded-md inline-flex items-center justify-center px-2 uppercase text-xs font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
|
>
|
||||||
|
{relay.purpose}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => removeRelay(relay.url)}
|
||||||
|
className="inline-flex items-center justify-center size-7 rounded-md text-neutral-700 dark:text-white/20 hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
|
>
|
||||||
|
<CancelIcon className="size-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="flex items-center h-14 border-t border-neutral-100 dark:border-white/5">
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
className="w-full flex items-center gap-2 mb-0"
|
||||||
|
>
|
||||||
|
<div className="flex-1 flex items-center gap-2 rounded-lg border border-neutral-300 dark:border-white/20">
|
||||||
|
<input
|
||||||
|
{...register("url", {
|
||||||
|
required: true,
|
||||||
|
minLength: 1,
|
||||||
|
})}
|
||||||
|
name="url"
|
||||||
|
placeholder="wss://..."
|
||||||
|
spellCheck={false}
|
||||||
|
className="h-9 flex-1 bg-transparent border-none rounded-l-lg px-3 placeholder:text-neutral-500 dark:placeholder:text-neutral-400"
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
{...register("purpose")}
|
||||||
|
className="flex-1 h-9 p-0 m-0 text-sm bg-transparent border-none ring-0 outline-none focus:outline-none focus:ring-0"
|
||||||
|
>
|
||||||
|
<option value="read">Read</option>
|
||||||
|
<option value="write">Write</option>
|
||||||
|
<option value="">Both</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="shrink-0 inline-flex h-9 w-14 px-2 items-center justify-center rounded-lg bg-black/20 dark:bg-white/20 font-medium text-sm text-white hover:bg-blue-500 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<PlusIcon className="size-7" />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => save()}
|
||||||
|
disabled={isLoading}
|
||||||
|
className="mt-4 inline-flex h-10 w-full shrink-0 items-center justify-center rounded-lg bg-blue-500 text-sm font-semibold text-white hover:bg-blue-600 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isLoading ? <Spinner /> : "Save & Relaunch"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { PlusIcon } from "@lume/icons";
|
import { PlusIcon, RelayIcon } from "@lume/icons";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
import { checkForAppUpdates } from "@lume/utils";
|
import { checkForAppUpdates } from "@lume/utils";
|
||||||
@ -59,16 +59,17 @@ function Screen() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className="relative flex h-full w-full items-center justify-center"
|
className="h-full w-full flex flex-col items-center justify-between"
|
||||||
>
|
>
|
||||||
<div className="relative z-20 flex flex-col items-center gap-16">
|
<div className="w-full flex-1 flex items-end justify-center px-4">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h2 className="text-xl text-neutral-700 dark:text-neutral-300">
|
<h2 className="mb-1 text-lg text-neutral-700 dark:text-neutral-300">
|
||||||
{currentDate}
|
{currentDate}
|
||||||
</h2>
|
</h2>
|
||||||
<h2 className="text-2xl font-semibold">Welcome back!</h2>
|
<h2 className="text-2xl font-semibold">Welcome back!</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap px-3 items-center justify-center gap-6">
|
</div>
|
||||||
|
<div className="w-full flex-1 flex flex-wrap items-center justify-center gap-6 px-3">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="inline-flex size-6 items-center justify-center">
|
<div className="inline-flex size-6 items-center justify-center">
|
||||||
<Spinner className="size-6" />
|
<Spinner className="size-6" />
|
||||||
@ -100,6 +101,16 @@ function Screen() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full flex-1 flex items-end justify-center pb-4 px-4">
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
to="/bootstrap-relays"
|
||||||
|
className="inline-flex items-center justify-center gap-2 bg-black/5 dark:bg-white/5 hover:bg-black/10 dark:hover:bg-white/10 rounded-full h-8 w-36 px-2 text-xs font-medium text-neutral-700 dark:text-white/40"
|
||||||
|
>
|
||||||
|
<RelayIcon className="size-4" />
|
||||||
|
Bootstrap Relays
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { CancelIcon, PlusIcon } from "@lume/icons";
|
import { CancelIcon, PlusIcon } from "@lume/icons";
|
||||||
import { NostrQuery } from "@lume/system";
|
import { NostrQuery } from "@lume/system";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
@ -15,22 +15,32 @@ export const Route = createFileRoute("/settings/relay")({
|
|||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
const relayList = Route.useLoaderData();
|
const relayList = Route.useLoaderData();
|
||||||
const [relays, setRelays] = useState(relayList.connected);
|
|
||||||
|
|
||||||
const { register, reset, handleSubmit } = useForm();
|
const { register, reset, handleSubmit } = useForm();
|
||||||
|
|
||||||
|
const [relays, setRelays] = useState<string[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async (data: { url: string }) => {
|
const onSubmit = async (data: { url: string }) => {
|
||||||
try {
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
const add = await NostrQuery.connectRelay(data.url);
|
const add = await NostrQuery.connectRelay(data.url);
|
||||||
|
|
||||||
if (add) {
|
if (add) {
|
||||||
setRelays((prev) => [...prev, data.url]);
|
setRelays((prev) => [...prev, data.url]);
|
||||||
|
setIsLoading(false);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
setIsLoading(false);
|
||||||
toast.error(String(e));
|
toast.error(String(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRelays(relayList.connected);
|
||||||
|
}, [relayList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-xl">
|
<div className="mx-auto w-full max-w-xl">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
@ -79,6 +89,7 @@ function Screen() {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
disabled={isLoading}
|
||||||
className="shrink-0 inline-flex h-9 w-16 px-2 items-center justify-center rounded-lg bg-black/20 dark:bg-white/20 font-medium text-sm text-white hover:bg-blue-500 disabled:opacity-50"
|
className="shrink-0 inline-flex h-9 w-16 px-2 items-center justify-center rounded-lg bg-black/20 dark:bg-white/20 font-medium text-sm text-white hover:bg-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<PlusIcon className="size-7" />
|
<PlusIcon className="size-7" />
|
||||||
|
@ -1,370 +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.
|
/** user-defined commands **/
|
||||||
|
|
||||||
/** user-defined commands **/
|
export const commands = {
|
||||||
|
async getRelays(): Promise<Result<Relays, null>> {
|
||||||
export const commands = {
|
try {
|
||||||
async getRelays() : Promise<Result<Relays, null>> {
|
|
||||||
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 getBootstrapRelays(): Promise<Result<string[], null>> {
|
||||||
try {
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("get_bootstrap_relays") };
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async saveBootstrapRelays(relays: string): Promise<Result<null, string>> {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
status: "ok",
|
||||||
|
data: await TAURI_INVOKE("save_bootstrap_relays", { relays }),
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getAccounts(): Promise<Result<string[], string>> {
|
||||||
|
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 connectRemoteAccount(uri: string) : Promise<Result<string, string>> {
|
async connectRemoteAccount(uri: string): Promise<Result<string, string>> {
|
||||||
try {
|
try {
|
||||||
return { status: "ok", data: await TAURI_INVOKE("connect_remote_account", { uri }) };
|
return {
|
||||||
} catch (e) {
|
status: "ok",
|
||||||
if(e instanceof Error) throw e;
|
data: await TAURI_INVOKE("connect_remote_account", { 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, bunker: string | null) : Promise<Result<boolean, string>> {
|
async loadAccount(
|
||||||
try {
|
npub: string,
|
||||||
return { status: "ok", data: await TAURI_INVOKE("load_account", { npub, bunker }) };
|
bunker: string | null,
|
||||||
} catch (e) {
|
): Promise<Result<boolean, string>> {
|
||||||
if(e instanceof Error) throw e;
|
try {
|
||||||
|
return {
|
||||||
|
status: "ok",
|
||||||
|
data: await TAURI_INVOKE("load_account", { npub, bunker }),
|
||||||
|
};
|
||||||
|
} 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 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 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 getNotifications() : Promise<Result<string[], string>> {
|
async getNotifications(): Promise<Result<string[], string>> {
|
||||||
try {
|
try {
|
||||||
return { status: "ok", data: await TAURI_INVOKE("get_notifications") };
|
return { status: "ok", data: await TAURI_INVOKE("get_notifications") };
|
||||||
} 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 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 events **/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** user-defined statics **/
|
|
||||||
|
|
||||||
|
/** user-defined events **/
|
||||||
|
|
||||||
|
/** user-defined statics **/
|
||||||
|
|
||||||
/** 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>
|
||||||
@ -376,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 {
|
||||||
@ -406,8 +604,6 @@ function __makeEvents__<T extends Record<string, any>>(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
import { LumeColumn, Metadata, NostrEvent, Settings } from "@lume/types";
|
import { LumeColumn, Metadata, NostrEvent, Relay, Settings } from "@lume/types";
|
||||||
import { commands } from "./commands";
|
import { commands } from "./commands";
|
||||||
import { resolveResource } from "@tauri-apps/api/path";
|
import { resolveResource } from "@tauri-apps/api/path";
|
||||||
import { readFile, readTextFile } from "@tauri-apps/plugin-fs";
|
import { readFile, readTextFile } from "@tauri-apps/plugin-fs";
|
||||||
@ -6,6 +6,7 @@ import { isPermissionGranted } from "@tauri-apps/plugin-notification";
|
|||||||
import { open } from "@tauri-apps/plugin-dialog";
|
import { open } from "@tauri-apps/plugin-dialog";
|
||||||
import { dedupEvents } from "./dedup";
|
import { dedupEvents } from "./dedup";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { relaunch } from "@tauri-apps/plugin-process";
|
||||||
|
|
||||||
enum NSTORE_KEYS {
|
enum NSTORE_KEYS {
|
||||||
settings = "lume_user_settings",
|
settings = "lume_user_settings",
|
||||||
@ -305,4 +306,38 @@ export class NostrQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getBootstrapRelays() {
|
||||||
|
const query = await commands.getBootstrapRelays();
|
||||||
|
|
||||||
|
if (query.status === "ok") {
|
||||||
|
let relays: Relay[] = [];
|
||||||
|
console.log(query.data);
|
||||||
|
|
||||||
|
for (const item of query.data) {
|
||||||
|
const line = item.split(",");
|
||||||
|
const url = line[0];
|
||||||
|
const purpose = line[1] ?? "";
|
||||||
|
|
||||||
|
relays.push({ url, purpose });
|
||||||
|
}
|
||||||
|
|
||||||
|
return relays;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async saveBootstrapRelays(relays: Relay[]) {
|
||||||
|
const text = relays
|
||||||
|
.map((relay) => Object.values(relay).join(","))
|
||||||
|
.join("\n");
|
||||||
|
const query = await commands.saveBootstrapRelays(text);
|
||||||
|
|
||||||
|
if (query.status === "ok") {
|
||||||
|
return await relaunch();
|
||||||
|
} else {
|
||||||
|
throw new Error(query.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
packages/types/index.d.ts
vendored
5
packages/types/index.d.ts
vendored
@ -179,3 +179,8 @@ export interface Relays {
|
|||||||
write: string[];
|
write: string[];
|
||||||
both: string[];
|
both: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Relay {
|
||||||
|
url: string;
|
||||||
|
purpose: "read" | "write" | string;
|
||||||
|
}
|
||||||
|
2
src-tauri/resources/relays.txt
Normal file
2
src-tauri/resources/relays.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
wss://nostr.wine,
|
||||||
|
wss://relay.nostr.net,
|
@ -15,8 +15,12 @@ extern crate cocoa;
|
|||||||
extern crate objc;
|
extern crate objc;
|
||||||
|
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use std::fs;
|
use std::{
|
||||||
use tauri::Manager;
|
fs,
|
||||||
|
io::{self, BufRead},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
use tauri::{path::BaseDirectory, Manager};
|
||||||
use tauri_nspanel::ManagerExt;
|
use tauri_nspanel::ManagerExt;
|
||||||
use tauri_plugin_decorum::WebviewWindowExt;
|
use tauri_plugin_decorum::WebviewWindowExt;
|
||||||
|
|
||||||
@ -40,6 +44,8 @@ fn main() {
|
|||||||
nostr::relay::get_relays,
|
nostr::relay::get_relays,
|
||||||
nostr::relay::connect_relay,
|
nostr::relay::connect_relay,
|
||||||
nostr::relay::remove_relay,
|
nostr::relay::remove_relay,
|
||||||
|
nostr::relay::get_bootstrap_relays,
|
||||||
|
nostr::relay::save_bootstrap_relays,
|
||||||
nostr::keys::get_accounts,
|
nostr::keys::get_accounts,
|
||||||
nostr::keys::create_account,
|
nostr::keys::create_account,
|
||||||
nostr::keys::save_account,
|
nostr::keys::save_account,
|
||||||
@ -145,22 +151,34 @@ fn main() {
|
|||||||
Err(_) => ClientBuilder::default().opts(opts).build(),
|
Err(_) => ClientBuilder::default().opts(opts).build(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add bootstrap relays
|
// Get bootstrap relays
|
||||||
client
|
let relays_path = app
|
||||||
.add_relay("wss://relay.nostr.net")
|
.path()
|
||||||
.await
|
.resolve("resources/relays.txt", BaseDirectory::Resource)
|
||||||
.expect("Cannot connect to relay.nostr.net, please try again later.");
|
.expect("Bootstrap relays not found.");
|
||||||
client
|
let file = std::fs::File::open(&relays_path).unwrap();
|
||||||
.add_relay("wss://relay.damus.io")
|
let lines = io::BufReader::new(file).lines();
|
||||||
.await
|
|
||||||
.expect("Cannot connect to relay.damus.io, please try again later.");
|
// Add bootstrap relays to relay pool
|
||||||
client
|
for line in lines.flatten() {
|
||||||
.add_relay_with_opts(
|
if let Some((relay, option)) = line.split_once(',') {
|
||||||
"wss://directory.yabu.me/",
|
match RelayMetadata::from_str(option) {
|
||||||
RelayOptions::new().read(true).write(false),
|
Ok(meta) => {
|
||||||
)
|
println!("connecting to bootstrap relay...: {} - {}", relay, meta);
|
||||||
.await
|
let opts = if meta == RelayMetadata::Read {
|
||||||
.expect("Cannot connect to directory.yabu.me, please try again later.");
|
RelayOptions::new().read(true).write(false)
|
||||||
|
} else {
|
||||||
|
RelayOptions::new().write(true).read(false)
|
||||||
|
};
|
||||||
|
let _ = client.add_relay_with_opts(relay, opts).await;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
println!("connecting to bootstrap relay...: {}", relay);
|
||||||
|
let _ = client.add_relay(relay).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Connect
|
// Connect
|
||||||
client.connect().await;
|
client.connect().await;
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::{self, BufRead, Write},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Nostr;
|
use crate::Nostr;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use specta::Type;
|
use specta::Type;
|
||||||
use tauri::State;
|
use tauri::{path::BaseDirectory, Manager, State};
|
||||||
|
|
||||||
#[derive(Serialize, Type)]
|
#[derive(Serialize, Type)]
|
||||||
pub struct Relays {
|
pub struct Relays {
|
||||||
@ -103,3 +108,42 @@ pub async fn remove_relay(relay: &str, state: State<'_, Nostr>) -> Result<bool,
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub fn get_bootstrap_relays(app: tauri::AppHandle) -> Result<Vec<String>, ()> {
|
||||||
|
let relays_path = app
|
||||||
|
.path()
|
||||||
|
.resolve("resources/relays.txt", BaseDirectory::Resource)
|
||||||
|
.expect("Bootstrap relays not found.");
|
||||||
|
|
||||||
|
let file = std::fs::File::open(&relays_path).unwrap();
|
||||||
|
let lines = io::BufReader::new(file).lines();
|
||||||
|
|
||||||
|
let mut relays = Vec::new();
|
||||||
|
|
||||||
|
for line in lines.flatten() {
|
||||||
|
relays.push(line.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(relays)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub fn save_bootstrap_relays(relays: &str, app: tauri::AppHandle) -> Result<(), String> {
|
||||||
|
let relays_path = app
|
||||||
|
.path()
|
||||||
|
.resolve("resources/relays.txt", BaseDirectory::Resource)
|
||||||
|
.expect("Bootstrap relays not found.");
|
||||||
|
|
||||||
|
let mut file = fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.open(&relays_path)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match file.write_all(relays.as_bytes()) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(_) => Err("Cannot save bootstrap relays, please try again later.".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user