Files
lume/packages/system/src/query.ts
2024-06-19 21:02:33 +07:00

407 lines
9.1 KiB
TypeScript

import type { LumeColumn, Metadata, NostrEvent, Relay } from "@lume/types";
import { resolveResource } from "@tauri-apps/api/path";
import { open } from "@tauri-apps/plugin-dialog";
import { readFile, readTextFile } from "@tauri-apps/plugin-fs";
import { relaunch } from "@tauri-apps/plugin-process";
import { nip19 } from "nostr-tools";
import { type Result, type RichEvent, commands } from "./commands";
import { LumeEvent } from "./event";
export class NostrQuery {
static #toLumeEvents(richEvents: RichEvent[]) {
const events = richEvents.map((item) => {
const nostrEvent: NostrEvent = JSON.parse(item.raw);
if (item.parsed) {
nostrEvent.meta = item.parsed;
} else {
nostrEvent.meta = null;
}
const lumeEvent = new LumeEvent(nostrEvent);
return lumeEvent;
});
return events;
}
static async upload(filePath?: string) {
const allowExts = [
"png",
"jpeg",
"jpg",
"gif",
"mp4",
"mp3",
"webm",
"mkv",
"avi",
"mov",
];
const selected =
filePath ||
(
await open({
multiple: false,
filters: [
{
name: "Media",
extensions: allowExts,
},
],
})
).path;
// User cancelled action
if (!selected) return null;
try {
const file = await readFile(selected);
const blob = new Blob([file]);
const data = new FormData();
data.append("fileToUpload", blob);
data.append("submit", "Upload Image");
const res = await fetch("https://nostr.build/api/v2/upload/files", {
method: "POST",
body: data,
});
if (!res.ok) return null;
const json = await res.json();
const content = json.data[0];
return content.url as string;
} catch (e) {
throw new Error(String(e));
}
}
static async getNotifications() {
const query = await commands.getNotifications();
if (query.status === "ok") {
const data = query.data.map((item) => JSON.parse(item) as NostrEvent);
const events = data.map((ev) => new LumeEvent(ev));
return events;
} else {
console.error(query.error);
return [];
}
}
static async getProfile(pubkey: string) {
const normalize = pubkey.replace("nostr:", "").replace(/[^\w\s]/gi, "");
const query = await commands.getProfile(normalize);
if (query.status === "ok") {
const profile: Metadata = JSON.parse(query.data);
return profile;
} else {
return null;
}
}
static async getEvent(id: string, hint?: string) {
// Validate ID
const normalizeId: string = id
.replace("nostr:", "")
.replace(/[^\w\s]/gi, "");
// Define query
let query: Result<RichEvent, string>;
let relayHint: string = hint;
if (normalizeId.startsWith("nevent1")) {
const decoded = nip19.decode(normalizeId);
if (decoded.type === "nevent") relayHint = decoded.data.relays[0];
}
// Build query
if (relayHint) {
try {
const url = new URL(relayHint);
query = await commands.getEventFrom(normalizeId, url.toString());
} catch {
query = await commands.getEvent(normalizeId);
}
} else {
query = await commands.getEvent(normalizeId);
}
if (query.status === "ok") {
const data = query.data;
const raw = JSON.parse(data.raw) as NostrEvent;
if (data?.parsed) {
raw.meta = data.parsed;
}
const event = new LumeEvent(raw);
return event;
} else {
console.log("[getEvent]: ", query.error);
return null;
}
}
static async getRepostEvent(event: LumeEvent) {
try {
const embed: NostrEvent = JSON.parse(event.content);
const query = await commands.getEventMeta(embed.content);
if (query.status === "ok") {
embed.meta = query.data;
const lumeEvent = new LumeEvent(embed);
return lumeEvent;
}
} catch {
const query = await commands.getEvent(event.repostId);
if (query.status === "ok") {
const data = query.data;
const raw = JSON.parse(data.raw) as NostrEvent;
if (data?.parsed) {
raw.meta = data.parsed;
}
const event = new LumeEvent(raw);
return event;
} else {
console.log("[getRepostEvent]: ", query.error);
return null;
}
}
}
static async getUserEvents(pubkey: string, asOf?: number) {
const until: string = asOf && asOf > 0 ? asOf.toString() : undefined;
const query = await commands.getEventsBy(pubkey, until);
if (query.status === "ok") {
const data = NostrQuery.#toLumeEvents(query.data);
return data;
} else {
return [];
}
}
static async getLocalEvents(asOf?: number) {
const until: string = asOf && asOf > 0 ? asOf.toString() : undefined;
const query = await commands.getLocalEvents(until);
if (query.status === "ok") {
const data = NostrQuery.#toLumeEvents(query.data);
return data;
} else {
return [];
}
}
static async getGroupEvents(pubkeys: string[], asOf?: number) {
const until: string = asOf && asOf > 0 ? asOf.toString() : undefined;
const query = await commands.getGroupEvents(pubkeys, until);
if (query.status === "ok") {
const data = NostrQuery.#toLumeEvents(query.data);
return data;
} else {
return [];
}
}
static async getGlobalEvents(asOf?: number) {
const until: string = asOf && asOf > 0 ? asOf.toString() : undefined;
const query = await commands.getGlobalEvents(until);
if (query.status === "ok") {
const data = NostrQuery.#toLumeEvents(query.data);
return data;
} else {
return [];
}
}
static async getHashtagEvents(hashtags: string[], asOf?: number) {
const until: string = asOf && asOf > 0 ? asOf.toString() : undefined;
const nostrTags = hashtags.map((tag) => tag.replace("#", ""));
const query = await commands.getHashtagEvents(nostrTags, until);
if (query.status === "ok") {
const data = NostrQuery.#toLumeEvents(query.data);
return data;
} else {
return [];
}
}
static async verifyNip05(pubkey: string, nip05?: string) {
const query = await commands.verifyNip05(pubkey, nip05);
if (query.status === "ok") {
return query.data;
} else {
return false;
}
}
static async getNstore(key: string) {
const query = await commands.getNstore(key);
if (query.status === "ok") {
const data = query.data ? JSON.parse(query.data) : null;
return data;
} else {
return null;
}
}
static async setNstore(key: string, value: string) {
const query = await commands.setNstore(key, value);
if (query.status === "ok") {
return query.data;
} else {
throw new Error(query.error);
}
}
static async getUserSettings() {
const query = await commands.getSettings();
if (query.status === "ok") {
return query.data;
} else {
return query.error;
}
}
static async setUserSettings(newSettings: string) {
const query = await commands.setNewSettings(newSettings);
if (query.status === "ok") {
return query.data;
} else {
return query.error;
}
}
static async getColumns() {
const key = "lume:columns";
const systemPath = "resources/system_columns.json";
const resourcePath = await resolveResource(systemPath);
const resourceFile = await readTextFile(resourcePath);
const systemColumns: LumeColumn[] = JSON.parse(resourceFile);
const query = await commands.getNstore(key);
try {
if (query.status === "ok") {
const columns: LumeColumn[] = JSON.parse(query.data);
if (!columns?.length) {
return systemColumns;
}
// Filter "open" column
// Reason: deprecated
return columns.filter((col) => col.label !== "open");
} else {
return systemColumns;
}
} catch {
return systemColumns;
}
}
static async setColumns(columns: LumeColumn[]) {
const key = "lume:columns";
const content = JSON.stringify(columns);
const query = await commands.setNstore(key, content);
if (query.status === "ok") {
return query.data;
} else {
throw new Error(query.error);
}
}
static async getRelays() {
const query = await commands.getRelays();
if (query.status === "ok") {
return query.data;
} else {
throw new Error(query.error);
}
}
static async connectRelay(url: string) {
const relayUrl = new URL(url);
if (relayUrl.protocol === "wss:" || relayUrl.protocol === "ws:") {
const query = await commands.connectRelay(relayUrl.toString());
if (query.status === "ok") {
return query.data;
} else {
throw new Error(query.error);
}
}
}
static async removeRelay(url: string) {
const relayUrl = new URL(url);
if (relayUrl.protocol === "wss:" || relayUrl.protocol === "ws:") {
const query = await commands.removeRelay(relayUrl.toString());
if (query.status === "ok") {
return query.data;
} else {
throw new Error(query.error);
}
}
}
static async getBootstrapRelays() {
const query = await commands.getBootstrapRelays();
if (query.status === "ok") {
const relays: Relay[] = [];
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);
}
}
}