diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index d998f392..8d6374d5 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -17,6 +17,7 @@
"@getalby/sdk": "^3.2.3",
"@lume/ark": "workspace:^",
"@lume/icons": "workspace:^",
+ "@lume/storage": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@nostr-dev-kit/ndk": "^2.3.2",
@@ -79,6 +80,7 @@
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3",
"vite": "^5.0.11",
+ "vite-plugin-top-level-await": "^1.4.1",
"vite-tsconfig-paths": "^4.2.3"
}
}
diff --git a/apps/desktop/src/app.tsx b/apps/desktop/src/app.tsx
index 1c342ab0..64a7f3c3 100644
--- a/apps/desktop/src/app.tsx
+++ b/apps/desktop/src/app.tsx
@@ -1,4 +1,5 @@
import { ColumnProvider, LumeProvider } from "@lume/ark";
+import { StorageProvider } from "@lume/storage";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "sonner";
import Router from "./router";
@@ -15,11 +16,13 @@ export default function App() {
return (
-
-
-
-
-
+
+
+
+
+
+
+
);
}
diff --git a/apps/desktop/src/router.tsx b/apps/desktop/src/router.tsx
index 1aabdd5f..86dbde34 100644
--- a/apps/desktop/src/router.tsx
+++ b/apps/desktop/src/router.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { AppLayout, AuthLayout, HomeLayout, SettingsLayout } from "@lume/ui";
import { fetch } from "@tauri-apps/plugin-http";
import {
@@ -23,7 +24,7 @@ export default function Router() {
element: ,
errorElement: ,
loader: async () => {
- if (!storage.account) return redirect("auth");
+ if (!ark.account) return redirect("auth");
return null;
},
children: [
diff --git a/apps/desktop/src/routes/auth/create.tsx b/apps/desktop/src/routes/auth/create.tsx
index 027d24a4..af56b9b7 100644
--- a/apps/desktop/src/routes/auth/create.tsx
+++ b/apps/desktop/src/routes/auth/create.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { CheckIcon, ChevronDownIcon, LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { onboardingAtom } from "@lume/utils";
import NDK, {
NDKEvent,
diff --git a/apps/desktop/src/routes/auth/login-key.tsx b/apps/desktop/src/routes/auth/login-key.tsx
index 34c50684..26a7333f 100644
--- a/apps/desktop/src/routes/auth/login-key.tsx
+++ b/apps/desktop/src/routes/auth/login-key.tsx
@@ -1,5 +1,5 @@
-import { useStorage } from "@lume/ark";
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { getPublicKey, nip19 } from "nostr-tools";
import { useState } from "react";
import { useForm } from "react-hook-form";
diff --git a/apps/desktop/src/routes/auth/login-nsecbunker.tsx b/apps/desktop/src/routes/auth/login-nsecbunker.tsx
index a7c49bb0..61f09ef3 100644
--- a/apps/desktop/src/routes/auth/login-nsecbunker.tsx
+++ b/apps/desktop/src/routes/auth/login-nsecbunker.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import NDK, { NDKNip46Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools";
import { useState } from "react";
diff --git a/apps/desktop/src/routes/auth/login-oauth.tsx b/apps/desktop/src/routes/auth/login-oauth.tsx
index 9da971d0..53e266a3 100644
--- a/apps/desktop/src/routes/auth/login-oauth.tsx
+++ b/apps/desktop/src/routes/auth/login-oauth.tsx
@@ -1,10 +1,10 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { NIP05 } from "@lume/types";
import NDK, { NDKNip46Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { Window } from "@tauri-apps/api/window";
import { fetch } from "@tauri-apps/plugin-http";
-import { nip19 } from "nostr-tools";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
@@ -30,6 +30,13 @@ export function LoginWithOAuth() {
try {
setLoading(true);
+ if (!emailRegex.test(data.nip05)) {
+ setLoading(false);
+ return toast.error(
+ "Cannot verify your NIP-05 address, please try again later.",
+ );
+ }
+
const localPath = data.nip05.split("@")[0];
const service = data.nip05.split("@")[1];
diff --git a/apps/desktop/src/routes/auth/onboarding.tsx b/apps/desktop/src/routes/auth/onboarding.tsx
index fda75e0d..77d6ab4b 100644
--- a/apps/desktop/src/routes/auth/onboarding.tsx
+++ b/apps/desktop/src/routes/auth/onboarding.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { InfoIcon, LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { FETCH_LIMIT } from "@lume/utils";
import { NDKKind } from "@nostr-dev-kit/ndk";
import * as Switch from "@radix-ui/react-switch";
@@ -57,7 +58,7 @@ export function OnboardingScreen() {
setLoading(true);
// get account contacts
- await ark.getUserContacts(storage.account.pubkey);
+ await ark.getUserContacts(ark.account.pubkey);
// refetch newsfeed
await queryClient.prefetchInfiniteQuery({
@@ -73,7 +74,7 @@ export function OnboardingScreen() {
const events = await ark.getInfiniteEvents({
filter: {
kinds: [NDKKind.Text, NDKKind.Repost],
- authors: storage.account.contacts,
+ authors: ark.account.contacts,
},
limit: FETCH_LIMIT,
pageParam,
diff --git a/apps/desktop/src/routes/depot/components/contact.tsx b/apps/desktop/src/routes/depot/components/contact.tsx
index f19953f1..e456e6c6 100644
--- a/apps/desktop/src/routes/depot/components/contact.tsx
+++ b/apps/desktop/src/routes/depot/components/contact.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { LoaderIcon, RunIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { User } from "@lume/ui";
import { NDKKind } from "@nostr-dev-kit/ndk";
import { useState } from "react";
@@ -17,7 +18,7 @@ export function DepotContactCard() {
const event = await ark.getEventByFilter({
filter: {
- authors: [storage.account.pubkey],
+ authors: [ark.account.pubkey],
kinds: [NDKKind.Contacts],
},
});
@@ -39,13 +40,13 @@ export function DepotContactCard() {
- {storage.account.contacts?.slice(0, 8).map((item) => (
+ {ark.account.contacts?.slice(0, 8).map((item) => (
))}
- {storage.account.contacts?.length > 8 ? (
+ {ark.account.contacts?.length > 8 ? (
- +{storage.account.contacts?.length - 8}
+ +{ark.account.contacts?.length - 8}
) : null}
diff --git a/apps/desktop/src/routes/depot/components/profile.tsx b/apps/desktop/src/routes/depot/components/profile.tsx
index 5f138173..a44c478a 100644
--- a/apps/desktop/src/routes/depot/components/profile.tsx
+++ b/apps/desktop/src/routes/depot/components/profile.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { LoaderIcon, RunIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { User } from "@lume/ui";
import { NDKKind } from "@nostr-dev-kit/ndk";
import { useState } from "react";
@@ -17,7 +18,7 @@ export function DepotProfileCard() {
const event = await ark.getEventByFilter({
filter: {
- authors: [storage.account.pubkey],
+ authors: [ark.account.pubkey],
kinds: [NDKKind.Metadata],
},
});
@@ -38,7 +39,7 @@ export function DepotProfileCard() {
return (
-
+
Profile
diff --git a/apps/desktop/src/routes/depot/components/relays.tsx b/apps/desktop/src/routes/depot/components/relays.tsx
index d80d668a..0ef3cdee 100644
--- a/apps/desktop/src/routes/depot/components/relays.tsx
+++ b/apps/desktop/src/routes/depot/components/relays.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { LoaderIcon, RunIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { NDKKind } from "@nostr-dev-kit/ndk";
import { useEffect, useState } from "react";
import { toast } from "sonner";
@@ -17,7 +18,7 @@ export function DepotRelaysCard() {
const event = await ark.getEventByFilter({
filter: {
- authors: [storage.account.pubkey],
+ authors: [ark.account.pubkey],
kinds: [NDKKind.RelayList],
},
});
@@ -39,7 +40,7 @@ export function DepotRelaysCard() {
async function loadRelays() {
const event = await ark.getEventByFilter({
filter: {
- authors: [storage.account.pubkey],
+ authors: [ark.account.pubkey],
kinds: [NDKKind.RelayList],
},
});
diff --git a/apps/desktop/src/routes/depot/index.tsx b/apps/desktop/src/routes/depot/index.tsx
index f27493d1..c9dac648 100644
--- a/apps/desktop/src/routes/depot/index.tsx
+++ b/apps/desktop/src/routes/depot/index.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { ChevronDownIcon, DepotIcon, GossipIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { NDKKind } from "@nostr-dev-kit/ndk";
import * as Collapsible from "@radix-ui/react-collapsible";
import { invoke } from "@tauri-apps/api/core";
@@ -36,7 +37,7 @@ export function DepotScreen() {
const relayEvent = await ark.getEventByFilter({
filter: {
- authors: [storage.account.pubkey],
+ authors: [ark.account.pubkey],
kinds: [NDKKind.RelayList],
},
});
diff --git a/apps/desktop/src/routes/depot/onboarding.tsx b/apps/desktop/src/routes/depot/onboarding.tsx
index 365405df..6267a30d 100644
--- a/apps/desktop/src/routes/depot/onboarding.tsx
+++ b/apps/desktop/src/routes/depot/onboarding.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { delay } from "@lume/utils";
import { resolveResource } from "@tauri-apps/api/path";
import { readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
@@ -26,9 +27,7 @@ export function DepotOnboardingScreen() {
// add current user to whitelist
// biome-ignore lint/complexity/useLiteralKeys:
- parsedConfig.authorization["pubkey_whitelist"].push(
- storage.account.pubkey,
- );
+ parsedConfig.authorization["pubkey_whitelist"].push(ark.account.pubkey);
// update new config
const newConfig = stringify(parsedConfig);
diff --git a/apps/desktop/src/routes/error.tsx b/apps/desktop/src/routes/error.tsx
index 4601c6df..9351d05d 100644
--- a/apps/desktop/src/routes/error.tsx
+++ b/apps/desktop/src/routes/error.tsx
@@ -1,4 +1,4 @@
-import { useStorage } from "@lume/ark";
+import { useStorage } from "@lume/storage";
import { downloadDir } from "@tauri-apps/api/path";
import { message, save } from "@tauri-apps/plugin-dialog";
import { writeTextFile } from "@tauri-apps/plugin-fs";
@@ -25,18 +25,18 @@ export function ErrorScreen() {
const filePath = await save({
defaultPath: `${downloadPath}/${fileName}`,
});
- const nsec = await storage.loadPrivkey(storage.account.pubkey);
+ const nsec = await storage.loadPrivkey(ark.account.pubkey);
if (filePath) {
if (nsec) {
await writeTextFile(
filePath,
- `Nostr account, generated by Lume (lume.nu)\nPublic key: ${storage.account.id}\nPrivate key: ${nsec}`,
+ `Nostr account, generated by Lume (lume.nu)\nPublic key: ${ark.account.id}\nPrivate key: ${nsec}`,
);
} else {
await writeTextFile(
filePath,
- `Nostr account, generated by Lume (lume.nu)\nPublic key: ${storage.account.id}`,
+ `Nostr account, generated by Lume (lume.nu)\nPublic key: ${ark.account.id}`,
);
}
} // else { user cancel action }
diff --git a/apps/desktop/src/routes/nwc/components/form.tsx b/apps/desktop/src/routes/nwc/components/form.tsx
index aed87d85..c173cac7 100644
--- a/apps/desktop/src/routes/nwc/components/form.tsx
+++ b/apps/desktop/src/routes/nwc/components/form.tsx
@@ -1,5 +1,5 @@
-import { useStorage } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { useState } from "react";
import { toast } from "sonner";
@@ -25,7 +25,7 @@ export function NWCForm({ setWalletConnectURL }) {
const params = new URLSearchParams(uriObj.search);
if (params.has("relay") && params.has("secret")) {
- await storage.createPrivkey(`${storage.account.pubkey}-nwc`, uri);
+ await storage.createPrivkey(`${ark.account.pubkey}-nwc`, uri);
setWalletConnectURL(uri);
setLoading(false);
} else {
diff --git a/apps/desktop/src/routes/nwc/index.tsx b/apps/desktop/src/routes/nwc/index.tsx
index 2c8a3345..96ac0efd 100644
--- a/apps/desktop/src/routes/nwc/index.tsx
+++ b/apps/desktop/src/routes/nwc/index.tsx
@@ -1,5 +1,5 @@
-import { useStorage } from "@lume/ark";
import { CheckCircleIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { useEffect, useState } from "react";
import { NWCForm } from "./components/form";
@@ -8,13 +8,13 @@ export function NWCScreen() {
const [walletConnectURL, setWalletConnectURL] = useState(null);
const remove = async () => {
- await storage.removePrivkey(`${storage.account.pubkey}-nwc`);
+ await storage.removePrivkey(`${ark.account.pubkey}-nwc`);
setWalletConnectURL(null);
};
useEffect(() => {
async function getNWC() {
- const nwc = await storage.loadPrivkey(`${storage.account.pubkey}-nwc`);
+ const nwc = await storage.loadPrivkey(`${ark.account.pubkey}-nwc`);
if (nwc) setWalletConnectURL(nwc);
}
getNWC();
diff --git a/apps/desktop/src/routes/relays/components/userRelayList.tsx b/apps/desktop/src/routes/relays/components/userRelayList.tsx
index c24c3add..cf6617ad 100644
--- a/apps/desktop/src/routes/relays/components/userRelayList.tsx
+++ b/apps/desktop/src/routes/relays/components/userRelayList.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { CancelIcon, RefreshIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { NDKKind } from "@nostr-dev-kit/ndk";
import { useQuery } from "@tanstack/react-query";
import { RelayForm } from "./relayForm";
@@ -9,12 +10,12 @@ export function UserRelayList() {
const storage = useStorage();
const { status, data, refetch } = useQuery({
- queryKey: ["relays", storage.account.pubkey],
+ queryKey: ["relays", ark.account.pubkey],
queryFn: async () => {
const event = await ark.getEventByFilter({
filter: {
kinds: [NDKKind.RelayList],
- authors: [storage.account.pubkey],
+ authors: [ark.account.pubkey],
},
});
diff --git a/apps/desktop/src/routes/settings/advanced.tsx b/apps/desktop/src/routes/settings/advanced.tsx
index 630daa95..23f1a619 100644
--- a/apps/desktop/src/routes/settings/advanced.tsx
+++ b/apps/desktop/src/routes/settings/advanced.tsx
@@ -1,4 +1,4 @@
-import { useStorage } from "@lume/ark";
+import { useStorage } from "@lume/storage";
export function AdvancedSettingScreen() {
const storage = useStorage();
diff --git a/apps/desktop/src/routes/settings/backup.tsx b/apps/desktop/src/routes/settings/backup.tsx
index 3efb1bdb..036c69f7 100644
--- a/apps/desktop/src/routes/settings/backup.tsx
+++ b/apps/desktop/src/routes/settings/backup.tsx
@@ -1,5 +1,5 @@
-import { useStorage } from "@lume/ark";
import { EyeOffIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { nip19 } from "nostr-tools";
import { useEffect, useState } from "react";
@@ -10,12 +10,12 @@ export function BackupSettingScreen() {
const [showPassword, setShowPassword] = useState(false);
const removePrivkey = async () => {
- await storage.removePrivkey(storage.account.pubkey);
+ await storage.removePrivkey(ark.account.pubkey);
};
useEffect(() => {
async function loadPrivkey() {
- const key = await storage.loadPrivkey(storage.account.pubkey);
+ const key = await storage.loadPrivkey(ark.account.pubkey);
if (key) setPrivkey(key);
}
diff --git a/apps/desktop/src/routes/settings/components/postCard.tsx b/apps/desktop/src/routes/settings/components/postCard.tsx
index 6acdf1bf..f0496075 100644
--- a/apps/desktop/src/routes/settings/components/postCard.tsx
+++ b/apps/desktop/src/routes/settings/components/postCard.tsx
@@ -1,5 +1,5 @@
-import { useStorage } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { compactNumber } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { fetch } from "@tauri-apps/plugin-http";
@@ -8,10 +8,10 @@ import { Link } from "react-router-dom";
export function PostCard() {
const storage = useStorage();
const { status, data } = useQuery({
- queryKey: ["user-stats", storage.account.pubkey],
+ queryKey: ["user-stats", ark.account.pubkey],
queryFn: async ({ signal }: { signal: AbortSignal }) => {
const res = await fetch(
- `https://api.nostr.band/v0/stats/profile/${storage.account.pubkey}`,
+ `https://api.nostr.band/v0/stats/profile/${ark.account.pubkey}`,
{
signal,
},
@@ -39,7 +39,7 @@ export function PostCard() {
{compactNumber.format(
- data.stats[storage.account.pubkey].pub_note_count,
+ data.stats[ark.account.pubkey].pub_note_count,
)}
@@ -47,7 +47,7 @@ export function PostCard() {
Posts
View
diff --git a/apps/desktop/src/routes/settings/components/profileCard.tsx b/apps/desktop/src/routes/settings/components/profileCard.tsx
index 97e8528c..8dc18b3e 100644
--- a/apps/desktop/src/routes/settings/components/profileCard.tsx
+++ b/apps/desktop/src/routes/settings/components/profileCard.tsx
@@ -1,5 +1,6 @@
-import { useProfile, useStorage } from "@lume/ark";
+import { useProfile } from "@lume/ark";
import { EditIcon, LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { displayNpub } from "@lume/utils";
import * as Avatar from "@radix-ui/react-avatar";
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
@@ -10,13 +11,13 @@ import { Link } from "react-router-dom";
export function ProfileCard() {
const storage = useStorage();
const svgURI = `data:image/svg+xml;utf8,${encodeURIComponent(
- minidenticon(storage.account.pubkey, 90, 50),
+ minidenticon(ark.account.pubkey, 90, 50),
)}`;
- const { isLoading, user } = useProfile(storage.account.pubkey);
+ const { isLoading, user } = useProfile(ark.account.pubkey);
const copyNpub = async () => {
- return await writeText(nip19.npubEncode(storage.account.pubkey));
+ return await writeText(nip19.npubEncode(ark.account.pubkey));
};
return (
@@ -47,7 +48,7 @@ export function ProfileCard() {
@@ -66,7 +67,7 @@ export function ProfileCard() {
{user?.display_name || user?.name}
- {user?.nip05 || displayNpub(storage.account.pubkey, 16)}
+ {user?.nip05 || displayNpub(ark.account.pubkey, 16)}
diff --git a/apps/desktop/src/routes/settings/components/relayCard.tsx b/apps/desktop/src/routes/settings/components/relayCard.tsx
index 1af6ea6b..132d506b 100644
--- a/apps/desktop/src/routes/settings/components/relayCard.tsx
+++ b/apps/desktop/src/routes/settings/components/relayCard.tsx
@@ -1,5 +1,6 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import { EditIcon, LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { compactNumber } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { Link } from "react-router-dom";
@@ -9,7 +10,7 @@ export function RelayCard() {
const storage = useStorage();
const { status, data } = useQuery({
- queryKey: ["relays", storage.account.pubkey],
+ queryKey: ["relays", ark.account.pubkey],
queryFn: async () => {
const relays = await ark.getUserRelays({});
return relays;
diff --git a/apps/desktop/src/routes/settings/components/zapCard.tsx b/apps/desktop/src/routes/settings/components/zapCard.tsx
index 134a2e33..e1771d42 100644
--- a/apps/desktop/src/routes/settings/components/zapCard.tsx
+++ b/apps/desktop/src/routes/settings/components/zapCard.tsx
@@ -1,5 +1,5 @@
-import { useStorage } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { compactNumber } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { fetch } from "@tauri-apps/plugin-http";
@@ -7,10 +7,10 @@ import { fetch } from "@tauri-apps/plugin-http";
export function ZapCard() {
const storage = useStorage();
const { status, data } = useQuery({
- queryKey: ["user-stats", storage.account.pubkey],
+ queryKey: ["user-stats", ark.account.pubkey],
queryFn: async ({ signal }: { signal: AbortSignal }) => {
const res = await fetch(
- `https://api.nostr.band/v0/stats/profile/${storage.account.pubkey}`,
+ `https://api.nostr.band/v0/stats/profile/${ark.account.pubkey}`,
{
signal,
},
@@ -38,8 +38,7 @@ export function ZapCard() {
{compactNumber.format(
- data?.stats[storage.account.pubkey]?.zaps_received?.msats /
- 1000 || 0,
+ data?.stats[ark.account.pubkey]?.zaps_received?.msats / 1000 || 0,
)}
diff --git a/apps/desktop/src/routes/settings/editProfile.tsx b/apps/desktop/src/routes/settings/editProfile.tsx
index b57afd6a..7031681d 100644
--- a/apps/desktop/src/routes/settings/editProfile.tsx
+++ b/apps/desktop/src/routes/settings/editProfile.tsx
@@ -1,10 +1,11 @@
-import { useArk, useStorage } from "@lume/ark";
+import { useArk } from "@lume/ark";
import {
CheckCircleIcon,
LoaderIcon,
PlusIcon,
UnverifiedIcon,
} from "@lume/icons";
+import { useStorage } from "@lume/storage";
import { NDKKind, NDKUserProfile } from "@nostr-dev-kit/ndk";
import { useQueryClient } from "@tanstack/react-query";
import { message } from "@tauri-apps/plugin-dialog";
@@ -31,7 +32,7 @@ export function EditProfileScreen() {
defaultValues: async () => {
const res: NDKUserProfile = queryClient.getQueryData([
"user",
- storage.account.pubkey,
+ ark.account.pubkey,
]);
if (res.image) {
setPicture(res.image);
@@ -102,7 +103,7 @@ export function EditProfileScreen() {
if (data.nip05) {
const verify = ark.validateNIP05({
- pubkey: storage.account.pubkey,
+ pubkey: ark.account.pubkey,
nip05: data.nip05,
});
if (verify) {
@@ -125,7 +126,7 @@ export function EditProfileScreen() {
if (publish) {
// invalid cache
queryClient.invalidateQueries({
- queryKey: ["user", storage.account.pubkey],
+ queryKey: ["user", ark.account.pubkey],
});
// reset form
reset();
diff --git a/apps/desktop/src/routes/settings/general.tsx b/apps/desktop/src/routes/settings/general.tsx
index 99a2a227..01f112d0 100644
--- a/apps/desktop/src/routes/settings/general.tsx
+++ b/apps/desktop/src/routes/settings/general.tsx
@@ -1,5 +1,5 @@
-import { useStorage } from "@lume/ark";
import { DarkIcon, LightIcon, SystemModeIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import * as Switch from "@radix-ui/react-switch";
import { invoke } from "@tauri-apps/api/core";
import { getCurrent } from "@tauri-apps/api/window";
diff --git a/apps/desktop/src/routes/users/components/profile.tsx b/apps/desktop/src/routes/users/components/profile.tsx
index fd905990..250db59d 100644
--- a/apps/desktop/src/routes/users/components/profile.tsx
+++ b/apps/desktop/src/routes/users/components/profile.tsx
@@ -1,4 +1,5 @@
-import { useArk, useProfile, useStorage } from "@lume/ark";
+import { useArk, useProfile } from "@lume/ark";
+import { useStorage } from "@lume/storage";
import { NIP05 } from "@lume/ui";
import { displayNpub } from "@lume/utils";
import * as Avatar from "@radix-ui/react-avatar";
@@ -50,7 +51,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
};
useEffect(() => {
- if (storage.account.contacts.includes(pubkey)) {
+ if (ark.account.contacts.includes(pubkey)) {
setFollowed(true);
}
}, []);
diff --git a/apps/desktop/vite.config.ts b/apps/desktop/vite.config.ts
index a48b3938..d0be4dc5 100644
--- a/apps/desktop/vite.config.ts
+++ b/apps/desktop/vite.config.ts
@@ -1,6 +1,7 @@
import react from "@vitejs/plugin-react-swc";
import million from "million/compiler";
import { defineConfig } from "vite";
+import topLevelAwait from "vite-plugin-top-level-await";
import viteTsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
@@ -13,6 +14,12 @@ export default defineConfig({
}),
react(),
viteTsconfigPaths(),
+ topLevelAwait({
+ // The export name of top-level await promise for each chunk module
+ promiseExportName: "__tla",
+ // The function to generate import names of top-level await promise in each chunk module
+ promiseImportName: (i) => `__tla_${i}`,
+ }),
],
envPrefix: ["VITE_", "TAURI_"],
build: {
diff --git a/packages/ark/src/ark.ts b/packages/ark/src/ark.ts
index 66aa26a9..3601f317 100644
--- a/packages/ark/src/ark.ts
+++ b/packages/ark/src/ark.ts
@@ -1,5 +1,4 @@
-import { LumeStorage } from "@lume/storage";
-import { type NDKEventWithReplies, type NIP05 } from "@lume/types";
+import { Account, type NDKEventWithReplies, type NIP05 } from "@lume/types";
import NDK, {
NDKEvent,
NDKFilter,
@@ -20,18 +19,18 @@ import { NostrFetcher, normalizeRelayUrl } from "nostr-fetch";
import { nip19 } from "nostr-tools";
export class Ark {
- #storage: LumeStorage;
public ndk: NDK;
+ public account: Account;
constructor({
ndk,
- storage,
+ account,
}: {
ndk: NDK;
- storage: LumeStorage;
+ account: Account;
}) {
this.ndk = ndk;
- this.#storage = storage;
+ this.account = account;
}
public async connectDepot() {
@@ -106,8 +105,9 @@ export class Ark {
public async getUserProfile(pubkey?: string) {
try {
+ const currentUserPubkey = this.account.pubkey;
+
// get clean pubkey without any special characters
- const currentUserPubkey = this.#storage.account.pubkey;
let hexstring = pubkey
? pubkey.replace(/[^a-zA-Z0-9]/g, "").replace("nostr:", "")
: currentUserPubkey;
@@ -141,8 +141,9 @@ export class Ark {
public async getUserContacts(pubkey?: string) {
try {
+ const currentUserPubkey = this.account.pubkey;
+
// get clean pubkey without any special characters
- const currentUserPubkey = this.#storage.account.pubkey;
let hexstring = pubkey
? pubkey.replace(/[^a-zA-Z0-9]/g, "").replace("nostr:", "")
: currentUserPubkey;
@@ -165,8 +166,8 @@ export class Ark {
const contacts = [...(await user.follows())].map((user) => user.pubkey);
- if (!pubkey || pubkey === this.#storage.account.pubkey)
- this.#storage.account.contacts = contacts;
+ if (!pubkey || pubkey === this.account.pubkey)
+ this.account.contacts = contacts;
return contacts;
} catch (e) {
@@ -177,7 +178,7 @@ export class Ark {
public async getUserRelays({ pubkey }: { pubkey?: string }) {
try {
const user = this.ndk.getUser({
- pubkey: pubkey ? pubkey : this.#storage.account.pubkey,
+ pubkey: pubkey ? pubkey : this.account.pubkey,
});
return await user.relayList();
} catch (e) {
@@ -192,19 +193,19 @@ export class Ark {
});
if (publish) {
- this.#storage.account.contacts = tags.map((item) => item[1]);
+ this.account.contacts = tags.map((item) => item[1]);
return publish;
}
}
public async createContact({ pubkey }: { pubkey: string }) {
- const user = this.ndk.getUser({ pubkey: this.#storage.account.pubkey });
+ const user = this.ndk.getUser({ pubkey: this.account.pubkey });
const contacts = await user.follows();
return await user.follow(new NDKUser({ pubkey: pubkey }), contacts);
}
public async deleteContact({ pubkey }: { pubkey: string }) {
- const user = this.ndk.getUser({ pubkey: this.#storage.account.pubkey });
+ const user = this.ndk.getUser({ pubkey: this.account.pubkey });
const contacts = await user.follows();
contacts.delete(new NDKUser({ pubkey: pubkey }));
@@ -362,7 +363,7 @@ export class Ark {
const relayMap = new Map
();
const relayEvents = fetcher.fetchLatestEventsPerAuthor(
{
- authors: this.#storage.account.contacts,
+ authors: this.account.contacts,
relayUrls: connectedRelays,
},
{ kinds: [NDKKind.RelayList] },
@@ -569,7 +570,7 @@ export class Ark {
const event = await this.ndk.fetchEvent({
kinds: [NDKKind.AppRecommendation],
"#d": [unknownKind],
- authors: this.#storage.account.contacts || [author],
+ authors: this.account.contacts || [author],
});
if (event) return event.tags.filter((item) => item[0] !== "d");
@@ -577,7 +578,7 @@ export class Ark {
const altEvent = await this.ndk.fetchEvent({
kinds: [NDKKind.AppHandler],
"#k": [unknownKind],
- authors: this.#storage.account.contacts || [author],
+ authors: this.account.contacts || [author],
});
if (altEvent) return altEvent.tags.filter((item) => item[0] !== "d");
diff --git a/packages/ark/src/components/column/live.tsx b/packages/ark/src/components/column/live.tsx
index 444799d2..ed46ab3f 100644
--- a/packages/ark/src/components/column/live.tsx
+++ b/packages/ark/src/components/column/live.tsx
@@ -1,7 +1,7 @@
import { ChevronUpIcon } from "@lume/icons";
import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk";
import { useEffect, useState } from "react";
-import { useArk } from "../../provider";
+import { useArk } from "../../hooks/useArk";
export function ColumnLiveWidget({
filter,
diff --git a/packages/ark/src/components/column/provider.tsx b/packages/ark/src/components/column/provider.tsx
index 0743cbc3..49876fbf 100644
--- a/packages/ark/src/components/column/provider.tsx
+++ b/packages/ark/src/components/column/provider.tsx
@@ -1,3 +1,4 @@
+import { useStorage } from "@lume/storage";
import { IColumn } from "@lume/types";
import { COL_TYPES } from "@lume/utils";
import {
@@ -8,7 +9,6 @@ import {
useEffect,
useState,
} from "react";
-import { useStorage } from "../../provider";
type ColumnContext = {
columns: IColumn[];
diff --git a/packages/ark/src/components/note/appHandler.tsx b/packages/ark/src/components/note/appHandler.tsx
index 540ddb72..f229f063 100644
--- a/packages/ark/src/components/note/appHandler.tsx
+++ b/packages/ark/src/components/note/appHandler.tsx
@@ -1,6 +1,6 @@
import { NDKAppHandlerEvent } from "@nostr-dev-kit/ndk";
import { useQuery } from "@tanstack/react-query";
-import { useArk } from "../../provider";
+import { useArk } from "../../hooks/useArk";
export function AppHandler({ tag }: { tag: string[] }) {
const ark = useArk();
diff --git a/packages/ark/src/components/note/buttons/zap.tsx b/packages/ark/src/components/note/buttons/zap.tsx
index c47ac145..b48d8700 100644
--- a/packages/ark/src/components/note/buttons/zap.tsx
+++ b/packages/ark/src/components/note/buttons/zap.tsx
@@ -1,6 +1,7 @@
import { webln } from "@getalby/sdk";
import { SendPaymentResponse } from "@getalby/sdk/dist/types";
import { CancelIcon, ZapIcon } from "@lume/icons";
+import { useStorage } from "@lume/storage";
import {
compactNumber,
displayNpub,
@@ -13,8 +14,8 @@ import { QRCodeSVG } from "qrcode.react";
import { useEffect, useRef, useState } from "react";
import CurrencyInput from "react-currency-input-field";
import { useNavigate } from "react-router-dom";
+import { useArk } from "../../../hooks/useArk";
import { useProfile } from "../../../hooks/useProfile";
-import { useArk, useStorage } from "../../../provider";
import { useNoteContext } from "../provider";
export function NoteZap() {
@@ -87,7 +88,7 @@ export function NoteZap() {
useEffect(() => {
async function getWalletConnectURL() {
const uri: string = await invoke("secure_load", {
- key: `${storage.account.pubkey}-nwc`,
+ key: `${ark.account.pubkey}-nwc`,
});
if (uri) setWalletConnectURL(uri);
}
diff --git a/packages/ark/src/components/note/child.tsx b/packages/ark/src/components/note/child.tsx
index cf066af3..d4fa7328 100644
--- a/packages/ark/src/components/note/child.tsx
+++ b/packages/ark/src/components/note/child.tsx
@@ -22,7 +22,7 @@ export function NoteChild({
"\n",
);
- const text = parsedContent;
+ const text = parsedContent as string;
const words = text.split(/( |\n)/);
const hashtags = words.filter((word) => word.startsWith("#"));
diff --git a/packages/ark/src/components/note/content.tsx b/packages/ark/src/components/note/content.tsx
index 92866792..47662cc8 100644
--- a/packages/ark/src/components/note/content.tsx
+++ b/packages/ark/src/components/note/content.tsx
@@ -1,3 +1,4 @@
+import { useStorage } from "@lume/storage";
import {
AUDIOS,
IMAGES,
@@ -15,7 +16,6 @@ import { nip19 } from "nostr-tools";
import { ReactNode, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import reactStringReplace from "react-string-replace";
-import { useStorage } from "../../provider";
import { Hashtag } from "./mentions/hashtag";
import { MentionNote } from "./mentions/note";
import { MentionUser } from "./mentions/user";
diff --git a/packages/ark/src/components/note/nip89.tsx b/packages/ark/src/components/note/nip89.tsx
index 66b4b60a..c747b2e9 100644
--- a/packages/ark/src/components/note/nip89.tsx
+++ b/packages/ark/src/components/note/nip89.tsx
@@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
-import { useArk } from "../../provider";
+import { useArk } from "../../hooks/useArk";
import { AppHandler } from "./appHandler";
import { useNoteContext } from "./provider";
diff --git a/packages/ark/src/components/note/primitives/repost.tsx b/packages/ark/src/components/note/primitives/repost.tsx
index 6e8a90d9..446e1494 100644
--- a/packages/ark/src/components/note/primitives/repost.tsx
+++ b/packages/ark/src/components/note/primitives/repost.tsx
@@ -1,7 +1,7 @@
import { NDKEvent, NostrEvent } from "@nostr-dev-kit/ndk";
import { useQuery } from "@tanstack/react-query";
import { Note } from "..";
-import { useArk } from "../../../provider";
+import { useArk } from "../../../hooks/useArk";
export function RepostNote({
event,
diff --git a/packages/ark/src/context.ts b/packages/ark/src/context.ts
new file mode 100644
index 00000000..918a4536
--- /dev/null
+++ b/packages/ark/src/context.ts
@@ -0,0 +1,4 @@
+import { createContext } from "react";
+import { type Ark } from "./ark";
+
+export const LumeContext = createContext(undefined);
diff --git a/packages/ark/src/hooks/useArk.ts b/packages/ark/src/hooks/useArk.ts
new file mode 100644
index 00000000..8d45b8dc
--- /dev/null
+++ b/packages/ark/src/hooks/useArk.ts
@@ -0,0 +1,10 @@
+import { useContext } from "react";
+import { LumeContext } from "../context";
+
+export const useArk = () => {
+ const context = useContext(LumeContext);
+ if (context === undefined) {
+ throw new Error("Please import Ark Provider to use useArk() hook");
+ }
+ return context;
+};
diff --git a/packages/ark/src/hooks/useEvent.ts b/packages/ark/src/hooks/useEvent.ts
index f07cfec9..0d872e1e 100644
--- a/packages/ark/src/hooks/useEvent.ts
+++ b/packages/ark/src/hooks/useEvent.ts
@@ -1,21 +1,24 @@
-import { useQuery } from '@tanstack/react-query';
-import { useArk } from '../provider';
+import { useQuery } from "@tanstack/react-query";
+import { useArk } from "./useArk";
export function useEvent(id: string) {
- const ark = useArk();
- const { status, isLoading, isError, data } = useQuery({
- queryKey: ['event', id],
- queryFn: async () => {
- const event = await ark.getEventById({ id });
- if (!event)
- throw new Error(`Cannot get event with ${id}, will be retry after 10 seconds`);
- return event;
- },
- refetchOnWindowFocus: false,
- refetchOnMount: false,
- refetchOnReconnect: false,
- retry: 2,
- });
+ const ark = useArk();
+ const { status, isLoading, isError, data } = useQuery({
+ queryKey: ["event", id],
+ queryFn: async () => {
+ const event = await ark.getEventById({ id });
+ if (!event)
+ throw new Error(
+ `Cannot get event with ${id}, will be retry after 10 seconds`,
+ );
+ return event;
+ },
+ refetchOnWindowFocus: false,
+ refetchOnMount: false,
+ refetchOnReconnect: false,
+ staleTime: Infinity,
+ retry: 2,
+ });
- return { status, isLoading, isError, data };
+ return { status, isLoading, isError, data };
}
diff --git a/packages/ark/src/hooks/useProfile.ts b/packages/ark/src/hooks/useProfile.ts
index 3f79d1dd..30c4f734 100644
--- a/packages/ark/src/hooks/useProfile.ts
+++ b/packages/ark/src/hooks/useProfile.ts
@@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
-import { useArk } from "../provider";
+import { useArk } from "./useArk";
export function useProfile(pubkey: string) {
const ark = useArk();
@@ -20,6 +20,7 @@ export function useProfile(pubkey: string) {
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
+ staleTime: Infinity,
retry: 2,
});
diff --git a/packages/ark/src/hooks/useRelay.ts b/packages/ark/src/hooks/useRelay.ts
index 4c38a342..8d29697e 100644
--- a/packages/ark/src/hooks/useRelay.ts
+++ b/packages/ark/src/hooks/useRelay.ts
@@ -1,6 +1,7 @@
+import { useStorage } from "@lume/storage";
import { NDKKind, NDKRelayUrl, NDKTag } from "@nostr-dev-kit/ndk";
import { useMutation, useQueryClient } from "@tanstack/react-query";
-import { useArk, useStorage } from "../provider";
+import { useArk } from "./useArk";
export function useRelay() {
const ark = useArk();
@@ -14,13 +15,13 @@ export function useRelay() {
) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({
- queryKey: ["relays", storage.account.pubkey],
+ queryKey: ["relays", ark.account.pubkey],
});
// Snapshot the previous value
const prevRelays: NDKTag[] = queryClient.getQueryData([
"relays",
- storage.account.pubkey,
+ ark.account.pubkey,
]);
// create new relay list if not exist
@@ -42,7 +43,7 @@ export function useRelay() {
// Optimistically update to the new value
queryClient.setQueryData(
- ["relays", storage.account.pubkey],
+ ["relays", ark.account.pubkey],
(prev: NDKTag[]) => [...prev, ["r", relay, purpose ?? ""]],
);
@@ -51,7 +52,7 @@ export function useRelay() {
},
onSettled: () => {
queryClient.invalidateQueries({
- queryKey: ["relays", storage.account.pubkey],
+ queryKey: ["relays", ark.account.pubkey],
});
},
});
@@ -60,13 +61,13 @@ export function useRelay() {
mutationFn: async (relay: NDKRelayUrl) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({
- queryKey: ["relays", storage.account.pubkey],
+ queryKey: ["relays", ark.account.pubkey],
});
// Snapshot the previous value
const prevRelays: NDKTag[] = queryClient.getQueryData([
"relays",
- storage.account.pubkey,
+ ark.account.pubkey,
]);
if (!prevRelays) return;
@@ -80,14 +81,14 @@ export function useRelay() {
});
// Optimistically update to the new value
- queryClient.setQueryData(["relays", storage.account.pubkey], prevRelays);
+ queryClient.setQueryData(["relays", ark.account.pubkey], prevRelays);
// Return a context object with the snapshotted value
return { prevRelays };
},
onSettled: () => {
queryClient.invalidateQueries({
- queryKey: ["relays", storage.account.pubkey],
+ queryKey: ["relays", ark.account.pubkey],
});
},
});
diff --git a/packages/ark/src/index.ts b/packages/ark/src/index.ts
index a12fbfd1..22a7efc2 100644
--- a/packages/ark/src/index.ts
+++ b/packages/ark/src/index.ts
@@ -1,6 +1,8 @@
export * from "./ark";
+export * from "./context";
export * from "./provider";
export * from "./hooks/useEvent";
+export * from "./hooks/useArk";
export * from "./hooks/useProfile";
export * from "./hooks/useRelay";
export * from "./components/column/provider";
diff --git a/packages/ark/src/provider.tsx b/packages/ark/src/provider.tsx
index bf790a86..ab897a1f 100644
--- a/packages/ark/src/provider.tsx
+++ b/packages/ark/src/provider.tsx
@@ -1,7 +1,7 @@
import { LoaderIcon } from "@lume/icons";
import { NDKCacheAdapterTauri } from "@lume/ndk-cache-tauri";
-import { LumeStorage } from "@lume/storage";
-import { QUOTES, delay, sendNativeNotification } from "@lume/utils";
+import { useStorage } from "@lume/storage";
+import { QUOTES, sendNativeNotification } from "@lume/utils";
import NDK, {
NDKEvent,
NDKKind,
@@ -11,44 +11,28 @@ import NDK, {
NDKRelayAuthPolicies,
} from "@nostr-dev-kit/ndk";
import { fetch } from "@tauri-apps/plugin-http";
-import { locale, platform } from "@tauri-apps/plugin-os";
-import { relaunch } from "@tauri-apps/plugin-process";
-import Database from "@tauri-apps/plugin-sql";
-import { check } from "@tauri-apps/plugin-updater";
import Linkify from "linkify-react";
-import { normalizeRelayUrl, normalizeRelayUrlSet } from "nostr-fetch";
+import { normalizeRelayUrlSet } from "nostr-fetch";
import { PropsWithChildren, useEffect, useState } from "react";
-import { createContext, useContextSelector } from "use-context-selector";
import { Ark } from "./ark";
+import { LumeContext } from "./context";
-type Context = {
- storage: LumeStorage;
- ark: Ark;
-};
-
-const LumeContext = createContext({
- storage: undefined,
- ark: undefined,
-});
-
-const LumeProvider = ({ children }: PropsWithChildren
-
- {isNewVersion ? "Found a new version, updating..." : "Starting..."}
-
+
Starting
);
}
return (
-
- {children}
-
+ {children}
);
};
-
-const useArk = () => {
- const context = useContextSelector(LumeContext, (state) => state.ark);
- if (context === undefined) {
- throw new Error("Please import Ark Provider to use useArk() hook");
- }
- return context;
-};
-
-const useStorage = () => {
- const context = useContextSelector(LumeContext, (state) => state.storage);
- if (context === undefined) {
- throw new Error("Please import Ark Provider to use useStorage() hook");
- }
- return context;
-};
-
-export { LumeProvider, useArk, useStorage };
diff --git a/packages/lume-column-antenas/src/home.tsx b/packages/lume-column-antenas/src/home.tsx
index 461b42ab..4300ed77 100644
--- a/packages/lume-column-antenas/src/home.tsx
+++ b/packages/lume-column-antenas/src/home.tsx
@@ -1,4 +1,4 @@
-import { RepostNote, TextNote, useArk, useStorage } from "@lume/ark";
+import { RepostNote, TextNote, useArk } from "@lume/ark";
import { ArrowRightCircleIcon, LoaderIcon } from "@lume/icons";
import { FETCH_LIMIT } from "@lume/utils";
import { NDKEvent, NDKFilter, NDKKind } from "@nostr-dev-kit/ndk";
@@ -11,7 +11,6 @@ export function HomeRoute({
content,
}: { colKey: string; content: string }) {
const ark = useArk();
- const storage = useStorage();
const ref = useRef();
const cacheKey = `${colKey}-vlist`;
@@ -40,7 +39,7 @@ export function HomeRoute({
filter = {
kinds: [NDKKind.Text, NDKKind.Repost],
"#t": parsed.hashtags.map((item) => item.replace("#", "")),
- authors: storage.account.contacts,
+ authors: ark.account.contacts,
};
} else {
filter = {
diff --git a/packages/lume-column-group/src/components/form.tsx b/packages/lume-column-group/src/components/form.tsx
index e3efe6f3..97844b67 100644
--- a/packages/lume-column-group/src/components/form.tsx
+++ b/packages/lume-column-group/src/components/form.tsx
@@ -1,10 +1,10 @@
-import { useColumnContext, useStorage } from "@lume/ark";
+import { useArk, useColumnContext } from "@lume/ark";
import { CancelIcon, CheckCircleIcon } from "@lume/icons";
import { User } from "@lume/ui";
import { useState } from "react";
export function GroupForm({ id }: { id: number }) {
- const storage = useStorage();
+ const ark = useArk();
const { updateColumn, removeColumn } = useColumnContext();
const [title, setTitle] = useState(`Group-${id}`);
@@ -59,7 +59,7 @@ export function GroupForm({ id }: { id: number }) {
{`${users.length} / ∞`}
- {storage.account?.contacts?.map((item: string) => (
+ {ark.account?.contacts?.map((item: string) => (