From a10e9aafd125b96c427ef4a30ea748707d3b2a0b Mon Sep 17 00:00:00 2001 From: daniyal Date: Mon, 23 Sep 2024 20:57:29 +0500 Subject: [PATCH 1/2] chore(refactor): implement a custom hook for comments --- src/hooks/useComments.ts | 46 +++++++++++++++++ src/pages/mod/internal/comment/index.tsx | 65 ++++++------------------ src/types/mod.ts | 12 +++++ 3 files changed, 74 insertions(+), 49 deletions(-) create mode 100644 src/hooks/useComments.ts diff --git a/src/hooks/useComments.ts b/src/hooks/useComments.ts new file mode 100644 index 0000000..808e67d --- /dev/null +++ b/src/hooks/useComments.ts @@ -0,0 +1,46 @@ +import { + MetadataController, + RelayController, + UserRelaysType +} from 'controllers' +import { Filter, kinds } from 'nostr-tools' +import { useState } from 'react' +import { CommentEvent, ModDetails } from 'types' +import { useDidMount } from './useDidMount' + +export const useComments = (mod: ModDetails) => { + const [commentEvents, setCommentEvents] = useState([]) + + useDidMount(async () => { + const metadataController = await MetadataController.getInstance() + + const authorReadRelays = await metadataController.findUserRelays( + mod.author, + UserRelaysType.Read + ) + + const filter: Filter = { + kinds: [kinds.ShortTextNote], + '#a': [mod.aTag] + } + + RelayController.getInstance().subscribeForEvents( + filter, + authorReadRelays, + (event) => { + setCommentEvents((prev) => { + if (prev.find((e) => e.id === event.id)) { + return [...prev] + } + + return [event, ...prev] + }) + } + ) + }) + + return { + commentEvents, + setCommentEvents + } +} diff --git a/src/pages/mod/internal/comment/index.tsx b/src/pages/mod/internal/comment/index.tsx index f42a94b..62c5099 100644 --- a/src/pages/mod/internal/comment/index.tsx +++ b/src/pages/mod/internal/comment/index.tsx @@ -6,19 +6,24 @@ import { } from 'controllers' import { formatDate } from 'date-fns' import { useAppSelector, useDidMount, useReactions } from 'hooks' -import { - Event, - kinds, - nip19, - Filter as NostrEventFilter, - UnsignedEvent -} from 'nostr-tools' -import React, { useEffect, useMemo } from 'react' -import { Dispatch, SetStateAction, useState } from 'react' +import { useComments } from 'hooks/useComments' +import { Event, kinds, nip19, UnsignedEvent } from 'nostr-tools' +import React, { + Dispatch, + SetStateAction, + useEffect, + useMemo, + useState +} from 'react' import { Link } from 'react-router-dom' import { toast } from 'react-toastify' import { getProfilePageRoute } from 'routes' -import { ModDetails, UserProfile } from 'types' +import { + CommentEvent, + CommentEventStatus, + ModDetails, + UserProfile +} from 'types/index.ts' import { abbreviateNumber, hexToNpub, log, LogType, now } from 'utils' enum SortByEnum { @@ -36,23 +41,13 @@ type FilterOptions = { author: AuthorFilterEnum } -enum CommentEventStatus { - Publishing = 'Publishing comment...', - Published = 'Published!', - Failed = 'Failed to publish comment.' -} - -interface CommentEvent extends Event { - status?: CommentEventStatus -} - type Props = { modDetails: ModDetails setCommentCount: Dispatch> } export const Comments = ({ modDetails, setCommentCount }: Props) => { - const [commentEvents, setCommentEvents] = useState([]) + const { commentEvents, setCommentEvents } = useComments(modDetails) const [filterOptions, setFilterOptions] = useState({ sort: SortByEnum.Latest, author: AuthorFilterEnum.All_Comments @@ -64,34 +59,6 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => { const userState = useAppSelector((state) => state.user) - useDidMount(async () => { - const metadataController = await MetadataController.getInstance() - - const authorReadRelays = await metadataController.findUserRelays( - modDetails.author, - UserRelaysType.Read - ) - - const filter: NostrEventFilter = { - kinds: [kinds.ShortTextNote], - '#a': [modDetails.aTag] - } - - RelayController.getInstance().subscribeForEvents( - filter, - authorReadRelays, - (event) => { - setCommentEvents((prev) => { - if (prev.find((e) => e.id === event.id)) { - return [...prev] - } - - return [event, ...prev] - }) - } - ) - }) - const handleSubmit = async (content: string): Promise => { if (content === '') return false diff --git a/src/types/mod.ts b/src/types/mod.ts index 710f631..c9d14f8 100644 --- a/src/types/mod.ts +++ b/src/types/mod.ts @@ -1,3 +1,15 @@ +import { Event } from 'nostr-tools' + +export enum CommentEventStatus { + Publishing = 'Publishing comment...', + Published = 'Published!', + Failed = 'Failed to publish comment.' +} + +export interface CommentEvent extends Event { + status?: CommentEventStatus +} + export type Game = { 'Game Name': string '16 by 9 image': string From b21c79b992a46428ba3dcb3db341197c81e1b820 Mon Sep 17 00:00:00 2001 From: daniyal Date: Mon, 23 Sep 2024 20:58:50 +0500 Subject: [PATCH 2/2] feat: display reactions, comments, and zap data in mod cards --- src/components/ModCard.tsx | 187 +++++++++++++++++++++---------------- src/pages/game.tsx | 26 +----- src/pages/home.tsx | 35 +------ src/pages/mods.tsx | 26 +----- src/pages/search.tsx | 26 +----- 5 files changed, 124 insertions(+), 176 deletions(-) diff --git a/src/components/ModCard.tsx b/src/components/ModCard.tsx index ca653fb..49fb142 100644 --- a/src/components/ModCard.tsx +++ b/src/components/ModCard.tsx @@ -1,89 +1,118 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import { Link } from 'react-router-dom' import '../styles/cardMod.css' import { handleModImageError } from '../utils' +import { ModDetails } from 'types' +import { getModPageRoute } from 'routes' +import { kinds, nip19 } from 'nostr-tools' +import { useDidMount, useReactions } from 'hooks' +import { RelayController } from 'controllers' +import { toast } from 'react-toastify' +import { useComments } from 'hooks/useComments' -type ModCardProps = { - title: string - gameName: string - summary: string - imageUrl: string - route: string -} +export const ModCard = React.memo((props: ModDetails) => { + const [totalZappedAmount, setTotalZappedAmount] = useState(0) + const [commentCount, setCommentCount] = useState(0) + const { commentEvents } = useComments(props) + const { likesCount, disLikesCount } = useReactions({ + pubkey: props.author, + eTag: props.id, + aTag: props.aTag + }) -export const ModCard = React.memo( - ({ title, gameName, summary, imageUrl, route }: ModCardProps) => { - return ( - -
-
- + useDidMount(() => { + RelayController.getInstance() + .getTotalZapAmount(props.author, props.id, props.aTag) + .then((res) => { + setTotalZappedAmount(res.accumulatedZapAmount) + }) + .catch((err) => { + toast.error(err.message || err) + }) + }) + + useEffect(() => { + setCommentCount(commentEvents.length) + }, [commentEvents]) + + const route = getModPageRoute( + nip19.naddrEncode({ + identifier: props.aTag, + pubkey: props.author, + kind: kinds.ClassifiedListing + }) + ) + + return ( + +
+
+ +
+
+

{props.title}

+

{props.summary}

+
+

{props.game}

-
-

{title}

-

{summary}

-
-

{gameName}

+
+
+
+
+ + + +

{likesCount}

-
-
-
-
- - - -

420

-
-
- - - -

420

-
-
- - - -

420

-
-
- - - -

420

-
+
+ + + +

{disLikesCount}

+
+
+ + + +

{commentCount}

+
+
+ + + +

{totalZappedAmount}

- - ) - } -) +
+ + ) +}) diff --git a/src/pages/game.tsx b/src/pages/game.tsx index 3ab9d14..bbeaef5 100644 --- a/src/pages/game.tsx +++ b/src/pages/game.tsx @@ -4,7 +4,7 @@ import { PaginationWithPageNumbers } from 'components/Pagination' import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts' import { RelayController } from 'controllers' import { useAppSelector, useMuteLists } from 'hooks' -import { Filter, kinds, nip19 } from 'nostr-tools' +import { Filter, kinds } from 'nostr-tools' import { Subscription } from 'nostr-tools/abstract-relay' import React, { Dispatch, @@ -16,7 +16,6 @@ import React, { } from 'react' import { useParams } from 'react-router-dom' import { toast } from 'react-toastify' -import { getModPageRoute } from 'routes' import { ModDetails } from 'types' import { extractModData, isModDataComplete, log, LogType } from 'utils' @@ -179,26 +178,9 @@ export const GamePage = () => { />
- {currentMods.map((mod) => { - const route = getModPageRoute( - nip19.naddrEncode({ - identifier: mod.aTag, - pubkey: mod.author, - kind: kinds.ClassifiedListing - }) - ) - - return ( - - ) - })} + {currentMods.map((mod) => ( + + ))}
{ if (!mod) return - const route = getModPageRoute(naddr) - - return ( - - ) + return } const DisplayLatestMods = () => { @@ -291,24 +281,7 @@ const DisplayLatestMods = () => { ) : ( latestMods.map((mod) => { - const route = getModPageRoute( - nip19.naddrEncode({ - identifier: mod.aTag, - pubkey: mod.author, - kind: kinds.ClassifiedListing - }) - ) - - return ( - - ) + return }) )}
diff --git a/src/pages/mods.tsx b/src/pages/mods.tsx index 8503802..3b24b56 100644 --- a/src/pages/mods.tsx +++ b/src/pages/mods.tsx @@ -1,5 +1,4 @@ import { Pagination } from 'components/Pagination' -import { kinds, nip19 } from 'nostr-tools' import React, { Dispatch, SetStateAction, @@ -15,7 +14,7 @@ import { ModCard } from '../components/ModCard' import { MOD_FILTER_LIMIT } from '../constants' import { MetadataController } from '../controllers' import { useAppSelector, useDidMount, useMuteLists } from '../hooks' -import { appRoutes, getModPageRoute } from '../routes' +import { appRoutes } from '../routes' import '../styles/filters.css' import '../styles/pagination.css' import '../styles/search.css' @@ -192,26 +191,9 @@ export const ModsPage = () => {
- {filteredModList.map((mod) => { - const route = getModPageRoute( - nip19.naddrEncode({ - identifier: mod.aTag, - pubkey: mod.author, - kind: kinds.ClassifiedListing - }) - ) - - return ( - - ) - })} + {filteredModList.map((mod) => ( + + ))}
diff --git a/src/pages/search.tsx b/src/pages/search.tsx index 5bace93..f507dd9 100644 --- a/src/pages/search.tsx +++ b/src/pages/search.tsx @@ -12,7 +12,7 @@ import { } from 'constants.ts' import { RelayController } from 'controllers' import { useAppSelector, useGames, useMuteLists } from 'hooks' -import { Filter, kinds, nip19 } from 'nostr-tools' +import { Filter, kinds } from 'nostr-tools' import { Subscription } from 'nostr-tools/abstract-relay' import React, { Dispatch, @@ -24,7 +24,6 @@ import React, { } from 'react' import { useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' -import { getModPageRoute } from 'routes' import { ModDetails, MuteLists } from 'types' import { extractModData, isModDataComplete, log, LogType } from 'utils' @@ -462,26 +461,9 @@ const ModsResult = ({
{filteredModList .slice((page - 1) * MAX_MODS_PER_PAGE, page * MAX_MODS_PER_PAGE) - .map((mod) => { - const route = getModPageRoute( - nip19.naddrEncode({ - identifier: mod.aTag, - pubkey: mod.author, - kind: kinds.ClassifiedListing - }) - ) - - return ( - - ) - })} + .map((mod) => ( + + ))}