From 3aeb70f2340fea7fc08e733ec1d71a3b30386f21 Mon Sep 17 00:00:00 2001
From: Ren Amamiya <123083837+reyamir@users.noreply.github.com>
Date: Sun, 16 Apr 2023 16:43:17 +0700
Subject: [PATCH] added kind 6 to newsfeed and changed useMetadata to
useProfileMetadata
---
src/app/newsfeed/following/page.tsx | 10 +-
src/app/page.tsx | 66 ++++++++-----
src/components/chats/chatListItem.tsx | 4 +-
src/components/chats/messageUser.tsx | 4 +-
src/components/eventCollector.tsx | 94 +++++++++++-------
src/components/networkStatusIndicator.tsx | 4 +-
src/components/note/base.tsx | 4 +-
src/components/note/quote.tsx | 2 +-
src/components/note/quoteRepost.tsx | 26 +++++
src/components/note/rootNote.tsx | 99 +++++++++++++++++++
src/components/user/base.tsx | 4 +-
src/components/user/extend.tsx | 4 +-
src/components/user/follow.tsx | 4 +-
src/components/user/large.tsx | 4 +-
src/components/user/mention.tsx | 4 +-
src/components/user/mini.tsx | 4 +-
src/components/user/quoteRepost.tsx | 35 +++++++
.../useNetworkStatus.tsx} | 2 +-
.../useProfileMetadata.tsx} | 6 +-
19 files changed, 297 insertions(+), 83 deletions(-)
create mode 100644 src/components/note/quoteRepost.tsx
create mode 100644 src/components/note/rootNote.tsx
create mode 100644 src/components/user/quoteRepost.tsx
rename src/utils/{network.tsx => hooks/useNetworkStatus.tsx} (93%)
rename src/utils/{metadata.tsx => hooks/useProfileMetadata.tsx} (92%)
diff --git a/src/app/newsfeed/following/page.tsx b/src/app/newsfeed/following/page.tsx
index c2c1c484..60a071d4 100644
--- a/src/app/newsfeed/following/page.tsx
+++ b/src/app/newsfeed/following/page.tsx
@@ -3,6 +3,7 @@
import FormBase from '@components/form/base';
import { NoteBase } from '@components/note/base';
import { Placeholder } from '@components/note/placeholder';
+import { NoteQuoteRepost } from '@components/note/quoteRepost';
import { filteredNotesAtom, hasNewerNoteAtom, notesAtom } from '@stores/note';
@@ -25,7 +26,14 @@ export default function Page() {
const itemContent: any = useCallback(
(index: string | number) => {
- return ;
+ switch (data[index].kind) {
+ case 1:
+ return ;
+ case 6:
+ return ;
+ default:
+ break;
+ }
},
[data]
);
diff --git a/src/app/page.tsx b/src/app/page.tsx
index e8b4c290..b9a4a02c 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -66,7 +66,7 @@ export default function Page() {
since = dateToUnix(new Date(lastLogin));
}
query.push({
- kinds: [1],
+ kinds: [1, 6],
authors: follows,
since: since,
until: dateToUnix(now.current),
@@ -93,32 +93,52 @@ export default function Page() {
query,
relays,
(event) => {
- if (event.kind === 1) {
- const parentID = getParentID(event.tags, event.id);
- // insert event to local database
- createNote({
- event_id: event.id,
- pubkey: event.pubkey,
- kind: event.kind,
- tags: JSON.stringify(event.tags),
- content: event.content,
- parent_id: parentID,
- parent_comment_id: '',
- created_at: event.created_at,
- account_id: account.id,
- }).catch(console.error);
- } else if (event.kind === 4) {
- if (event.pubkey !== account.pubkey) {
- createChat({
+ switch (event.kind) {
+ // short text note
+ case 1:
+ const parentID = getParentID(event.tags, event.id);
+ // insert event to local database
+ createNote({
+ event_id: event.id,
pubkey: event.pubkey,
+ kind: event.kind,
+ tags: JSON.stringify(event.tags),
+ content: event.content,
+ parent_id: parentID,
+ parent_comment_id: '',
created_at: event.created_at,
account_id: account.id,
}).catch(console.error);
- }
- } else if (event.kind === 40) {
- createChannel({ event_id: event.id, content: event.content, account_id: account.id }).catch(console.error);
- } else {
- console.error;
+ break;
+ // chat
+ case 4:
+ if (event.pubkey !== account.pubkey) {
+ createChat({
+ pubkey: event.pubkey,
+ created_at: event.created_at,
+ account_id: account.id,
+ }).catch(console.error);
+ }
+ // repost
+ case 6:
+ createNote({
+ event_id: event.id,
+ pubkey: event.pubkey,
+ kind: event.kind,
+ tags: JSON.stringify(event.tags),
+ content: event.content,
+ parent_id: '',
+ parent_comment_id: '',
+ created_at: event.created_at,
+ account_id: account.id,
+ }).catch(console.error);
+ // channel
+ case 40:
+ createChannel({ event_id: event.id, content: event.content, account_id: account.id }).catch(
+ console.error
+ );
+ default:
+ break;
}
},
undefined,
diff --git a/src/components/chats/chatListItem.tsx b/src/components/chats/chatListItem.tsx
index 29ff0365..b192173b 100644
--- a/src/components/chats/chatListItem.tsx
+++ b/src/components/chats/chatListItem.tsx
@@ -2,14 +2,14 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
import { useRouter } from 'next/navigation';
export const ChatListItem = ({ pubkey }: { pubkey: string }) => {
const router = useRouter();
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
const openChat = () => {
router.push(`/chats/${pubkey}`);
diff --git a/src/components/chats/messageUser.tsx b/src/components/chats/messageUser.tsx
index 8ff71336..19a303be 100644
--- a/src/components/chats/messageUser.tsx
+++ b/src/components/chats/messageUser.tsx
@@ -2,7 +2,7 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
import dayjs from 'dayjs';
@@ -11,7 +11,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
export const MessageUser = ({ pubkey, time }: { pubkey: string; time: number }) => {
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
return (
diff --git a/src/components/eventCollector.tsx b/src/components/eventCollector.tsx
index 918ff1f0..0e3eb026 100644
--- a/src/components/eventCollector.tsx
+++ b/src/components/eventCollector.tsx
@@ -6,7 +6,7 @@ import { RelayContext } from '@components/relaysProvider';
import { hasNewerNoteAtom } from '@stores/note';
import { dateToUnix } from '@utils/getDate';
-import { fetchMetadata } from '@utils/metadata';
+import { fetchProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { getParentID, pubkeyArray } from '@utils/transform';
import useLocalStorage, { writeStorage } from '@rehooks/local-storage';
@@ -31,7 +31,7 @@ export default function EventCollector() {
const { createPleb } = await import('@utils/bindings');
for (const tag of tags) {
const pubkey = tag[1];
- fetchMetadata(pubkey)
+ fetchProfileMetadata(pubkey)
.then((res: { content: string }) => {
createPleb({
pleb_id: pubkey + '-lume' + activeAccount.id.toString(),
@@ -55,7 +55,7 @@ export default function EventCollector() {
unsubscribe.current = pool.subscribe(
[
{
- kinds: [1],
+ kinds: [1, 6],
authors: pubkeyArray(follows),
since: dateToUnix(now.current),
},
@@ -75,39 +75,65 @@ export default function EventCollector() {
],
relays,
(event) => {
- if (event.kind === 1) {
- const parentID = getParentID(event.tags, event.id);
- // insert event to local database
- createNote({
- event_id: event.id,
- pubkey: event.pubkey,
- kind: event.kind,
- tags: JSON.stringify(event.tags),
- content: event.content,
- parent_id: parentID,
- parent_comment_id: '',
- created_at: event.created_at,
- account_id: activeAccount.id,
- })
- .then(() =>
- // notify user reload to get newer note
- setHasNewerNote(true)
- )
- .catch(console.error);
- } else if (event.kind === 3) {
- createFollowingPlebs(event.tags);
- } else if (event.kind === 4) {
- if (event.pubkey !== activeAccount.pubkey) {
- createChat({ pubkey: event.pubkey, created_at: event.created_at, account_id: activeAccount.id }).catch(
+ switch (event.kind) {
+ // short text note
+ case 1:
+ const parentID = getParentID(event.tags, event.id);
+ createNote({
+ event_id: event.id,
+ pubkey: event.pubkey,
+ kind: event.kind,
+ tags: JSON.stringify(event.tags),
+ content: event.content,
+ parent_id: parentID,
+ parent_comment_id: '',
+ created_at: event.created_at,
+ account_id: activeAccount.id,
+ })
+ .then(() =>
+ // notify user reload to get newer note
+ setHasNewerNote(true)
+ )
+ .catch(console.error);
+ break;
+ // contacts
+ case 3:
+ createFollowingPlebs(event.tags);
+ break;
+ // chat
+ case 4:
+ if (event.pubkey !== activeAccount.pubkey) {
+ createChat({
+ pubkey: event.pubkey,
+ created_at: event.created_at,
+ account_id: activeAccount.id,
+ }).catch(console.error);
+ }
+ // repost
+ case 6:
+ createNote({
+ event_id: event.id,
+ pubkey: event.pubkey,
+ kind: event.kind,
+ tags: JSON.stringify(event.tags),
+ content: event.content,
+ parent_id: '',
+ parent_comment_id: '',
+ created_at: event.created_at,
+ account_id: activeAccount.id,
+ })
+ .then(() =>
+ // notify user reload to get newer note
+ setHasNewerNote(true)
+ )
+ .catch(console.error);
+ // channel
+ case 40:
+ createChannel({ event_id: event.id, content: event.content, account_id: activeAccount.id }).catch(
console.error
);
- }
- } else if (event.kind === 40) {
- createChannel({ event_id: event.id, content: event.content, account_id: activeAccount.id }).catch(
- console.error
- );
- } else {
- console.error;
+ default:
+ break;
}
}
);
diff --git a/src/components/networkStatusIndicator.tsx b/src/components/networkStatusIndicator.tsx
index e17bdbb3..69ca64ac 100644
--- a/src/components/networkStatusIndicator.tsx
+++ b/src/components/networkStatusIndicator.tsx
@@ -1,7 +1,7 @@
-import { useNavigatorOnLine } from '@utils/network';
+import { useNetworkStatus } from '@utils/hooks/useNetworkStatus';
export const NetworkStatusIndicator = () => {
- const isOnline = useNavigatorOnLine();
+ const isOnline = useNetworkStatus();
return (
diff --git a/src/components/note/base.tsx b/src/components/note/base.tsx
index a3ec85f1..bcfeee32 100644
--- a/src/components/note/base.tsx
+++ b/src/components/note/base.tsx
@@ -45,7 +45,7 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) {
));
// handle mentions
if (tags.length > 0) {
- parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => {
+ parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match) => {
if (tags[match][0] === 'p') {
// @-mentions
return
;
@@ -90,7 +90,7 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) {
onClick={(e) => openThread(e)}
className="relative z-10 m-0 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 px-3 py-5 hover:bg-black/20"
>
- <>{parentNote}>
+ {parentNote}
openUserPage(e)}>
diff --git a/src/components/note/quote.tsx b/src/components/note/quote.tsx
index 77059077..b1a0761f 100644
--- a/src/components/note/quote.tsx
+++ b/src/components/note/quote.tsx
@@ -9,7 +9,7 @@ import destr from 'destr';
import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import reactStringReplace from 'react-string-replace';
-export const NoteQuote = memo(function NoteRepost({ id }: { id: string }) {
+export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
const [pool, relays]: any = useContext(RelayContext);
const [activeAccount]: any = useLocalStorage('activeAccount', {});
diff --git a/src/components/note/quoteRepost.tsx b/src/components/note/quoteRepost.tsx
new file mode 100644
index 00000000..a30ea812
--- /dev/null
+++ b/src/components/note/quoteRepost.tsx
@@ -0,0 +1,26 @@
+import { RootNote } from '@components/note/rootNote';
+import { UserQuoteRepost } from '@components/user/quoteRepost';
+
+import { memo } from 'react';
+
+export const NoteQuoteRepost = memo(function NoteQuoteRepost({ event }: { event: any }) {
+ const rootNote = () => {
+ let note = null;
+
+ if (event.content.length > 0) {
+ note =
;
+ }
+
+ return note;
+ };
+
+ return (
+
+ );
+});
diff --git a/src/components/note/rootNote.tsx b/src/components/note/rootNote.tsx
new file mode 100644
index 00000000..e3153ea2
--- /dev/null
+++ b/src/components/note/rootNote.tsx
@@ -0,0 +1,99 @@
+import NoteMetadata from '@components/note/metadata';
+import { ImagePreview } from '@components/note/preview/image';
+import { VideoPreview } from '@components/note/preview/video';
+import { NoteQuote } from '@components/note/quote';
+import { UserExtend } from '@components/user/extend';
+import { UserMention } from '@components/user/mention';
+
+import destr from 'destr';
+import { useRouter } from 'next/navigation';
+import { memo, useMemo } from 'react';
+import reactStringReplace from 'react-string-replace';
+
+export const RootNote = memo(function RootNote({ event }: { event: any }) {
+ const router = useRouter();
+
+ const content = useMemo(() => {
+ let parsedContent = event.content;
+ // get data tags
+ const tags = destr(event.tags);
+ // handle urls
+ parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => {
+ if (match.match(/\.(jpg|jpeg|gif|png|webp)$/i)) {
+ // image url
+ return
;
+ } else if (match.match(/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/i)) {
+ // youtube
+ return
;
+ } else if (match.match(/\.(mp4|webm)$/i)) {
+ // video
+ return
;
+ } else {
+ return (
+
+ {match}
+
+ );
+ }
+ });
+ // handle #-hashtags
+ parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => (
+
+ #{match}
+
+ ));
+ // handle mentions
+ if (tags.length > 0) {
+ parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match) => {
+ if (tags[match][0] === 'p') {
+ // @-mentions
+ return
;
+ } else if (tags[match][0] === 'e') {
+ // note-quotes
+ return
;
+ } else {
+ return;
+ }
+ });
+ }
+
+ return parsedContent;
+ }, [event.content, event.tags]);
+
+ const openUserPage = (e) => {
+ e.stopPropagation();
+ router.push(`/users/${event.pubkey}`);
+ };
+
+ const openThread = (e) => {
+ const selection = window.getSelection();
+ if (selection.toString().length === 0) {
+ router.push(`/newsfeed/${event.parent_id}`);
+ } else {
+ e.stopPropagation();
+ }
+ };
+
+ return (
+
openThread(e)} className="relative z-10 flex flex-col">
+
openUserPage(e)}>
+
+
+
+
e.stopPropagation()} className="mt-5 pl-[52px]">
+
+
+
+ );
+});
diff --git a/src/components/user/base.tsx b/src/components/user/base.tsx
index 7551b520..6d11be56 100644
--- a/src/components/user/base.tsx
+++ b/src/components/user/base.tsx
@@ -2,13 +2,13 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
import { memo } from 'react';
export const UserBase = memo(function UserBase({ pubkey }: { pubkey: string }) {
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
return (
diff --git a/src/components/user/extend.tsx b/src/components/user/extend.tsx
index b0dc7f86..9db2b0f3 100644
--- a/src/components/user/extend.tsx
+++ b/src/components/user/extend.tsx
@@ -2,7 +2,7 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
import dayjs from 'dayjs';
@@ -12,7 +12,7 @@ import { MoreHoriz } from 'iconoir-react';
dayjs.extend(relativeTime);
export const UserExtend = ({ pubkey, time }: { pubkey: string; time: number }) => {
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
return (
diff --git a/src/components/user/follow.tsx b/src/components/user/follow.tsx
index fe7b0306..051428c0 100644
--- a/src/components/user/follow.tsx
+++ b/src/components/user/follow.tsx
@@ -2,11 +2,11 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
export const UserFollow = ({ pubkey }: { pubkey: string }) => {
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
return (
diff --git a/src/components/user/large.tsx b/src/components/user/large.tsx
index 3957cfa4..f3779860 100644
--- a/src/components/user/large.tsx
+++ b/src/components/user/large.tsx
@@ -2,7 +2,7 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
import dayjs from 'dayjs';
@@ -12,7 +12,7 @@ import { MoreHoriz } from 'iconoir-react';
dayjs.extend(relativeTime);
export const UserLarge = ({ pubkey, time }: { pubkey: string; time: number }) => {
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
return (
diff --git a/src/components/user/mention.tsx b/src/components/user/mention.tsx
index ed136390..82340491 100644
--- a/src/components/user/mention.tsx
+++ b/src/components/user/mention.tsx
@@ -1,8 +1,8 @@
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
export const UserMention = ({ pubkey }: { pubkey: string }) => {
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
return (
@{profile?.name || profile?.username || truncate(pubkey, 16, ' .... ')}
diff --git a/src/components/user/mini.tsx b/src/components/user/mini.tsx
index 198ee205..8546736f 100644
--- a/src/components/user/mini.tsx
+++ b/src/components/user/mini.tsx
@@ -2,11 +2,11 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useMetadata } from '@utils/metadata';
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
import { truncate } from '@utils/truncate';
export const UserMini = ({ pubkey }: { pubkey: string }) => {
- const profile = useMetadata(pubkey);
+ const profile = useProfileMetadata(pubkey);
return (
diff --git a/src/components/user/quoteRepost.tsx b/src/components/user/quoteRepost.tsx
new file mode 100644
index 00000000..b96bf965
--- /dev/null
+++ b/src/components/user/quoteRepost.tsx
@@ -0,0 +1,35 @@
+import { ImageWithFallback } from '@components/imageWithFallback';
+
+import { DEFAULT_AVATAR } from '@stores/constants';
+
+import { useProfileMetadata } from '@utils/hooks/useProfileMetadata';
+import { truncate } from '@utils/truncate';
+
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+
+dayjs.extend(relativeTime);
+
+export const UserQuoteRepost = ({ pubkey, time }: { pubkey: string; time: number }) => {
+ const profile = useProfileMetadata(pubkey);
+
+ return (
+
+
+
+
+
+
+ {profile?.display_name || profile?.name || truncate(pubkey, 16, ' .... ')} reposted
+
+ ยท
+ {dayjs().to(dayjs.unix(time))}
+
+
+ );
+};
diff --git a/src/utils/network.tsx b/src/utils/hooks/useNetworkStatus.tsx
similarity index 93%
rename from src/utils/network.tsx
rename to src/utils/hooks/useNetworkStatus.tsx
index 41db85a8..f61f0edc 100644
--- a/src/utils/network.tsx
+++ b/src/utils/hooks/useNetworkStatus.tsx
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
const getOnLineStatus = () =>
typeof navigator !== 'undefined' && typeof navigator.onLine === 'boolean' ? navigator.onLine : true;
-export const useNavigatorOnLine = () => {
+export const useNetworkStatus = () => {
const [status, setStatus] = useState(getOnLineStatus());
const setOnline = () => setStatus(true);
diff --git a/src/utils/metadata.tsx b/src/utils/hooks/useProfileMetadata.tsx
similarity index 92%
rename from src/utils/metadata.tsx
rename to src/utils/hooks/useProfileMetadata.tsx
index 8f86436e..53b28c8d 100644
--- a/src/utils/metadata.tsx
+++ b/src/utils/hooks/useProfileMetadata.tsx
@@ -2,7 +2,7 @@ import useLocalStorage from '@rehooks/local-storage';
import { fetch } from '@tauri-apps/api/http';
import { useCallback, useEffect, useMemo, useState } from 'react';
-export const fetchMetadata = async (pubkey: string) => {
+export const fetchProfileMetadata = async (pubkey: string) => {
const result = await fetch(`https://rbr.bio/${pubkey}/metadata.json`, {
method: 'GET',
timeout: 5,
@@ -10,7 +10,7 @@ export const fetchMetadata = async (pubkey: string) => {
return await result.data;
};
-export const useMetadata = (pubkey) => {
+export const useProfileMetadata = (pubkey) => {
const [activeAccount]: any = useLocalStorage('activeAccount', {});
const [plebs] = useLocalStorage('activeAccountFollows', []);
const [profile, setProfile] = useState(null);
@@ -47,7 +47,7 @@ export const useMetadata = (pubkey) => {
useEffect(() => {
if (!cacheProfile) {
- fetchMetadata(pubkey)
+ fetchProfileMetadata(pubkey)
.then((res: any) => {
// update state
setProfile(JSON.parse(res.content));