wip: migrate frontend to new backend
This commit is contained in:
@@ -11,9 +11,11 @@ export class Ark {
|
||||
public async verify_signer() {
|
||||
try {
|
||||
const cmd: string = await invoke("verify_signer");
|
||||
if (!cmd) return false;
|
||||
this.account.pubkey = cmd;
|
||||
return true;
|
||||
if (cmd) {
|
||||
this.account.pubkey = cmd;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
console.error(String(e));
|
||||
}
|
||||
|
||||
@@ -5,48 +5,33 @@ import {
|
||||
RefreshIcon,
|
||||
TrashIcon,
|
||||
} from "@lume/icons";
|
||||
import { useColumn } from "@lume/storage";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { InterestModal } from "./interestModal";
|
||||
import { useColumnContext } from "./provider";
|
||||
|
||||
export function ColumnHeader({
|
||||
id,
|
||||
title,
|
||||
queryKey,
|
||||
}: {
|
||||
id: number;
|
||||
title: string;
|
||||
queryKey?: string[];
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { moveColumn, removeColumn } = useColumnContext();
|
||||
const { move, remove } = useColumn();
|
||||
|
||||
const column = useColumnContext();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const refresh = async () => {
|
||||
if (queryKey) await queryClient.refetchQueries({ queryKey });
|
||||
};
|
||||
|
||||
const moveLeft = async () => {
|
||||
moveColumn(id, "left");
|
||||
};
|
||||
|
||||
const moveRight = async () => {
|
||||
moveColumn(id, "right");
|
||||
};
|
||||
|
||||
const deleteWidget = async () => {
|
||||
await removeColumn(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<div className="flex items-center justify-center gap-2 px-3 w-full border-b h-11 shrink-0 border-neutral-100 dark:border-neutral-900">
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div className="inline-flex items-center gap-1.5">
|
||||
<div className="text-[13px] font-medium">{title}</div>
|
||||
<div className="text-[13px] font-medium">{column.title}</div>
|
||||
<ChevronDownIcon className="size-5" />
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
@@ -65,18 +50,10 @@ export function ColumnHeader({
|
||||
{t("global.refresh")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
{queryKey?.[0] === "foryou-9998" ? (
|
||||
<DropdownMenu.Item asChild>
|
||||
<InterestModal
|
||||
queryKey={queryKey}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
/>
|
||||
</DropdownMenu.Item>
|
||||
) : null}
|
||||
<DropdownMenu.Item asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={moveLeft}
|
||||
onClick={() => move(column.id, "left")}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
<MoveLeftIcon className="size-4" />
|
||||
@@ -86,7 +63,7 @@ export function ColumnHeader({
|
||||
<DropdownMenu.Item asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={moveRight}
|
||||
onClick={() => move(column.id, "right")}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
<MoveRightIcon className="size-4" />
|
||||
@@ -97,7 +74,7 @@ export function ColumnHeader({
|
||||
<DropdownMenu.Item asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={deleteWidget}
|
||||
onClick={() => remove(column.id)}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium text-red-500 rounded-lg h-9 hover:bg-red-500 hover:text-red-50 focus:outline-none"
|
||||
>
|
||||
<TrashIcon className="size-4" />
|
||||
|
||||
@@ -2,9 +2,11 @@ import { Route } from "react-router-dom";
|
||||
import { ColumnContent } from "./content";
|
||||
import { ColumnHeader } from "./header";
|
||||
import { ColumnLiveWidget } from "./live";
|
||||
import { ColumnProvider } from "./provider";
|
||||
import { ColumnRoot } from "./root";
|
||||
|
||||
export const Column = {
|
||||
Provider: ColumnProvider,
|
||||
Root: ColumnRoot,
|
||||
Live: ColumnLiveWidget,
|
||||
Header: ColumnHeader,
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
import { ArrowLeftIcon, EditInterestIcon, LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { TOPICS, cn } from "@lume/utils";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export function InterestModal({
|
||||
queryKey,
|
||||
className,
|
||||
children,
|
||||
}: { queryKey: string[]; className?: string; children?: ReactNode }) {
|
||||
const storage = useStorage();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [t] = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [hashtags, setHashtags] = useState(storage.interests?.hashtags || []);
|
||||
|
||||
const toggleHashtag = (item: string) => {
|
||||
const arr = hashtags.includes(item)
|
||||
? hashtags.filter((i) => i !== item)
|
||||
: [...hashtags, item];
|
||||
setHashtags(arr);
|
||||
};
|
||||
|
||||
const toggleAll = (item: string[]) => {
|
||||
const sets = new Set([...hashtags, ...item]);
|
||||
setHashtags([...sets]);
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const save = await storage.createSetting(
|
||||
"interests",
|
||||
JSON.stringify({ hashtags }),
|
||||
);
|
||||
|
||||
if (save) {
|
||||
storage.interests = { hashtags, users: [], words: [] };
|
||||
await queryClient.refetchQueries({ queryKey });
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
setOpen(false);
|
||||
} catch (e) {
|
||||
setLoading(false);
|
||||
toast.error(String(e));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={setOpen}>
|
||||
<Dialog.Trigger
|
||||
className={cn(
|
||||
"inline-flex items-center gap-3 px-3 rounded-lg h-9 focus:outline-none",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<>
|
||||
<EditInterestIcon className="size-4" />
|
||||
{t("interests.edit")}
|
||||
</>
|
||||
)}
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="h-20 absolute top-0 left-0 w-full"
|
||||
/>
|
||||
<div className="relative w-full max-w-xl xl:max-w-2xl bg-white h-[600px] xl:h-[700px] rounded-xl dark:bg-black overflow-hidden">
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<div className="h-16 shrink-0 px-8 border-b border-neutral-100 dark:border-neutral-900 flex w-full items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="font-semibold">{t("interests.edit")}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex-1 min-h-0 flex flex-col justify-between">
|
||||
<div className="flex-1 min-h-0 overflow-y-auto px-8 py-8">
|
||||
<div className="flex flex-col gap-8">
|
||||
{TOPICS.map((topic) => (
|
||||
<div key={topic.title} className="flex flex-col gap-4">
|
||||
<div className="w-full flex items-center justify-between">
|
||||
<div className="inline-flex items-center gap-2.5">
|
||||
<img
|
||||
src={topic.icon}
|
||||
alt={topic.title}
|
||||
className="size-8 object-cover rounded-lg"
|
||||
/>
|
||||
<h3 className="text-lg font-semibold">
|
||||
{topic.title}
|
||||
</h3>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleAll(topic.content)}
|
||||
className="text-sm font-medium text-blue-500"
|
||||
>
|
||||
{t("interests.followAll")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{topic.content.map((hashtag) => (
|
||||
<button
|
||||
key={hashtag}
|
||||
type="button"
|
||||
onClick={() => toggleHashtag(hashtag)}
|
||||
className={cn(
|
||||
"inline-flex items-center rounded-full bg-neutral-100 dark:bg-neutral-900 border border-transparent px-2 py-1 text-sm font-medium",
|
||||
hashtags.includes(hashtag)
|
||||
? "border-blue-500 text-blue-500"
|
||||
: "",
|
||||
)}
|
||||
>
|
||||
{hashtag}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-16 shrink-0 w-full flex items-center px-8 justify-center gap-2 border-t border-neutral-100 dark:border-neutral-900 bg-neutral-50 dark:bg-neutral-950">
|
||||
<Dialog.Close className="inline-flex h-9 flex-1 gap-2 shrink-0 items-center justify-center rounded-lg bg-neutral-100 font-medium dark:bg-neutral-900 dark:hover:bg-neutral-800 hover:bg-blue-200">
|
||||
<ArrowLeftIcon className="size-4" />
|
||||
{t("global.cancel")}
|
||||
</Dialog.Close>
|
||||
<button
|
||||
type="button"
|
||||
onClick={submit}
|
||||
className="inline-flex h-9 flex-1 shrink-0 items-center justify-center rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600 disabled:opacity-50"
|
||||
>
|
||||
{loading ? (
|
||||
<LoaderIcon className="size-4 animate-spin" />
|
||||
) : (
|
||||
t("global.save")
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import { ArrowUpIcon } from "@lume/icons";
|
||||
import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
|
||||
export function ColumnLiveWidget({
|
||||
filter,
|
||||
|
||||
@@ -1,136 +1,14 @@
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { IColumn } from "@lume/types";
|
||||
import { COL_TYPES } from "@lume/utils";
|
||||
import {
|
||||
type MutableRefObject,
|
||||
type ReactNode,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { toast } from "sonner";
|
||||
import { type VListHandle } from "virtua";
|
||||
import { LumeColumn } from "@lume/types";
|
||||
import { ReactNode, createContext, useContext } from "react";
|
||||
|
||||
type ColumnContext = {
|
||||
columns: IColumn[];
|
||||
vlistRef: MutableRefObject<VListHandle>;
|
||||
addColumn: (column: IColumn) => Promise<void>;
|
||||
removeColumn: (id: number) => Promise<void>;
|
||||
moveColumn: (id: number, position: "left" | "right") => void;
|
||||
updateColumn: (id: number, title: string, content: string) => Promise<void>;
|
||||
loadAllColumns: () => Promise<IColumn[]>;
|
||||
};
|
||||
|
||||
const ColumnContext = createContext<ColumnContext>(null);
|
||||
|
||||
export function ColumnProvider({ children }: { children: ReactNode }) {
|
||||
const storage = useStorage();
|
||||
const vlistRef = useRef<VListHandle>(null);
|
||||
|
||||
const [columns, setColumns] = useState<IColumn[]>([
|
||||
{
|
||||
id: 9999,
|
||||
title: "Newsfeed",
|
||||
content: "",
|
||||
kind: COL_TYPES.newsfeed,
|
||||
},
|
||||
{
|
||||
id: 9998,
|
||||
title: "For You",
|
||||
content: "",
|
||||
kind: COL_TYPES.foryou,
|
||||
},
|
||||
]);
|
||||
|
||||
const loadAllColumns = useCallback(async () => {
|
||||
return await storage.getColumns();
|
||||
}, []);
|
||||
|
||||
const addColumn = useCallback(async (column: IColumn) => {
|
||||
const result = await storage.createColumn(
|
||||
column.kind,
|
||||
column.title,
|
||||
column.content,
|
||||
);
|
||||
if (result) {
|
||||
setColumns((prev) => [...prev, result]);
|
||||
vlistRef?.current.scrollToIndex(columns.length);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const removeColumn = useCallback(async (id: number) => {
|
||||
if (id === 9998 || id === 9999) {
|
||||
toast.info("You cannot remove default column");
|
||||
return;
|
||||
}
|
||||
|
||||
await storage.removeColumn(id);
|
||||
setColumns((prev) => prev.filter((t) => t.id !== id));
|
||||
}, []);
|
||||
|
||||
const updateColumn = useCallback(
|
||||
async (id: number, title: string, content: string) => {
|
||||
const res = await storage.updateColumn(id, title, content);
|
||||
if (res) {
|
||||
const newCols = columns.map((col) => {
|
||||
if (col.id === id) {
|
||||
return { ...col, title, content };
|
||||
}
|
||||
return col;
|
||||
});
|
||||
|
||||
setColumns(newCols);
|
||||
}
|
||||
},
|
||||
[columns],
|
||||
);
|
||||
|
||||
const moveColumn = useCallback(
|
||||
(id: number, position: "left" | "right") => {
|
||||
const newCols = [...columns];
|
||||
|
||||
const col = newCols.find((el) => el.id === id);
|
||||
const colIndex = newCols.findIndex((el) => el.id === id);
|
||||
|
||||
newCols.splice(colIndex, 1);
|
||||
|
||||
if (position === "left") newCols.splice(colIndex - 1, 0, col);
|
||||
if (position === "right") newCols.splice(colIndex + 1, 0, col);
|
||||
|
||||
setColumns(newCols);
|
||||
},
|
||||
[columns],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
loadAllColumns().then((data) => {
|
||||
if (isMounted) setColumns((prev) => [...prev, ...data]);
|
||||
});
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
const ColumnContext = createContext<LumeColumn>(null);
|
||||
|
||||
export function ColumnProvider({
|
||||
column,
|
||||
children,
|
||||
}: { column: LumeColumn; children: ReactNode }) {
|
||||
return (
|
||||
<ColumnContext.Provider
|
||||
value={{
|
||||
columns,
|
||||
vlistRef,
|
||||
addColumn,
|
||||
removeColumn,
|
||||
moveColumn,
|
||||
updateColumn,
|
||||
loadAllColumns,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ColumnContext.Provider>
|
||||
<ColumnContext.Provider value={column}>{children}</ColumnContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { NDKAppHandlerEvent } from "@nostr-dev-kit/ndk";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
|
||||
export function AppHandler({ tag }: { tag: string[] }) {
|
||||
const ark = useArk();
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
import { COL_TYPES } from "@lume/utils";
|
||||
import { useColumnContext } from "../../column/provider";
|
||||
|
||||
export function Hashtag({ tag }: { tag: string }) {
|
||||
const { addColumn } = useColumnContext();
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () =>
|
||||
await addColumn({
|
||||
kind: COL_TYPES.hashtag,
|
||||
title: tag,
|
||||
content: tag,
|
||||
})
|
||||
}
|
||||
className="text-blue-500 break-all cursor-default hover:text-blue-600"
|
||||
>
|
||||
{tag}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { PinIcon } from "@lume/icons";
|
||||
import { COL_TYPES, NOSTR_MENTIONS } from "@lume/utils";
|
||||
import { NOSTR_MENTIONS } from "@lume/utils";
|
||||
import { ReactNode, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import { useEvent } from "../../../hooks/useEvent";
|
||||
import { useColumnContext } from "../../column/provider";
|
||||
import { User } from "../../user";
|
||||
import { Hashtag } from "./hashtag";
|
||||
import { MentionUser } from "./user";
|
||||
@@ -15,7 +14,6 @@ export function MentionNote({
|
||||
openable = true,
|
||||
}: { eventId: string; openable?: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
const { addColumn } = useColumnContext();
|
||||
const { isLoading, isError, data } = useEvent(eventId);
|
||||
|
||||
const richContent = useMemo(() => {
|
||||
@@ -133,13 +131,6 @@ export function MentionNote({
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () =>
|
||||
await addColumn({
|
||||
kind: COL_TYPES.thread,
|
||||
title: "Thread",
|
||||
content: data.id,
|
||||
})
|
||||
}
|
||||
className="inline-flex items-center justify-center rounded-md text-neutral-600 dark:text-neutral-400 size-6 bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
|
||||
>
|
||||
<PinIcon className="size-4" />
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { HorizontalDotsIcon } from "@lume/icons";
|
||||
import { COL_TYPES } from "@lume/utils";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
import { useArk } from "../../provider";
|
||||
import { useNoteContext } from "./provider";
|
||||
|
||||
export function NoteMenu() {
|
||||
@@ -19,7 +18,7 @@ export function NoteMenu() {
|
||||
};
|
||||
|
||||
const copyRaw = async () => {
|
||||
await writeText(JSON.stringify(await event.toNostrEvent()));
|
||||
await writeText(JSON.stringify(event));
|
||||
};
|
||||
|
||||
const copyNpub = async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
import { useArk } from "../../provider";
|
||||
import { AppHandler } from "./appHandler";
|
||||
import { useNoteContext } from "./provider";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CheckCircleIcon, DownloadIcon } from "@lume/icons";
|
||||
import { downloadDir } from "@tauri-apps/api/path";
|
||||
import { Window } from "@tauri-apps/api/window";
|
||||
import { WebviewWindow } from "@tauri-apps/api/webview";
|
||||
import { download } from "@tauri-apps/plugin-upload";
|
||||
import { SyntheticEvent, useState } from "react";
|
||||
|
||||
@@ -23,7 +23,7 @@ export function ImagePreview({ url }: { url: string }) {
|
||||
|
||||
const open = async () => {
|
||||
const name = new URL(url).pathname.split("/").pop();
|
||||
return new Window("image-viewer", {
|
||||
return new WebviewWindow("image-viewer", {
|
||||
url,
|
||||
title: name,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NavArrowDownIcon } from "@lume/icons";
|
||||
import { NDKEventWithReplies } from "@lume/types";
|
||||
import { EventWithReplies } from "@lume/types";
|
||||
import { cn } from "@lume/utils";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useState } from "react";
|
||||
@@ -10,7 +10,7 @@ import { ChildReply } from "./childReply";
|
||||
export function Reply({
|
||||
event,
|
||||
}: {
|
||||
event: NDKEventWithReplies;
|
||||
event: EventWithReplies;
|
||||
}) {
|
||||
const [t] = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cn } from "@lume/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Note } from "..";
|
||||
import { useArk } from "../../../hooks/useArk";
|
||||
import { useArk } from "../../../provider";
|
||||
import { User } from "../../user";
|
||||
|
||||
export function RepostNote({
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { PinIcon } from "@lume/icons";
|
||||
import { COL_TYPES, cn } from "@lume/utils";
|
||||
import { cn } from "@lume/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Note } from ".";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
import { useColumnContext } from "../column/provider";
|
||||
import { useArk } from "../../provider";
|
||||
import { useNoteContext } from "./provider";
|
||||
|
||||
export function NoteThread({
|
||||
|
||||
@@ -2,14 +2,11 @@ import { LoaderIcon } from "@lume/icons";
|
||||
import { cn } from "@lume/utils";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
|
||||
export function UserFollowButton({
|
||||
target,
|
||||
className,
|
||||
}: { target: string; className?: string }) {
|
||||
const ark = useArk();
|
||||
|
||||
const [t] = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [followed, setFollowed] = useState(false);
|
||||
|
||||
@@ -8,7 +8,7 @@ export function UserNip05({ className }: { className?: string }) {
|
||||
|
||||
const { isLoading, data: verified } = useQuery({
|
||||
queryKey: ["nip05", user?.profile.nip05],
|
||||
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||
queryFn: async () => {
|
||||
if (!user) return false;
|
||||
if (!user.profile.nip05) return false;
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Metadata } from "@lume/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { ReactNode, createContext, useContext } from "react";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
|
||||
const UserContext = createContext<{ pubkey: string; profile: Metadata }>(null);
|
||||
|
||||
@@ -10,13 +10,12 @@ export function UserProvider({
|
||||
children,
|
||||
embed,
|
||||
}: { pubkey: string; children: ReactNode; embed?: string }) {
|
||||
const ark = useArk();
|
||||
const { data: profile } = useQuery({
|
||||
queryKey: ["user", pubkey],
|
||||
queryFn: async () => {
|
||||
if (embed) return JSON.parse(embed) as Metadata;
|
||||
|
||||
const profile = await ark.get_profile(pubkey);
|
||||
const profile: Metadata = await invoke("get_profile", { id: pubkey });
|
||||
|
||||
if (!profile)
|
||||
throw new Error(
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { useContext } from "react";
|
||||
import { ArkContext } from "../provider";
|
||||
|
||||
export const useArk = () => {
|
||||
const context = useContext(ArkContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("Please import Ark Provider to use useArk() hook");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useArk } from "./useArk";
|
||||
import { useArk } from "../provider";
|
||||
|
||||
export function useEvent(id: string) {
|
||||
const ark = useArk();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useArk } from "./useArk";
|
||||
import { useArk } from "../provider";
|
||||
|
||||
export function useProfile(pubkey: string) {
|
||||
const ark = useArk();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useArk } from "./useArk";
|
||||
import { useArk } from "../provider";
|
||||
|
||||
export function useRelaylist() {
|
||||
const ark = useArk();
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
export * from "./ark";
|
||||
export * from "./provider";
|
||||
export * from "./hooks/useEvent";
|
||||
export * from "./hooks/useArk";
|
||||
export * from "./hooks/useProfile";
|
||||
export * from "./hooks/useRelayList";
|
||||
export * from "./components/user";
|
||||
export * from "./components/column";
|
||||
export * from "./components/column/provider";
|
||||
export * from "./components/note";
|
||||
export * from "./components/note/primitives/text";
|
||||
export * from "./components/note/primitives/repost";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PropsWithChildren, createContext, useEffect, useMemo } from "react";
|
||||
import { PropsWithChildren, createContext, useContext, useMemo } from "react";
|
||||
import { Ark } from "./ark";
|
||||
|
||||
export const ArkContext = createContext<Ark>(undefined);
|
||||
@@ -7,3 +7,11 @@ export const ArkProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
const ark = useMemo(() => new Ark(), []);
|
||||
return <ArkContext.Provider value={ark}>{children}</ArkContext.Provider>;
|
||||
};
|
||||
|
||||
export const useArk = () => {
|
||||
const context = useContext(ArkContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("Ark Provider is not import");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user