Skip to content

Commit

Permalink
feat(comments): support pending comments in state and UI
Browse files Browse the repository at this point in the history
- **src/redux/comments/index.ts**:
  - Added `comments` and `pendingComments` to the state.
  - Introduced reducers: `addComment`, `addPendingComment`, and `updateCommentStatus`.
  - Added respective actions to `commentsSlice`.

- **src/sections/publication/publication-details-comment-form.tsx**:
  - Implemented logic to handle pending comments.
  - Dispatch `addPendingComment` upon submission.
  - Updated comment status to "confirmed" after successful posting.

- **src/sections/publication/publication-comments-list.tsx**:
  - Introduced `pendingComments` integration into the comment list.
  - Pending comments are displayed at the top of the list.
  • Loading branch information
cswni committed Dec 17, 2024
1 parent 9669913 commit e5b3005
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 4 deletions.
28 changes: 28 additions & 0 deletions src/redux/comments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ export type CommentsReducerState = {
};
hiddenComments: AnyPublication[];
counterLikes: { [publicationId: string]: number };
comments: { [publicationId: string]: AnyPublication[] };
pendingComments: { [publicationId: string]: AnyPublication[] };
};

const initialState: CommentsReducerState = {
refetchTriggerByPublication: {},
hiddenComments: [],
counterLikes: {},
comments: {},
pendingComments: {},
};

const commentsSlice = createSlice({
Expand Down Expand Up @@ -45,6 +49,27 @@ const commentsSlice = createSlice({
state.counterLikes[publicationId] -= 1;
}
},
addComment: (state, action: PayloadAction<{ publicationId: string; comment: AnyPublication }>) => {
const { publicationId, comment } = action.payload;
if (!state.comments[publicationId]) {
state.comments[publicationId] = [];
}
state.comments[publicationId].push(comment);
},
addPendingComment: (state, action: PayloadAction<{ publicationId: string; comment: AnyPublication }>) => {
const { publicationId, comment } = action.payload;
if (!state.pendingComments[publicationId]) {
state.pendingComments[publicationId] = [];
}
// Prepend new comment to the beginning of the list
state.pendingComments[publicationId] = [comment, ...state.pendingComments[publicationId]];
},
updateCommentStatus: (state, action: PayloadAction<{ publicationId: string; commentId: string; status: string }>) => {
const { publicationId, commentId, status } = action.payload;
const comment = state.comments[publicationId]?.find(comment => comment.id === commentId);
state.comments[publicationId] = state.comments[publicationId].filter(comment => comment.id !== commentId);

},
},
});

Expand All @@ -54,6 +79,9 @@ export const {
setCounterLikes,
incrementCounterLikes,
decrementCounterLikes,
addComment,
addPendingComment,
updateCommentStatus,
} = commentsSlice.actions;

export default commentsSlice.reducer;
9 changes: 8 additions & 1 deletion src/sections/publication/publication-comments-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Props = {
};

export default function PostCommentList({ publicationId: id, showReplies }: Props) {
const pendingComments = useSelector((state: any) => state.comments.pendingComments);
const { data: comments, error, loading, execute } = useLazyPublications();
const { hiddenComments, refetchTriggerByPublication } = useSelector((state: any) => state.comments);
const refetchTrigger = refetchTriggerByPublication[id] || 0;
Expand All @@ -37,7 +38,13 @@ export default function PostCommentList({ publicationId: id, showReplies }: Prop

if (error) return <p>Error loading comments: {error.message}</p>;

const commentsFiltered = (comments ?? [])
// Join the comments with the pending comments but append the pending comments at the beginning of the list
const commentsWithPending = pendingComments[id] ? [...pendingComments[id], ...(comments ?? [])] : comments;

console.log('comments', comments);
console.log('comments pending', pendingComments[id]);

const commentsFiltered = (commentsWithPending ?? [])
.filter((comment) => !hiddenComments.some((hiddenComment: any) => hiddenComment.id === comment.id))
.filter((comment) => !comment.isHidden)

Expand Down
56 changes: 53 additions & 3 deletions src/sections/publication/publication-details-comment-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import { ReadResult } from '@lens-protocol/react/dist/declarations/src/helpers/r
import { uploadMetadataToIPFS, verifyIpfsData } from '@src/utils/ipfs';
import uuidv4 from '@src/utils/uuidv4.ts';
import { useDispatch } from 'react-redux';
import { refetchCommentsByPublication } from '@redux/comments';
import {refetchCommentsByPublication, addPendingComment, updateCommentStatus} from '@redux/comments';
import {useNotifications} from "@src/hooks/use-notifications.ts";
import { useNotificationPayload } from '@src/hooks/use-notification-payload.ts';
import {AnyPublication} from "@lens-protocol/api-bindings";

// Define the props types
type MovieCommentFormProps = {
Expand Down Expand Up @@ -106,9 +107,11 @@ const MovieCommentForm = ({ commentOn, owner, root }: MovieCommentFormProps) =>
*/
const onSubmit = handleSubmit(async (data) => {
try {
const uuid = uuidv4();

const metadata = textOnly({
appId: 'watchit',
id: uuidv4(),
id: uuid,
attributes: [
{ type: MetadataAttributeType.STRING, key: 'publication', value: commentOn },
{ type: MetadataAttributeType.STRING, key: 'creator', value: sessionData?.profile?.handle?.localName },
Expand All @@ -124,10 +127,55 @@ const MovieCommentForm = ({ commentOn, owner, root }: MovieCommentFormProps) =>
{ display_type: MarketplaceMetadataAttributeDisplayType.STRING, value: 'watchit' },
],
description: data.comment,
external_url: `https://watchit.movie/comment/${uuidv4()}`,
external_url: `https://watchit.movie/comment/${uuid}`,
}
});

// Create a pending comment object
const pendingComment: AnyPublication = {
// @ts-ignore
id: uuid as string,
isHidden: false,
isHiddenByAuthor: false,
// @ts-ignore
// Add metadata to the comment but replace the content with the original comment
metadata: {
...metadata,
content: data.comment,
},
// @ts-ignore
operations: {
hasUpvoted: false
},
status: 'pending',
by: sessionData?.profile,
createdAt: new Date().toISOString(),
__typename: 'Comment',
hashtagsMentioned: [],
profilesMentioned: [],
quoteOn: {
// @ts-ignore
__typename: 'CommentFields',
// @ts-ignore
id: commentOn,
createdAt: new Date().toISOString(),
// @ts-ignore
metadata,
by: sessionData?.profile,
stats: {
// @ts-ignore
totalAmountOfMirrors: 0,
totalAmountOfCollects: 0,
totalAmountOfComments: 0,
totalAmountOfUpvotes: 0,
},
},
};

reset(); // Reset the form
// Dispatch the addPendingComment action
dispatch(addPendingComment({ publicationId: commentOn, comment: pendingComment }));

// Validate metadata against the schema
const validation = PublicationMetadataSchema.safeParse(metadata);
if (!validation.success) {
Expand All @@ -146,6 +194,8 @@ const MovieCommentForm = ({ commentOn, owner, root }: MovieCommentFormProps) =>
commentOn: commentOn as any,
metadata: uri,
}).then(() => {
// Update the comment status to confirmed
dispatch(updateCommentStatus({ publicationId: commentOn, commentId: pendingComment.id, status: 'confirmed' }));
// Send notifications to the author of the publication
const notificationPayload = generatePayload('COMMENT', {
id: owner?.id,
Expand Down

0 comments on commit e5b3005

Please sign in to comment.