diff --git a/api/functions/graphql/nexus-typegen.ts b/api/functions/graphql/nexus-typegen.ts index 5316599..1e87d90 100644 --- a/api/functions/graphql/nexus-typegen.ts +++ b/api/functions/graphql/nexus-typegen.ts @@ -374,6 +374,7 @@ export interface NexusGenFieldTypes { Story: { // field return type author: NexusGenRootTypes['Author']; // Author! body: string; // String! + comments: NexusGenRootTypes['PostComment'][]; // [PostComment!]! cover_image: string | null; // String createdAt: NexusGenScalars['Date']; // Date! excerpt: string; // String! @@ -579,6 +580,7 @@ export interface NexusGenFieldTypeNames { Story: { // field return type name author: 'Author' body: 'String' + comments: 'PostComment' cover_image: 'String' createdAt: 'Date' excerpt: 'String' diff --git a/api/functions/graphql/schema.graphql b/api/functions/graphql/schema.graphql index ca5453a..bfa9713 100644 --- a/api/functions/graphql/schema.graphql +++ b/api/functions/graphql/schema.graphql @@ -186,6 +186,7 @@ type Question implements PostBase { type Story implements PostBase { author: Author! body: String! + comments: [PostComment!]! cover_image: String createdAt: Date! excerpt: String! diff --git a/src/features/Posts/Components/Comments/AddComment/AddComment.tsx b/src/features/Posts/Components/Comments/AddComment/AddComment.tsx index 9243ac4..ffaa4d8 100644 --- a/src/features/Posts/Components/Comments/AddComment/AddComment.tsx +++ b/src/features/Posts/Components/Comments/AddComment/AddComment.tsx @@ -14,7 +14,7 @@ import { PlaceholderExtension, } from 'remirror/extensions'; import { EditorComponent, Remirror, useRemirror } from '@remirror/react'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'; import Toolbar from './Toolbar'; import Button from 'src/Components/Button/Button'; @@ -26,7 +26,7 @@ interface Props { placeholder?: string; avatar: string; autoFocus?: boolean - onSubmit?: (comment: string) => void; + onSubmit?: (comment: string) => Promise; } @@ -41,7 +41,7 @@ export default function AddComment({ initialContent, placeholder, avatar, autoFo }); return extension; }, []); - + const [isLoading, setIsLoading] = useState(false) const valueRef = useRef(""); @@ -85,14 +85,18 @@ export default function AddComment({ initialContent, placeholder, avatar, autoFo }, [autoFocus]) - const submitComment = () => { - onSubmit?.(valueRef.current); - manager.view.updateState(manager.createState({ content: manager.createEmptyDoc() })) + const submitComment = async () => { + setIsLoading(true); + const isSuccess = await onSubmit?.(valueRef.current); + if (isSuccess) + manager.view.updateState(manager.createState({ content: manager.createEmptyDoc() })) + setIsLoading(false); + } return ( -
+
- +
diff --git a/src/features/Posts/Components/Comments/Comment/Comment.tsx b/src/features/Posts/Components/Comments/Comment/Comment.tsx index 552c96d..eae3a1e 100644 --- a/src/features/Posts/Components/Comments/Comment/Comment.tsx +++ b/src/features/Posts/Components/Comments/Comment/Comment.tsx @@ -1,5 +1,8 @@ +import { useToggle } from "@react-hookz/web"; import { useState } from "react"; +import { FaChevronDown, FaChevronUp } from "react-icons/fa"; +import Button from "src/Components/Button/Button"; import { useAppSelector } from "src/utils/hooks"; import AddComment from "../AddComment/AddComment"; import CommentCard from "../CommentCard/CommentCard"; @@ -16,7 +19,8 @@ interface Props { export default function Comment({ comment, canReply, isRoot, onClickedReply, onReply }: Props) { - const [replyOpen, setReplyOpen] = useState(false) + const [replyOpen, setReplyOpen] = useState(false); + const [repliesCollapsed, toggleRepliesCollapsed] = useToggle(true) const user = useAppSelector(s => s.user.me) const clickReply = () => { @@ -26,13 +30,31 @@ export default function Comment({ comment, canReply, isRoot, onClickedReply, onR onClickedReply?.() } + const handleReply = async (text: string) => { + try { + await onReply?.(text); + setReplyOpen(false); + return true; + } catch (error) { + return false; + } + } + return (
{(comment.replies.length > 0 || replyOpen) &&
-
+
- {comment.replies.map(reply => 0 && + } + {!repliesCollapsed && comment.replies.map(reply => onReply?.(text)} + onSubmit={handleReply} />}
} diff --git a/src/features/Posts/Components/Comments/CommentCard/CommentCard.tsx b/src/features/Posts/Components/Comments/CommentCard/CommentCard.tsx index 30d9d3c..30aec11 100644 --- a/src/features/Posts/Components/Comments/CommentCard/CommentCard.tsx +++ b/src/features/Posts/Components/Comments/CommentCard/CommentCard.tsx @@ -32,7 +32,7 @@ export default function CommentCard({ comment, canReply, onReply }: Props) { } return ( -
+
{ - CommentsWorker.post({ content, filter, parentId }); + const handleNewComment = async (content: string, parentId?: string) => { + try { + await CommentsWorker.post({ content, filter, parentId }); + return true; + } catch (error) { + return false + } } return ( -
+
Discussion
{!!user &&
void) { cb(newComments) }, 1000) + let sub = pool.sub({ filter: { "#r": [filter] @@ -43,10 +44,16 @@ export function sub(filter: string, cb: (data: Comment[]) => void) { //Got a new event if (!event.id) return; - if (event.id in events) return - events[event.id] = event + if (event.id in events) return; + events[event.id] = event reconstructTree() + + document.dispatchEvent( + new CustomEvent('nostr-event', { + detail: event + }) + ) } }); @@ -110,25 +117,35 @@ export async function post({ content, filter, parentId }: { - const publishTimeout = setTimeout(() => { - alert( - `failed to publish comment to any relay.` - ); - }, 5000) - pool.publish(event, (status: number, relay: string) => { - switch (status) { - case -1: - console.log(`failed to send ${JSON.stringify(event)} to ${relay}`) - // enable() - // onError() - break - case 1: - clearTimeout(publishTimeout) - console.log(`event ${event.id?.slice(0, 5)}… published to ${relay}.`) - // onSuccess() - break + return new Promise((resolve, reject) => { + + pool.publish(event, (status: number, relay: string) => { + switch (status) { + case -1: + console.log(`failed to send ${JSON.stringify(event)} to ${relay}`) + break + case 1: + clearTimeout(publishTimeout) + console.log(`event ${event.id?.slice(0, 5)}… published to ${relay}.`) + break + } + }); + + const onEventFetched = (e: CustomEvent) => { + if (e.detail.id === event.id) { + document.removeEventListener('nostr-event', onEventFetched); + resolve(); + } } + document.addEventListener('nostr-event', onEventFetched); + + const publishTimeout = setTimeout(() => { + document.removeEventListener('nostr-event', onEventFetched); + reject("Failed to publish to any relay..."); + }, 5000) + + }) }