From 29c7075f227f0ba10221656c066414d7c4e59fd6 Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Thu, 23 Mar 2023 08:37:39 +0700 Subject: [PATCH] updated profile page --- src/components/columns/account/active.tsx | 5 +- src/components/profile/followers.tsx | 24 ++ src/components/profile/follows.tsx | 23 ++ src/components/profile/metadata.tsx | 68 ++++++ src/components/profile/notes.tsx | 31 +++ src/components/user/extend.tsx | 13 +- src/pages/profile/update.tsx | 227 ------------------ .../{profile/index.tsx => users/[id].tsx} | 56 ++--- 8 files changed, 173 insertions(+), 274 deletions(-) create mode 100644 src/components/profile/followers.tsx create mode 100644 src/components/profile/follows.tsx create mode 100644 src/components/profile/metadata.tsx create mode 100644 src/components/profile/notes.tsx delete mode 100644 src/pages/profile/update.tsx rename src/pages/{profile/index.tsx => users/[id].tsx} (53%) diff --git a/src/components/columns/account/active.tsx b/src/components/columns/account/active.tsx index fdbce148..2c3e744a 100644 --- a/src/components/columns/account/active.tsx +++ b/src/components/columns/account/active.tsx @@ -19,10 +19,7 @@ export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any } const [currentUser]: any = useLocalStorage('current-user'); const openProfile = () => { - router.push({ - pathname: '/profile', - query: { id: currentUser.id }, - }); + router.push(`/users/${currentUser.id}`); }; // save follows to database diff --git a/src/components/profile/followers.tsx b/src/components/profile/followers.tsx new file mode 100644 index 00000000..febb62c7 --- /dev/null +++ b/src/components/profile/followers.tsx @@ -0,0 +1,24 @@ +import { RelayContext } from '@components/contexts/relay'; + +import useLocalStorage from '@rehooks/local-storage'; +import destr from 'destr'; +import { Author } from 'nostr-relaypool'; +import { useContext, useEffect, useState } from 'react'; + +export default function ProfileFollowers({ id }: { id: string }) { + const relayPool: any = useContext(RelayContext); + const [relays]: any = useLocalStorage('relays'); + + const [followers, setFollowers] = useState(null); + + useEffect(() => { + const user = new Author(relayPool, relays, id); + user.followers((res) => setFollowers(destr(res.tags)), 0, 100); + }, [id, relayPool, relays]); + + return ( +
+ {followers && followers.map((follower, index) =>

{follower[1]}

)} +
+ ); +} diff --git a/src/components/profile/follows.tsx b/src/components/profile/follows.tsx new file mode 100644 index 00000000..9ba8b4ed --- /dev/null +++ b/src/components/profile/follows.tsx @@ -0,0 +1,23 @@ +import { RelayContext } from '@components/contexts/relay'; + +import useLocalStorage from '@rehooks/local-storage'; +import { Author } from 'nostr-relaypool'; +import { useContext, useEffect, useState } from 'react'; + +export default function ProfileFollows({ id }: { id: string }) { + const relayPool: any = useContext(RelayContext); + const [relays]: any = useLocalStorage('relays'); + + const [follows, setFollows] = useState(null); + + useEffect(() => { + const user = new Author(relayPool, relays, id); + user.follows((res) => setFollows(res), 0); + }, [id, relayPool, relays]); + + return ( +
+ {follows && follows.map((follow, index) =>

{follow.pubkey}

)} +
+ ); +} diff --git a/src/components/profile/metadata.tsx b/src/components/profile/metadata.tsx new file mode 100644 index 00000000..8b3b3cdc --- /dev/null +++ b/src/components/profile/metadata.tsx @@ -0,0 +1,68 @@ +import { RelayContext } from '@components/contexts/relay'; +import { ImageWithFallback } from '@components/imageWithFallback'; + +import { truncate } from '@utils/truncate'; + +import useLocalStorage from '@rehooks/local-storage'; +import Avatar from 'boring-avatars'; +import destr from 'destr'; +import Image from 'next/image'; +import { Author } from 'nostr-relaypool'; +import { useContext, useEffect, useState } from 'react'; + +const DEFAULT_BANNER = 'https://bafybeiacwit7hjmdefqggxqtgh6ht5dhth7ndptwn2msl5kpkodudsr7py.ipfs.w3s.link/banner-1.jpg'; + +export default function ProfileMetadata({ id }: { id: string }) { + const relayPool: any = useContext(RelayContext); + const [relays]: any = useLocalStorage('relays'); + + const [profile, setProfile] = useState(null); + + useEffect(() => { + const user = new Author(relayPool, relays, id); + user.metaData((res) => setProfile(destr(res.content)), 0); + }, [id, relayPool, relays]); + + return ( + <> +
+
+ user's banner +
+
+
+ {profile?.picture ? ( + + ) : ( + + )} +
+
+
+
+
+
+

+ {profile?.display_name || profile?.name} +

+ + {profile?.username || (id && truncate(id, 16, ' .... '))} + +
+
{profile?.about}
+
+
+ + ); +} diff --git a/src/components/profile/notes.tsx b/src/components/profile/notes.tsx new file mode 100644 index 00000000..f3f9c979 --- /dev/null +++ b/src/components/profile/notes.tsx @@ -0,0 +1,31 @@ +import { RelayContext } from '@components/contexts/relay'; +import { Content } from '@components/note/content'; + +import useLocalStorage from '@rehooks/local-storage'; +import { Author } from 'nostr-relaypool'; +import { useContext, useEffect, useState } from 'react'; + +export default function ProfileNotes({ id }: { id: string }) { + const relayPool: any = useContext(RelayContext); + const [relays]: any = useLocalStorage('relays'); + + const [data, setData] = useState([]); + + useEffect(() => { + const user = new Author(relayPool, relays, id); + user.text((res) => setData((data) => [...data, res]), 0, 100); + }, [id, relayPool, relays]); + + return ( +
+ {data.map((item) => ( +
+ +
+ ))} +
+ ); +} diff --git a/src/components/user/extend.tsx b/src/components/user/extend.tsx index 48a1ea70..8adca8b4 100644 --- a/src/components/user/extend.tsx +++ b/src/components/user/extend.tsx @@ -8,14 +8,21 @@ import { fetch } from '@tauri-apps/api/http'; import Avatar from 'boring-avatars'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import { useRouter } from 'next/router'; import { memo, useCallback, useContext, useEffect, useState } from 'react'; dayjs.extend(relativeTime); export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: string; time: any }) { + const router = useRouter(); const { db }: any = useContext(DatabaseContext); const [profile, setProfile] = useState(null); + const openUserPage = (e) => { + e.stopPropagation(); + router.push(`/users/${pubkey}`); + }; + const fetchProfile = useCallback(async (id: string) => { const res = await fetch(`https://rbr.bio/${id}/metadata.json`, { method: 'GET', @@ -54,8 +61,8 @@ export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: s }, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]); return ( -
-
+
openUserPage(e)} className="group flex items-start gap-2"> +
{profile?.picture ? (
- + {profile?.display_name || profile?.name || truncate(pubkey, 16, ' .... ')} ยท diff --git a/src/pages/profile/update.tsx b/src/pages/profile/update.tsx deleted file mode 100644 index 31ca0fa5..00000000 --- a/src/pages/profile/update.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import BaseLayout from '@layouts/base'; -import UserLayout from '@layouts/user'; - -import { RelayContext } from '@components/contexts/relay'; - -import { dateToUnix } from '@utils/getDate'; - -import { useLocalStorage } from '@rehooks/local-storage'; -import { useRouter } from 'next/router'; -import { getEventHash, signEvent } from 'nostr-tools'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useState } from 'react'; -import { useForm } from 'react-hook-form'; -import Database from 'tauri-plugin-sql-api'; - -type FormValues = { - display_name: string; - name: string; - username: string; - picture: string; - banner: string; - about: string; - website: string; -}; - -// TODO: update the design -export default function Page() { - const relayPool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); - - const router = useRouter(); - const [loading, setLoading] = useState(false); - - const [currentUser]: any = useLocalStorage('current-user'); - const profile = - currentUser.metadata !== undefined ? JSON.parse(currentUser.metadata) : { display_name: null, username: null }; - - const { - register, - handleSubmit, - formState: { errors, isDirty, isValid }, - } = useForm(); - - const onSubmit = async (data: any) => { - setLoading(true); - - // publish account to relays - const event: any = { - content: JSON.stringify(data), - created_at: dateToUnix(), - kind: 0, - pubkey: currentUser.id, - tags: [], - }; - - event.id = getEventHash(event); - event.sig = signEvent(event, currentUser.privkey); - - relayPool.publish(event, relays); - - // save account to database - const db = await Database.load('sqlite:lume.db'); - await db.execute(`UPDATE accounts SET metadata = '${JSON.stringify(data)}' WHERE pubkey = "${currentUser.id}"`); - - // set currentUser in global state - currentUser.set({ - metadata: JSON.stringify(data), - npub: currentUser.npub, - privkey: currentUser.privkey, - pubkey: currentUser.id, - }); - - // redirect to newsfeed - setTimeout(() => { - setLoading(false); - router.reload(); - }, 1500); - }; - - return ( -
-
-

- Update profile -

-

- Your profile will be published to all relays, as long as you have the private key, you always can recover your - profile in any client -

-
-
-
-
- -
-
-
- -
- {errors.display_name &&

{errors.display_name.message}

}
-
-
-
-
- -
-
-
- -
- {errors.name &&

{errors.name.message}

}
-
-
-
-
- -
-
-
- -
- {errors.username &&

{errors.username.message}

}
-
-
-
-
- -
-
-
- -
- {errors.picture &&

{errors.picture.message}

}
-
-
-
-
- -
-
-
- -
- {errors.banner &&

{errors.banner.message}

}
-
-
-
-
- -
-
-
-