Skip to content

Commit

Permalink
Add comment posting, fetching and rendering functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
pulkitvyas08 committed Sep 13, 2022
1 parent 2e68c8e commit 8a5e6f3
Show file tree
Hide file tree
Showing 10 changed files with 418 additions and 58 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ yarn-error.log*

# typescript
*.tsbuildinfo

.env
249 changes: 249 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/atoms/fetchedCommentsAtom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { atom } from "jotai";

export const fetchedComments = atom<NostrEvent[] | []>([]);
3 changes: 2 additions & 1 deletion src/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import StarterKit from "@tiptap/starter-kit";
import React, { useEffect, useState } from "react";

import { MenuBar } from "./Menubar";
import { publishPost } from "../../service/nostrOps";
import { useAtomValue } from "jotai";
import { relayPoolAtom } from "../../atoms/relayPoolAtom";
import { useRouter } from "next/router";
import { useNostrOps } from "../../service/nostrOps";

const Editor = () => {
const { publishPost } = useNostrOps();
const pool = useAtomValue(relayPoolAtom);
const [title, setTitle] = useState("");
const [loading, setLoading] = useState(false);
Expand Down
33 changes: 33 additions & 0 deletions src/components/Post/CommentItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Avatar, Divider, Flex, Text } from "@chakra-ui/react";
import { useRouter } from "next/router";
import React from "react";

type CommentItemProps = {
comment: NostrEvent;
};

const CommentItem: React.FC<CommentItemProps> = ({ comment }) => {
const router = useRouter();

return (
<Flex direction="column">
<Flex columnGap="5px" alignItems="center">
<Avatar size="sm" />
<Text
color="gray.500"
cursor="pointer"
maxW={{ base: "250px", md: "500px" }}
onClick={() => router.push(`/user/${comment.pubkey}`)}
>
{comment.pubkey}
</Text>
</Flex>
<Flex mt="5px" ml="38px">
{JSON.parse(comment.content)}
</Flex>
<Divider mt="10px" />
</Flex>
);
};

