refactor
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import { Image } from "@shared/image";
|
||||
|
||||
import { DEFAULT_AVATAR } from "@stores/constants";
|
||||
|
||||
import { useProfile } from "@utils/hooks/useProfile";
|
||||
import { shortenKey } from "@utils/shortenKey";
|
||||
|
||||
export default function User({ pubkey }: { pubkey: string }) {
|
||||
export function User({ pubkey }: { pubkey: string }) {
|
||||
const { user } = useProfile(pubkey);
|
||||
|
||||
return (
|
||||
@@ -15,7 +13,6 @@ export default function User({ pubkey }: { pubkey: string }) {
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-11 w-11 rounded-md object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import EyeOffIcon from "@icons/eyeOff";
|
||||
import EyeOnIcon from "@icons/eyeOn";
|
||||
|
||||
import { onboardingAtom } from "@stores/onboarding";
|
||||
|
||||
import { useSetAtom } from "jotai";
|
||||
import { createAccount } from "@utils/storage";
|
||||
import { generatePrivateKey, getPublicKey, nip19 } from "nostr-tools";
|
||||
import { useMemo, useState } from "react";
|
||||
import { navigate } from "vite-plugin-ssr/client/router";
|
||||
|
||||
export function Page() {
|
||||
const [type, setType] = useState("password");
|
||||
const setOnboarding = useSetAtom(onboardingAtom);
|
||||
const privkey = useMemo(() => generatePrivateKey(), []);
|
||||
|
||||
const pubkey = getPublicKey(privkey);
|
||||
@@ -26,9 +22,12 @@ export function Page() {
|
||||
}
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
setOnboarding((prev) => ({ ...prev, pubkey: pubkey, privkey: privkey }));
|
||||
navigate("/app/auth/create/step-2");
|
||||
const submit = async () => {
|
||||
const account = await createAccount(npub, pubkey, privkey, null, 1);
|
||||
|
||||
if (account) {
|
||||
navigate("/app/auth/create/step-2");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,38 +1,53 @@
|
||||
import { AvatarUploader } from "@shared/avatarUploader";
|
||||
import { Image } from "@shared/image";
|
||||
|
||||
import { DEFAULT_AVATAR } from "@stores/constants";
|
||||
import { onboardingAtom } from "@stores/onboarding";
|
||||
|
||||
import { useAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { DEFAULT_AVATAR, WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { navigate } from "vite-plugin-ssr/client/router";
|
||||
|
||||
export function Page() {
|
||||
const pool: any = useContext(RelayContext);
|
||||
|
||||
const { account } = useActiveAccount();
|
||||
|
||||
const [image, setImage] = useState(DEFAULT_AVATAR);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [onboarding, setOnboarding] = useAtom(onboardingAtom);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { isDirty, isValid },
|
||||
} = useForm({
|
||||
defaultValues: async () => {
|
||||
if (onboarding.metadata) {
|
||||
return onboarding.metadata;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
} = useForm();
|
||||
|
||||
const onSubmit = (data: any) => {
|
||||
setLoading(true);
|
||||
setOnboarding((prev) => ({ ...prev, metadata: data }));
|
||||
navigate("/app/auth/create/step-3");
|
||||
|
||||
const event: any = {
|
||||
content: JSON.stringify(data),
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 0,
|
||||
pubkey: account.pubkey,
|
||||
tags: [],
|
||||
};
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
|
||||
// redirect to step 3
|
||||
setTimeout(
|
||||
() =>
|
||||
navigate("/app/auth/create/step-3", {
|
||||
overwriteLastHistoryEntry: true,
|
||||
}),
|
||||
2000,
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import User from "@app/auth/components/user";
|
||||
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
|
||||
import { User } from "@app/auth/components/user";
|
||||
import CheckCircleIcon from "@icons/checkCircle";
|
||||
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { onboardingAtom } from "@stores/onboarding";
|
||||
|
||||
import { createAccount, createPleb } from "@utils/storage";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import { updateAccount } from "@utils/storage";
|
||||
import { arrayToNIP02 } from "@utils/transform";
|
||||
|
||||
import { useAtom } from "jotai";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useState } from "react";
|
||||
import { navigate } from "vite-plugin-ssr/client/router";
|
||||
|
||||
@@ -117,9 +111,10 @@ const initialList = [
|
||||
export function Page() {
|
||||
const pool: any = useContext(RelayContext);
|
||||
|
||||
const { account } = useActiveAccount();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [follows, setFollows] = useState([]);
|
||||
const [onboarding] = useAtom(onboardingAtom);
|
||||
|
||||
// toggle follow state
|
||||
const toggleFollow = (pubkey: string) => {
|
||||
@@ -129,68 +124,37 @@ export function Page() {
|
||||
setFollows(arr);
|
||||
};
|
||||
|
||||
const broadcastAccount = () => {
|
||||
// build event
|
||||
const event: any = {
|
||||
content: JSON.stringify(onboarding.metadata),
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 0,
|
||||
pubkey: onboarding.pubkey,
|
||||
tags: [],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, onboarding.privkey);
|
||||
// broadcast
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
};
|
||||
|
||||
const broadcastContacts = () => {
|
||||
const nip02 = arrayToNIP02(follows);
|
||||
// build event
|
||||
const event: any = {
|
||||
content: "",
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 3,
|
||||
pubkey: onboarding.pubkey,
|
||||
tags: nip02,
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, onboarding.privkey);
|
||||
// broadcast
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
};
|
||||
|
||||
// save follows to database then broadcast
|
||||
const submit = async () => {
|
||||
setLoading(true);
|
||||
|
||||
const followsIncludeSelf = follows.concat([onboarding.pubkey]);
|
||||
// insert to database
|
||||
createAccount(
|
||||
onboarding.pubkey,
|
||||
onboarding.privkey,
|
||||
onboarding.metadata,
|
||||
arrayToNIP02(followsIncludeSelf),
|
||||
1,
|
||||
)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
for (const tag of follows) {
|
||||
fetch(`https://us.rbr.bio/${tag}/metadata.json`)
|
||||
.then((data) => data.json())
|
||||
.then((data) => createPleb(tag, data ?? ""));
|
||||
}
|
||||
broadcastAccount();
|
||||
broadcastContacts();
|
||||
setTimeout(
|
||||
() => navigate("/", { overwriteLastHistoryEntry: true }),
|
||||
2000,
|
||||
);
|
||||
} else {
|
||||
console.error();
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
// update account follows
|
||||
updateAccount("follows", follows, account.pubkey);
|
||||
|
||||
const tags = arrayToNIP02(follows);
|
||||
|
||||
const event: any = {
|
||||
content: "",
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 3,
|
||||
pubkey: account.pubkey,
|
||||
tags: tags,
|
||||
};
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
|
||||
// redirect to step 3
|
||||
setTimeout(
|
||||
() =>
|
||||
navigate("/app/prefetch", {
|
||||
overwriteLastHistoryEntry: true,
|
||||
}),
|
||||
2000,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { onboardingAtom } from "@stores/onboarding";
|
||||
|
||||
import { useSetAtom } from "jotai";
|
||||
import { createAccount } from "@utils/storage";
|
||||
import { getPublicKey, nip19 } from "nostr-tools";
|
||||
import { Resolver, useForm } from "react-hook-form";
|
||||
import { navigate } from "vite-plugin-ssr/client/router";
|
||||
@@ -24,8 +22,6 @@ const resolver: Resolver<FormValues> = async (values) => {
|
||||
};
|
||||
|
||||
export function Page() {
|
||||
const setOnboardingPrivkey = useSetAtom(onboardingAtom);
|
||||
|
||||
const {
|
||||
register,
|
||||
setError,
|
||||
@@ -42,8 +38,14 @@ export function Page() {
|
||||
}
|
||||
|
||||
if (typeof getPublicKey(privkey) === "string") {
|
||||
setOnboardingPrivkey((prev) => ({ ...prev, privkey: privkey }));
|
||||
navigate("/app/auth/import/step-2");
|
||||
const pubkey = getPublicKey(privkey);
|
||||
const npub = nip19.npubEncode(pubkey);
|
||||
|
||||
const account = await createAccount(npub, pubkey, privkey, null, 1);
|
||||
|
||||
if (account) {
|
||||
navigate("/app/auth/import/step-2");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setError("key", {
|
||||
|
||||
@@ -1,85 +1,55 @@
|
||||
import { Image } from "@shared/image";
|
||||
import { User } from "@app/auth/components/user";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
|
||||
import { DEFAULT_AVATAR, READONLY_RELAYS } from "@stores/constants";
|
||||
import { onboardingAtom } from "@stores/onboarding";
|
||||
|
||||
import { shortenKey } from "@utils/shortenKey";
|
||||
import { createAccount, createPleb } from "@utils/storage";
|
||||
|
||||
import { useAtom } from "jotai";
|
||||
import { getPublicKey } from "nostr-tools";
|
||||
import { useContext, useMemo, useState } from "react";
|
||||
import { READONLY_RELAYS } from "@stores/constants";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import { updateAccount } from "@utils/storage";
|
||||
import { nip02ToArray } from "@utils/transform";
|
||||
import { useContext, useState } from "react";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { navigate } from "vite-plugin-ssr/client/router";
|
||||
|
||||
export function Page() {
|
||||
const pool: any = useContext(RelayContext);
|
||||
|
||||
const { account } = useActiveAccount();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [onboarding, setOnboarding] = useAtom(onboardingAtom);
|
||||
const pubkey = useMemo(
|
||||
() => (onboarding.privkey ? getPublicKey(onboarding.privkey) : ""),
|
||||
[onboarding.privkey],
|
||||
);
|
||||
const [follows, setFollows] = useState([]);
|
||||
|
||||
const { data, error } = useSWRSubscription(
|
||||
pubkey ? pubkey : null,
|
||||
(key, { next }) => {
|
||||
const unsubscribe = pool.subscribe(
|
||||
[
|
||||
{
|
||||
kinds: [0, 3],
|
||||
authors: [key],
|
||||
},
|
||||
],
|
||||
READONLY_RELAYS,
|
||||
(event: any) => {
|
||||
switch (event.kind) {
|
||||
case 0:
|
||||
// update state
|
||||
next(null, JSON.parse(event.content));
|
||||
// create account
|
||||
setOnboarding((prev) => ({ ...prev, metadata: event.content }));
|
||||
break;
|
||||
case 3:
|
||||
setOnboarding((prev) => ({ ...prev, follows: event.tags }));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
useSWRSubscription(account ? account.pubkey : null, (key: string) => {
|
||||
const unsubscribe = pool.subscribe(
|
||||
[
|
||||
{
|
||||
kinds: [3],
|
||||
authors: [key],
|
||||
},
|
||||
);
|
||||
],
|
||||
READONLY_RELAYS,
|
||||
(event: any) => {
|
||||
setFollows(event.tags);
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
},
|
||||
);
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
});
|
||||
|
||||
const submit = () => {
|
||||
// show loading indicator
|
||||
setLoading(true);
|
||||
|
||||
const follows = onboarding.follows.concat([["p", pubkey]]);
|
||||
// insert to database
|
||||
createAccount(pubkey, onboarding.privkey, onboarding.metadata, follows, 1)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
for (const tag of onboarding.follows) {
|
||||
fetch(`https://rbr.bio/${tag[1]}/metadata.json`)
|
||||
.then((data) => data.json())
|
||||
.then((data) => createPleb(tag[1], data ?? ""));
|
||||
}
|
||||
setTimeout(
|
||||
() => navigate("/", { overwriteLastHistoryEntry: true }),
|
||||
2000,
|
||||
);
|
||||
} else {
|
||||
console.error();
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
// follows as list
|
||||
const followsList = nip02ToArray(follows);
|
||||
|
||||
// update account follows
|
||||
updateAccount("follows", followsList, account.pubkey);
|
||||
|
||||
// redirect to home
|
||||
setTimeout(
|
||||
() => navigate("/app/prefetch", { overwriteLastHistoryEntry: true }),
|
||||
2000,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -91,8 +61,7 @@ export function Page() {
|
||||
</h1>
|
||||
</div>
|
||||
<div className="w-full rounded-lg border border-zinc-800 bg-zinc-900 p-4">
|
||||
{error && <div>Failed to load profile</div>}
|
||||
{!data ? (
|
||||
{!account ? (
|
||||
<div className="w-full">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-11 w-11 animate-pulse rounded-lg bg-zinc-800" />
|
||||
@@ -104,21 +73,7 @@ export function Page() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Image
|
||||
className="relative inline-flex h-11 w-11 rounded-lg ring-2 ring-zinc-900"
|
||||
src={data.picture || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
/>
|
||||
<div>
|
||||
<h3 className="font-medium leading-none text-white">
|
||||
{data.display_name || data.name}
|
||||
</h3>
|
||||
<p className="text-base text-zinc-400">
|
||||
{data.nip05 || shortenKey(pubkey)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<User pubkey={account.pubkey} />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => submit()}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import { createChannel } from "@utils/storage";
|
||||
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { Fragment, useContext, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useSWRConfig } from "swr";
|
||||
@@ -56,7 +56,7 @@ export default function ChannelCreateModal() {
|
||||
tags: [],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish channel
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
import { useResetAtom } from "jotai/utils";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function ChannelMessageForm({
|
||||
@@ -50,7 +50,7 @@ export default function ChannelMessageForm({
|
||||
tags: tags,
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { useAtom } from "jotai";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { Fragment, useContext, useState } from "react";
|
||||
|
||||
export default function MessageHideButton({ id }: { id: string }) {
|
||||
@@ -40,7 +40,7 @@ export default function MessageHideButton({ id }: { id: string }) {
|
||||
tags: [["e", id]],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { useAtom } from "jotai";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { Fragment, useContext, useState } from "react";
|
||||
|
||||
export default function MessageMuteButton({ pubkey }: { pubkey: string }) {
|
||||
@@ -40,7 +40,7 @@ export default function MessageMuteButton({ pubkey }: { pubkey: string }) {
|
||||
tags: [["p", pubkey]],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import { getChannel } from "@utils/storage";
|
||||
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { Fragment, useContext, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function ChannelUpdateModal({ id }: { id: string }) {
|
||||
tags: [["e", id]],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish channel
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
|
||||
@@ -11,7 +11,7 @@ import useSWRSubscription from "swr/subscription";
|
||||
const fetcher = async ([, id]) => {
|
||||
const result = await getChannel(id);
|
||||
if (result) {
|
||||
return JSON.parse(result.metadata);
|
||||
return result;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -38,14 +38,14 @@ export default function ChatsListItem({ pubkey }: { pubkey: string }) {
|
||||
>
|
||||
<div className="relative h-5 w-5 shrink-0 rounded">
|
||||
<Image
|
||||
src={user.picture || DEFAULT_AVATAR}
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-5 w-5 rounded bg-white object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="max-w-[9rem] truncate font-medium text-zinc-200 group-hover:text-white">
|
||||
{user.nip05 || user.name || shortenKey(pubkey)}
|
||||
{user?.nip05 || user.name || shortenKey(pubkey)}
|
||||
</h5>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -2,16 +2,17 @@ import ChatsListItem from "@app/chat/components/item";
|
||||
import ChatsListSelfItem from "@app/chat/components/self";
|
||||
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import { getChats } from "@utils/storage";
|
||||
import { getChatsByPubkey } from "@utils/storage";
|
||||
|
||||
import useSWR from "swr";
|
||||
|
||||
const fetcher = ([, account]) => getChats(account);
|
||||
const fetcher = ([, pubkey]) => getChatsByPubkey(pubkey);
|
||||
|
||||
export default function ChatsList() {
|
||||
const { account, isLoading, isError } = useActiveAccount();
|
||||
|
||||
const { data: chats, error }: any = useSWR(
|
||||
!isLoading && !isError && account ? ["chats", account] : null,
|
||||
!isLoading && !isError && account ? ["chats", account.pubkey] : null,
|
||||
fetcher,
|
||||
);
|
||||
|
||||
@@ -30,8 +31,8 @@ export default function ChatsList() {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
chats.map((item: { pubkey: string }) => (
|
||||
<ChatsListItem key={item.pubkey} pubkey={item.pubkey} />
|
||||
chats.map((item) => (
|
||||
<ChatsListItem key={item.sender_pubkey} pubkey={item.sender_pubkey} />
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { useAtom } from "jotai";
|
||||
import { useResetAtom } from "jotai/utils";
|
||||
import { getEventHash, nip04, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature, nip04 } from "nostr-tools";
|
||||
import { useCallback, useContext } from "react";
|
||||
|
||||
export default function ChatMessageForm({
|
||||
@@ -40,7 +40,7 @@ export default function ChatMessageForm({
|
||||
tags: [["p", receiverPubkey]],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// reset state
|
||||
|
||||
@@ -15,7 +15,6 @@ export default function ChatsListSelfItem() {
|
||||
const pagePubkey = searchParams.pubkey;
|
||||
|
||||
const { account, isLoading, isError } = useActiveAccount();
|
||||
const profile = account ? JSON.parse(account.metadata) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -39,14 +38,14 @@ export default function ChatsListSelfItem() {
|
||||
>
|
||||
<div className="relative h-5 w-5 shrink-0 rounded">
|
||||
<Image
|
||||
src={profile?.picture || DEFAULT_AVATAR}
|
||||
src={account?.picture || DEFAULT_AVATAR}
|
||||
alt={account.pubkey}
|
||||
className="h-5 w-5 rounded bg-white object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="inline-flex items-baseline">
|
||||
<h5 className="max-w-[9rem] truncate font-medium text-zinc-200">
|
||||
{profile?.nip05 || profile?.name || shortenKey(account.pubkey)}
|
||||
{account?.nip05 || account?.name || shortenKey(account.pubkey)}
|
||||
</h5>
|
||||
<span className="text-zinc-600">(you)</span>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { getActiveAccount } from "@utils/storage";
|
||||
|
||||
import useSWR from "swr";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import { navigate } from "vite-plugin-ssr/client/router";
|
||||
|
||||
const fetcher = () => getActiveAccount();
|
||||
|
||||
export function Page() {
|
||||
const { data, isLoading } = useSWR("account", fetcher, {
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
});
|
||||
const { account, isLoading } = useActiveAccount();
|
||||
|
||||
if (!isLoading && !data) {
|
||||
if (!isLoading && !account) {
|
||||
navigate("/app/auth", { overwriteLastHistoryEntry: true });
|
||||
}
|
||||
|
||||
if (!isLoading && data) {
|
||||
navigate("/app/inital-data", { overwriteLastHistoryEntry: true });
|
||||
if (!isLoading && account) {
|
||||
navigate("/app/prefetch", { overwriteLastHistoryEntry: true });
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import NoteReply from "@app/note/components/metadata/reply";
|
||||
import NoteRepost from "@app/note/components/metadata/repost";
|
||||
import NoteZap from "@app/note/components/metadata/zap";
|
||||
import ZapIcon from "@shared/icons/zap";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { READONLY_RELAYS } from "@stores/constants";
|
||||
import { decode } from "light-bolt11-decoder";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { dateToUnix } from "@utils/date";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
|
||||
export default function NoteLike({
|
||||
@@ -35,7 +35,7 @@ export default function NoteLike({
|
||||
pubkey: account.pubkey,
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
// publish event to all relays
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// update state
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { compactNumber } from "@utils/number";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { Fragment, useContext, useEffect, useState } from "react";
|
||||
|
||||
export default function NoteReply({
|
||||
@@ -24,7 +24,6 @@ export default function NoteReply({
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const { account, isLoading, isError } = useActiveAccount();
|
||||
const profile = account ? JSON.parse(account.metadata) : null;
|
||||
|
||||
const closeModal = () => {
|
||||
setIsOpen(false);
|
||||
@@ -44,7 +43,7 @@ export default function NoteReply({
|
||||
tags: [["e", id]],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish event
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
@@ -106,7 +105,7 @@ export default function NoteReply({
|
||||
<div>
|
||||
<div className="relative h-11 w-11 shrink-0 overflow-hidden rounded-md border border-white/10">
|
||||
<Image
|
||||
src={profile?.picture}
|
||||
src={account?.picture}
|
||||
alt="user's avatar"
|
||||
className="h-11 w-11 rounded-md object-cover"
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { dateToUnix } from "@utils/date";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { compactNumber } from "@utils/number";
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
|
||||
export default function NoteRepost({
|
||||
@@ -36,7 +36,7 @@ export default function NoteRepost({
|
||||
pubkey: account.pubkey,
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
// publish event to all relays
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// update state
|
||||
|
||||
@@ -6,15 +6,14 @@ import { WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { dateToUnix } from "@utils/date";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
|
||||
import { getEventHash, signEvent } from "nostr-tools";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
export default function NoteReplyForm({ id }: { id: string }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const { account, isLoading, isError } = useActiveAccount();
|
||||
|
||||
const { account, isLoading, isError } = useActiveAccount();
|
||||
const [value, setValue] = useState("");
|
||||
const profile = account ? JSON.parse(account.metadata) : null;
|
||||
|
||||
const submitEvent = () => {
|
||||
if (!isLoading && !isError && account) {
|
||||
@@ -26,7 +25,7 @@ export default function NoteReplyForm({ id }: { id: string }) {
|
||||
tags: [["e", id]],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, account.privkey);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
@@ -42,7 +41,7 @@ export default function NoteReplyForm({ id }: { id: string }) {
|
||||
<div>
|
||||
<div className="relative h-9 w-9 shrink-0 overflow-hidden rounded-md">
|
||||
<Image
|
||||
src={profile?.picture}
|
||||
src={account?.picture}
|
||||
alt={account?.pubkey}
|
||||
className="h-9 w-9 rounded-md object-cover"
|
||||
/>
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
|
||||
import LumeIcon from "@icons/lume";
|
||||
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { READONLY_RELAYS } from "@stores/constants";
|
||||
|
||||
import { dateToUnix, getHourAgo } from "@utils/date";
|
||||
import { useActiveAccount } from "@utils/hooks/useActiveAccount";
|
||||
import {
|
||||
addToBlacklist,
|
||||
countTotalLongNotes,
|
||||
countTotalNotes,
|
||||
createChat,
|
||||
createNote,
|
||||
getActiveAccount,
|
||||
getLastLogin,
|
||||
updateLastLogin,
|
||||
} from "@utils/storage";
|
||||
import { getParentID, nip02ToArray } from "@utils/transform";
|
||||
|
||||
import { useContext, useEffect, useRef } from "react";
|
||||
import { getParentID } from "@utils/transform";
|
||||
import { useCallback, useContext, useRef } from "react";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { navigate } from "vite-plugin-ssr/client/router";
|
||||
|
||||
function isJSON(str: string) {
|
||||
@@ -29,79 +24,68 @@ function isJSON(str: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let lastLogin: string;
|
||||
let totalNotes: number;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
lastLogin = await getLastLogin();
|
||||
totalNotes = await countTotalNotes();
|
||||
}
|
||||
|
||||
export function Page() {
|
||||
const pool: any = useContext(RelayContext);
|
||||
|
||||
const now = useRef(new Date());
|
||||
const eose = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
let unsubscribe: () => void;
|
||||
let timeout: any;
|
||||
const { account, isLoading, isError } = useActiveAccount();
|
||||
|
||||
const fetchInitalData = async () => {
|
||||
const account = await getActiveAccount();
|
||||
const lastLogin = await getLastLogin();
|
||||
const notes = await countTotalNotes();
|
||||
const longNotes = await countTotalLongNotes();
|
||||
const getQuery = useCallback(() => {
|
||||
const query = [];
|
||||
const follows = JSON.parse(account.follows);
|
||||
|
||||
const follows = nip02ToArray(JSON.parse(account.follows));
|
||||
const query = [];
|
||||
let queryNoteSince: number;
|
||||
let querySince: number;
|
||||
|
||||
let sinceNotes: number;
|
||||
let sinceLongNotes: number;
|
||||
|
||||
if (notes === 0) {
|
||||
sinceNotes = dateToUnix(getHourAgo(48, now.current));
|
||||
if (totalNotes === 0) {
|
||||
queryNoteSince = dateToUnix(getHourAgo(48, now.current));
|
||||
} else {
|
||||
if (parseInt(lastLogin) > 0) {
|
||||
queryNoteSince = parseInt(lastLogin);
|
||||
} else {
|
||||
if (parseInt(lastLogin) > 0) {
|
||||
sinceNotes = parseInt(lastLogin);
|
||||
} else {
|
||||
sinceNotes = dateToUnix(getHourAgo(48, now.current));
|
||||
}
|
||||
queryNoteSince = dateToUnix(getHourAgo(48, now.current));
|
||||
}
|
||||
}
|
||||
|
||||
if (longNotes === 0) {
|
||||
sinceLongNotes = 0;
|
||||
} else {
|
||||
if (parseInt(lastLogin) > 0) {
|
||||
sinceLongNotes = parseInt(lastLogin);
|
||||
} else {
|
||||
sinceLongNotes = 0;
|
||||
}
|
||||
}
|
||||
// kind 1 (notes) query
|
||||
query.push({
|
||||
kinds: [1, 6, 1063],
|
||||
authors: follows,
|
||||
since: queryNoteSince,
|
||||
});
|
||||
|
||||
// kind 1 (notes) query
|
||||
query.push({
|
||||
kinds: [1, 6, 1063],
|
||||
authors: follows,
|
||||
since: sinceNotes,
|
||||
until: dateToUnix(now.current),
|
||||
});
|
||||
// kind 4 (chats) query
|
||||
query.push({
|
||||
kinds: [4],
|
||||
"#p": [account.pubkey],
|
||||
since: querySince,
|
||||
});
|
||||
|
||||
// kind 4 (chats) query
|
||||
query.push({
|
||||
kinds: [4],
|
||||
"#p": [account.pubkey],
|
||||
since: 0,
|
||||
until: dateToUnix(now.current),
|
||||
});
|
||||
// kind 43, 43 (mute user, hide message) query
|
||||
query.push({
|
||||
authors: [account.pubkey],
|
||||
kinds: [43, 44],
|
||||
since: querySince,
|
||||
});
|
||||
|
||||
// kind 43, 43 (mute user, hide message) query
|
||||
query.push({
|
||||
authors: [account.pubkey],
|
||||
kinds: [43, 44],
|
||||
since: 0,
|
||||
until: dateToUnix(now.current),
|
||||
});
|
||||
return query;
|
||||
}, [account.follows]);
|
||||
|
||||
// kind 30023 (long post) query
|
||||
query.push({
|
||||
kinds: [30023],
|
||||
since: sinceLongNotes,
|
||||
until: dateToUnix(now.current),
|
||||
});
|
||||
|
||||
// subscribe relays
|
||||
unsubscribe = pool.subscribe(
|
||||
useSWRSubscription(
|
||||
!isLoading && !isError && account ? "prefetch" : null,
|
||||
() => {
|
||||
const query = getQuery();
|
||||
const unsubscribe = pool.subscribe(
|
||||
query,
|
||||
READONLY_RELAYS,
|
||||
(event: any) => {
|
||||
@@ -124,9 +108,13 @@ export function Page() {
|
||||
}
|
||||
// chat
|
||||
case 4:
|
||||
if (event.pubkey !== account.pubkey) {
|
||||
createChat(account.id, event.pubkey, event.created_at);
|
||||
}
|
||||
createChat(
|
||||
event.id,
|
||||
account.pubkey,
|
||||
event.pubkey,
|
||||
event.content,
|
||||
event.created_at,
|
||||
);
|
||||
break;
|
||||
// repost
|
||||
case 6:
|
||||
@@ -165,47 +153,24 @@ export function Page() {
|
||||
"",
|
||||
);
|
||||
break;
|
||||
// long post
|
||||
case 30023: {
|
||||
// insert event to local database
|
||||
const verifyMetadata = isJSON(event.tags);
|
||||
if (verifyMetadata) {
|
||||
createNote(
|
||||
event.id,
|
||||
account.id,
|
||||
event.pubkey,
|
||||
event.kind,
|
||||
event.tags,
|
||||
event.content,
|
||||
event.created_at,
|
||||
"",
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
() => {
|
||||
updateLastLogin(dateToUnix(now.current));
|
||||
timeout = setTimeout(() => {
|
||||
eose.current += 1;
|
||||
if (eose.current === READONLY_RELAYS.length) {
|
||||
navigate("/app/space", { overwriteLastHistoryEntry: true });
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
fetchInitalData().catch(console.error);
|
||||
|
||||
return () => {
|
||||
if (unsubscribe) {
|
||||
return () => {
|
||||
unsubscribe();
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [pool]);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">
|
||||
Reference in New Issue
Block a user