feat: add new account management

This commit is contained in:
2024-02-08 17:17:45 +07:00
parent d7bbda6e7b
commit 17052aeeaa
35 changed files with 1140 additions and 1484 deletions

View File

@@ -4,8 +4,8 @@ import { invoke } from "@tauri-apps/api/core";
export class Ark {
public account: CurrentAccount;
constructor(account: CurrentAccount) {
this.account = account;
constructor() {
this.account = null;
}
public async event_to_bech32(id: string, relays: string[]) {
@@ -67,12 +67,12 @@ export class Ark {
};
}
public async get_metadata(id: string) {
public async get_profile(id: string) {
try {
const cmd: Metadata = await invoke("get_metadata", { id });
const cmd: Metadata = await invoke("get_profile", { id });
return cmd;
} catch (e) {
console.error("failed to get metadata", id);
console.error("failed to get profile", id);
}
}

View File

@@ -3,7 +3,6 @@ import { cn, editorAtom, editorValueAtom } from "@lume/utils";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import * as Tooltip from "@radix-ui/react-tooltip";
import { useSetAtom } from "jotai";
import { nip19 } from "nostr-tools";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

View File

@@ -31,7 +31,7 @@ export function UserAbout({ className }: { className?: string }) {
return (
<div className={cn("select-text break-p", className)}>
{user.about?.trim() || user.bio?.trim() || "No bio"}
{user.profile.about?.trim() || "No bio"}
</div>
);
}

View File

@@ -46,7 +46,7 @@ export function UserAvatar({ className }: { className?: string }) {
/>
) : (
<Avatar.Image
src={user.image}
src={user.profile.picture}
alt={user.pubkey}
loading="eager"
decoding="async"

View File

@@ -15,7 +15,7 @@ export function UserCover({ className }: { className?: string }) {
);
}
if (user && !user.banner) {
if (user && !user.profile.banner) {
return (
<div
className={cn("bg-gradient-to-b from-sky-400 to-sky-200", className)}
@@ -25,7 +25,7 @@ export function UserCover({ className }: { className?: string }) {
return (
<img
src={user.banner}
src={user.profile.banner}
alt="banner"
loading="lazy"
decoding="async"

View File

@@ -17,7 +17,7 @@ export function UserName({ className }: { className?: string }) {
return (
<div className={cn("max-w-[12rem] truncate", className)}>
{user.displayName || user.name || "Anon"}
{user.profile.display_name || user.profile.name || "Anon"}
</div>
);
}

View File

@@ -1,26 +1,17 @@
import { VerifiedIcon } from "@lume/icons";
import { cn, displayNpub } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { useArk } from "../../hooks/useArk";
import { useUserContext } from "./provider";
export function UserNip05({
pubkey,
className,
}: { pubkey: string; className?: string }) {
const ark = useArk();
export function UserNip05({ className }: { className?: string }) {
const user = useUserContext();
const { isLoading, data: verified } = useQuery({
queryKey: ["nip05", user?.nip05],
queryKey: ["nip05", user?.profile.nip05],
queryFn: async ({ signal }: { signal: AbortSignal }) => {
if (!user) return false;
if (!user.nip05) return false;
return ark.validateNIP05({
pubkey,
nip05: user.nip05,
signal,
});
if (!user.profile.nip05) return false;
return false;
},
enabled: !!user,
});
@@ -39,11 +30,11 @@ export function UserNip05({
return (
<div className="inline-flex items-center gap-1">
<p className={cn("text-sm", className)}>
{!user?.nip05
? displayNpub(pubkey, 16)
: user?.nip05?.startsWith("_@")
? user?.nip05?.replace("_@", "")
: user?.nip05}
{!user?.profile.nip05
? displayNpub(user.pubkey, 16)
: user?.profile.nip05?.startsWith("_@")
? user?.profile.nip05?.replace("_@", "")
: user?.profile.nip05}
</p>
{!isLoading && verified ? (
<VerifiedIcon className="size-4 text-teal-500" />

View File

@@ -3,7 +3,7 @@ import { useQuery } from "@tanstack/react-query";
import { ReactNode, createContext, useContext } from "react";
import { useArk } from "../../hooks/useArk";
const UserContext = createContext<Metadata>(null);
const UserContext = createContext<{ pubkey: string; profile: Metadata }>(null);
export function UserProvider({
pubkey,
@@ -11,12 +11,12 @@ export function UserProvider({
embed,
}: { pubkey: string; children: ReactNode; embed?: string }) {
const ark = useArk();
const { data: user } = useQuery({
const { data: profile } = useQuery({
queryKey: ["user", pubkey],
queryFn: async () => {
if (embed) return JSON.parse(embed) as Metadata;
const profile = await ark.get_metadata(pubkey);
const profile = await ark.get_profile(pubkey);
if (!profile)
throw new Error(
@@ -32,7 +32,11 @@ export function UserProvider({
retry: 2,
});
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
return (
<UserContext.Provider value={{ pubkey, profile }}>
{children}
</UserContext.Provider>
);
}
export function useUserContext() {

View File

@@ -1,4 +0,0 @@
import { createContext } from "react";
import { type Ark } from "./ark";
export const LumeContext = createContext<Ark>(undefined);

View File

@@ -1,8 +1,8 @@
import { useContext } from "react";
import { LumeContext } from "../context";
import { ArkContext } from "../provider";
export const useArk = () => {
const context = useContext(LumeContext);
const context = useContext(ArkContext);
if (context === undefined) {
throw new Error("Please import Ark Provider to use useArk() hook");
}

View File

@@ -1,4 +1,4 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useQuery } from "@tanstack/react-query";
import { useArk } from "./useArk";
export function useProfile(pubkey: string) {
@@ -10,7 +10,7 @@ export function useProfile(pubkey: string) {
} = useQuery({
queryKey: ["user", pubkey],
queryFn: async () => {
const profile = await ark.get_metadata(pubkey);
const profile = await ark.get_profile(pubkey);
if (!profile)
throw new Error(
`Cannot get metadata for ${pubkey}, will be retry after 10 seconds`,

View File

@@ -1,6 +1,4 @@
import { NDKKind, NDKTag } from "@nostr-dev-kit/ndk";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { normalizeRelayUrl } from "nostr-fetch";
import { useArk } from "./useArk";
export function useRelaylist() {

View File

@@ -1,5 +1,4 @@
export * from "./ark";
export * from "./context";
export * from "./provider";
export * from "./hooks/useEvent";
export * from "./hooks/useArk";

View File

@@ -1,18 +1,9 @@
import { PropsWithChildren, useEffect, useState } from "react";
import { PropsWithChildren, createContext, useMemo } from "react";
import { Ark } from "./ark";
import { LumeContext } from "./context";
export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
const [ark, setArk] = useState<Ark>(undefined);
export const ArkContext = createContext<Ark>(undefined);
useEffect(() => {
async function setupArk() {
const _ark = new Ark();
setArk(_ark);
}
if (!ark) setupArk();
}, []);
return <LumeContext.Provider value={ark}>{children}</LumeContext.Provider>;
export const ArkProvider = ({ children }: PropsWithChildren<object>) => {
const ark = useMemo(() => new Ark(), []);
return <ArkContext.Provider value={ark}>{children}</ArkContext.Provider>;
};