export default CommentItem;
4 changes: 2 additions & 2 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ const Home: NextPage = () => {
mt="25px"
rowGap="25px"
>
{getUniquePosts(posts).map((post) => {
{getUniquePosts(posts, true).map((post) => {
const postContent: Post = JSON.parse(post.content);
const postBody = postContent.content.replace(/<[^>]+>/g, "");
const postBody = postContent?.content?.replace(/<[^>]+>/g, "");
return (
<PostItem
key={String(post.id)}
Expand Down
68 changes: 63 additions & 5 deletions src/pages/post/[postId].tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {
Avatar,
Button,
Divider,
Flex,
Heading,
Input,
Spinner,
Text,
Textarea,
Tooltip,
useToast,
} from "@chakra-ui/react";
import { useAtomValue } from "jotai";
import moment from "moment";
Expand All @@ -16,18 +16,50 @@ import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import { relayPoolAtom } from "../../atoms/relayPoolAtom";
import AutoResizeTextarea from "../../components/Post/AutoGrowTextarea";
import CommentItem from "../../components/Post/CommentItem";
import { useNostrOps } from "../../service/nostrOps";
import { toDateTime } from "../../utils/index";
import { getUniquePosts } from "../../utils/index";

const PostPage: React.FC = () => {
const { getPostById, fetchedPost } = useNostrOps();
const {
getPostById,
fetchedPost,
fetchedComments,
getPostComments,
publishComment,
} = useNostrOps();
const toast = useToast();
const router = useRouter();
const pool = useAtomValue(relayPoolAtom);
const postId = router.query.postId;
const [postContent, setPostContent] = useState<PostStructure | undefined>(
undefined
);
const [commentText, setCommentText] = useState("");
const [commentList, setCommentList] = useState<NostrEvent[]>([]);

const handleCommentPost = async () => {
if (commentText.length === 0) return;

try {
await publishComment(commentText, [["e", postId]], pool);
toast({
title: "Comment Posted!",
duration: 2000,
isClosable: true,
status: "success",
});
} catch (error) {
console.log("handleCommentPost: ", error);
toast({
title: "Error posting comment",
duration: 2000,
isClosable: true,
status: "error",
});
}
};

useEffect(() => {
const fetchPost = async () => {
Expand All @@ -45,6 +77,19 @@ const PostPage: React.FC = () => {
});
}, [pool, postId, fetchedPost]);

useEffect(() => {
setCommentList(fetchedComments);
}, [fetchedComments]);

useEffect(() => {
const fetchComments = async () => {
if (postId) {
await getPostComments(pool, String(postId));
}
};
fetchComments();
}, [postId]);

return (
<>
<Head>
Expand Down Expand Up @@ -85,15 +130,28 @@ const PostPage: React.FC = () => {
/>
<Flex mt="20px" justifyContent="flex-start">
<Text fontSize="25px" fontWeight="bold">
Comments
Comments:
</Text>
</Flex>
<Flex>
<Flex flexDirection="column">
<AutoResizeTextarea
flexGrow={1}
placeholder="Leave a comment"
onChange={(event) => setCommentText(event.target.value)}
onKeyDown={(event) => {
if (event.key === "Enter") handleCommentPost();
}}
/>
<Button mt="15px" width="150px" onClick={handleCommentPost}>
Post Comment
</Button>
{commentList && (
<Flex direction="column" rowGap="20px" mt="30px">
{getUniquePosts(commentList, false).map((comment) => (
<CommentItem comment={comment} key={comment.id} />
))}
</Flex>
)}
</Flex>
</Flex>
</Flex>
Expand Down
4 changes: 2 additions & 2 deletions src/pages/user/[userId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ const MyPosts: React.FC = () => {
mt="25px"
rowGap="25px"
>
{getUniquePosts(postList).map((post) => {
{getUniquePosts(postList, true).map((post) => {
const postContent: Post = JSON.parse(post.content);
const postBody = postContent.content.replace(/<[^>]+>/g, "");
const postBody = postContent?.content?.replace(/<[^>]+>/g, "");
return (
<PostItem
key={String(post.id)}
Expand Down
88 changes: 44 additions & 44 deletions src/service/nostrOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,6 @@ import { useState } from "react";
import { getPublicKey } from "../utils";
import { reconnect } from "./nostrSetup";

export const publishPost = async (postContent: Post, pool: any) => {
if (!pool) pool = await reconnect(pool);
const publicKey = getPublicKey();
const event = {
pubkey: publicKey,
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: [],
content: JSON.stringify(postContent),
};
await pool.publish(event);
};

// Returns logged in user's posts if no userId is provided otherwise returns posts of given userId
export const getUserPosts = async (pool: any, userId?: string) => {
if (!pool) pool = reconnect(pool);
Expand Down Expand Up @@ -52,33 +39,7 @@ export const useNostrOps = () => {
const [fetchedPost, setFetchedPost] = useState<NostrEvent | undefined>(
undefined
);
const [fetchedUserPosts, setFetchedUserPosts] = useState<NostrEvent[]>();

const getUserPostsById = async (pool: any, userId: string) => {
if (!pool) pool = reconnect(pool);
const fetchedEvents: NostrEvent[] = [];
await pool.sub(
{
cb: async (event: NostrEvent) => {
switch (event.kind) {
case 0:
case 1:
case 2:
// fetchedEvents.push(event);
if (fetchedUserPosts) setFetchedUserPosts((prev) => [event]);
return;
}
},
filter: [
{
authors: [userId],
kinds: [0, 1, 3],
},
],
},
"profile-browser"
);
};
const [fetchedComments, setFetchedComments] = useState<NostrEvent[]>([]);

const getPostById = async (pool: any, postId: string) => {
if (!pool) {
Expand Down Expand Up @@ -110,17 +71,56 @@ export const useNostrOps = () => {

const getPostComments = async (pool: any, postId: string) => {
await pool.sub({
cb: async (event: NostrEvent) => {
if (!fetchedComments.includes(event)) {
setFetchedComments((prev) => [...prev, event]);
}
},
filter: [
{
"#e": [postId],
kinds: [1],
},
],
cb: async (event: NostrEvent) => {
console.log(event);
},
});
};

return { getPostById, fetchedPost, getUserPostsById, fetchedUserPosts };
const publishPost = async (postContent: Post, pool: any) => {
if (!pool) pool = await reconnect(pool);
const publicKey = getPublicKey();
const event = {
pubkey: publicKey,
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: [],
content: JSON.stringify(postContent),
};
await pool.publish(event);
};

const publishComment = async (
commentText: string,
tags: any = [],
pool: any
) => {
if (!pool) pool = await reconnect(pool);
const publicKey = getPublicKey();
const event = {
pubkey: publicKey,
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: tags,
content: JSON.stringify(commentText),
};
await pool.publish(event);
};

return {
getPostById,
fetchedPost,
fetchedComments,
publishPost,
getPostComments,
publishComment,
};
};
22 changes: 18 additions & 4 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,23 @@ export const toDateTime = (secs: number) => {
return t;
};

export const getUniquePosts = (posts: NostrEvent[]) => {
const uniquePosts = posts.filter(
(value, index, self) => index === self.findIndex((t) => t.id === value.id)
);
export const getUniquePosts = (posts: NostrEvent[], post?: boolean) => {
let uniquePosts: NostrEvent[] = [];
if (!post) {
uniquePosts = posts.filter(
(value, index, self) => index === self.findIndex((t) => t.id === value.id)
);
} else {
uniquePosts = posts.filter(
(value, index, self) =>
index ===
self.findIndex(
(t) => t.id === value.id && post && value.tags.length === 0
)
);
}
// if (post) {
// uniquePosts = posts.filter((post) => post.tags.length === 0);
// }
return uniquePosts;
};

0 comments on commit 8a5e6f3

Please sign in to comment.