UNPKG

@replyke/core

Version:

Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.

405 lines 21.9 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; import { useCallback, useEffect, useRef, useState } from "react"; import useCreateComment from "./useCreateComment"; import { handleError } from "../../utils/handleError"; import useDeleteComment from "./useDeleteComment"; import useUpdateComment from "./useUpdateComment"; import useEntityComments from "./useEntityComments"; import useFetchComment from "./useFetchComment"; import { useUser } from "../user"; import { useEntity, useFetchEntity, useFetchEntityByForeignId, useFetchEntityByShortId, } from "../entities"; import { isUUID } from "../../utils/isUUID"; import { useStableObject } from "../useStableObject"; function useCommentSectionData(props) { var _this = this; var entityProp = props.entity, entityId = props.entityId, foreignId = props.foreignId, shortId = props.shortId, createIfNotFound = props.createIfNotFound, _a = props.defaultSortBy, defaultSortBy = _a === void 0 ? "top" : _a, _b = props.limit, limit = _b === void 0 ? 15 : _b, _c = props.callbacks, callbacksProp = _c === void 0 ? {} : _c, highlightedCommentId = props.highlightedCommentId; // Stabilize callbacks reference to prevent unnecessary re-renders var callbacks = useStableObject(callbacksProp); var _d = useEntity(), entityFromContext = _d.entity, setContextEntity = _d.setEntity; var _e = useState(entityProp !== null && entityProp !== void 0 ? entityProp : entityFromContext), entity = _e[0], setEntity = _e[1]; var user = useUser().user; var _f = useEntityComments({ entityId: entity === null || entity === void 0 ? void 0 : entity.id, defaultSortBy: defaultSortBy, limit: limit }), entityCommentsTree = _f.entityCommentsTree, comments = _f.comments, newComments = _f.newComments, loading = _f.loading, hasMore = _f.hasMore, sortBy = _f.sortBy, setSortBy = _f.setSortBy, loadMore = _f.loadMore, addCommentsToTree = _f.addCommentsToTree, removeCommentFromTree = _f.removeCommentFromTree; var createComment = useCreateComment(); var deleteComment = useDeleteComment(); var updateComment = useUpdateComment(); var fetchComment = useFetchComment(); var fetchEntity = useFetchEntity(); var fetchEntityByForeignId = useFetchEntityByForeignId(); var fetchEntityByShortId = useFetchEntityByShortId(); var _g = useState(null), highlightedComment = _g[0], setHighlightedComment = _g[1]; var fetchingCommentIdRef = useRef(null); var fetchedStatus = useRef({}); // Track status by unique key var submittingComment = useRef(false); var _h = useState(false), submittingCommentState = _h[0], setSubmittingCommentState = _h[1]; // required to trigger rerenders var _j = useState(null), pushMention = _j[0], setPushMention = _j[1]; // const previousPushMention = useRef<null | User>(null); var _k = useState(null), repliedToComment = _k[0], setRepliedToComment = _k[1]; var _l = useState(false), showReplyBanner = _l[0], setShowReplyBanner = _l[1]; var _m = useState(null), selectedComment = _m[0], setSelectedComment = _m[1]; // const handleSetPushMention = (user: User | null) => { // if(!user?.username) // setPushMention((prevMention) => { // if (JSON.stringify(prevMention) === JSON.stringify(user)) { // return prevMention; // } // return user; // }); // }; // For replies that appear as a child of the comment they are replying to. var handleDeepReply = useCallback(function (comment) { setRepliedToComment(comment); setShowReplyBanner(true); }, [setRepliedToComment]); // For replies that appear at the same level as the comment they are replying to. Includes a mention (e.g. @username). var handleShallowReply = useCallback(function (comment) { var _a; setRepliedToComment({ id: (_a = comment.parentId) !== null && _a !== void 0 ? _a : undefined }); setPushMention(comment.user); }, [setRepliedToComment]); var handleCreateComment = useCallback(function (props) { return __awaiter(_this, void 0, void 0, function () { var parentId, content, gif, mentions, filteredMentions, TEMP_ID, tempNewComment, newCommentData, err_1; var _a, _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: parentId = props.parentId, content = props.content, gif = props.gif, mentions = props.mentions; if (submittingComment.current) return [2 /*return*/]; if (!entity) { console.error("Invalid entity in useCommentSection"); return [2 /*return*/]; } if (!user) { (_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.loginRequiredCallback) === null || _a === void 0 ? void 0 : _a.call(callbacks); return [2 /*return*/]; } if ((callbacks === null || callbacks === void 0 ? void 0 : callbacks.usernameRequiredCallback) && !user.username) { callbacks === null || callbacks === void 0 ? void 0 : callbacks.usernameRequiredCallback(); return [2 /*return*/]; } if (!gif && (!content || content.length <= 1)) { callbacks === null || callbacks === void 0 ? void 0 : callbacks.commentTooShortCallback(); return [2 /*return*/]; } submittingComment.current = true; setSubmittingCommentState(true); filteredMentions = content ? (mentions || []).filter(function (mention) { var mentionRegex = new RegExp("@".concat(mention.username, "\\b"), "g"); // const mentionRegex = new RegExp( // `@(${mention.username}|${mention.name})\\b`, // "g" // ); return mentionRegex.test(content); }) : []; TEMP_ID = Math.random().toString(36).substring(2, 7); tempNewComment = { id: TEMP_ID, foreignId: null, projectId: "TEMP_PROJECT_ID", userId: user.id, parentId: (_b = parentId !== null && parentId !== void 0 ? parentId : repliedToComment === null || repliedToComment === void 0 ? void 0 : repliedToComment.id) !== null && _b !== void 0 ? _b : null, entityId: entity.id, content: content !== null && content !== void 0 ? content : null, gif: gif !== null && gif !== void 0 ? gif : null, mentions: filteredMentions, user: __assign(__assign({}, user), { bio: null, birthdate: new Date(), location: null, createdAt: new Date() }), upvotes: [], downvotes: [], createdAt: new Date(), updatedAt: new Date(), deletedAt: null, parentDeletedAt: null, repliesCount: 0, metadata: {}, }; setRepliedToComment(null); setShowReplyBanner(false); setPushMention(null); _d.label = 1; case 1: _d.trys.push([1, 3, 4, 5]); addCommentsToTree([tempNewComment], true); return [4 /*yield*/, createComment({ entityId: entity.id, parentCommentId: (_c = parentId !== null && parentId !== void 0 ? parentId : repliedToComment === null || repliedToComment === void 0 ? void 0 : repliedToComment.id) !== null && _c !== void 0 ? _c : null, content: content, gif: gif, mentions: filteredMentions, })]; case 2: newCommentData = _d.sent(); if (newCommentData) { removeCommentFromTree(TEMP_ID); addCommentsToTree([newCommentData], true); } setContextEntity === null || setContextEntity === void 0 ? void 0 : setContextEntity(function (prevEntity) { if (!prevEntity) return prevEntity; return __assign(__assign({}, prevEntity), { repliesCount: prevEntity.repliesCount + 1 }); }); return [3 /*break*/, 5]; case 3: err_1 = _d.sent(); // TODO: currently we remove the temp comment from the tree but don't offer the user any option to retry. It's as if they've never sent anything and all they typed is gone. We need to add a flag for comment in the tree that says t failed so we can give he user a try again button removeCommentFromTree(TEMP_ID); handleError(err_1, "Failed to submit a new comment: "); return [3 /*break*/, 5]; case 4: submittingComment.current = false; setSubmittingCommentState(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, [ user, addCommentsToTree, removeCommentFromTree, entity, createComment, repliedToComment, callbacks, ]); var handleDeleteComment = useCallback(function (_a) { return __awaiter(_this, [_a], void 0, function (_b) { var err_2; var commentId = _b.commentId; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!isUUID(commentId)) return [2 /*return*/]; _c.label = 1; case 1: _c.trys.push([1, 3, , 4]); removeCommentFromTree(commentId); return [4 /*yield*/, deleteComment({ commentId: commentId })]; case 2: _c.sent(); setContextEntity === null || setContextEntity === void 0 ? void 0 : setContextEntity(function (prevEntity) { if (!prevEntity) return prevEntity; return __assign(__assign({}, prevEntity), { repliesCount: prevEntity.repliesCount - 1 }); }); return [3 /*break*/, 4]; case 3: err_2 = _c.sent(); handleError(err_2, "Failed to delete comment"); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }, [deleteComment, removeCommentFromTree]); var handleUpdateComment = useCallback(function (_a) { return __awaiter(_this, [_a], void 0, function (_b) { var updatedComment, err_3; var commentId = _b.commentId, content = _b.content; return __generator(this, function (_c) { switch (_c.label) { case 0: _c.trys.push([0, 2, , 3]); return [4 /*yield*/, updateComment({ commentId: commentId, content: content })]; case 1: updatedComment = _c.sent(); if (updatedComment) { console.log("update comment in tree. Implement!"); } return [3 /*break*/, 3]; case 2: err_3 = _c.sent(); handleError(err_3, "Failed to update comment"); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }, [updateComment]); useEffect(function () { var handleFetchSingleComment = function () { return __awaiter(_this, void 0, void 0, function () { var fetchedCommentData, targetComment, parentComment, err_4; return __generator(this, function (_a) { switch (_a.label) { case 0: if (fetchingCommentIdRef.current === highlightedCommentId) { return [2 /*return*/]; // Skip if already fetching for this comment ID } fetchingCommentIdRef.current = highlightedCommentId; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, fetchComment({ commentId: highlightedCommentId, withParent: true, })]; case 2: fetchedCommentData = _a.sent(); if (!fetchedCommentData) { console.error("Issue fetching single comment comment not found"); return [2 /*return*/]; } if (!fetchedCommentData.comment) { console.error("Highlighted comment not found"); } setHighlightedComment(fetchedCommentData); targetComment = fetchedCommentData.comment, parentComment = fetchedCommentData.parentComment; addCommentsToTree === null || addCommentsToTree === void 0 ? void 0 : addCommentsToTree(parentComment ? [targetComment, parentComment] : [targetComment]); return [3 /*break*/, 4]; case 3: err_4 = _a.sent(); handleError(err_4, "Fetching single comment failed"); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; if (highlightedCommentId) { handleFetchSingleComment(); } }, [highlightedCommentId, fetchComment, addCommentsToTree]); useEffect(function () { var handleFetchEntity = function () { return __awaiter(_this, void 0, void 0, function () { var uniqueKey, fetchedEntity, err_5; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!foreignId && !entityId && !shortId) { return [2 /*return*/]; } if (entity && entityId && entity.id === entityId) return [2 /*return*/]; if (entity && foreignId && entity.foreignId === foreignId) return [2 /*return*/]; if (entity && shortId && entity.shortId === shortId) return [2 /*return*/]; uniqueKey = "".concat(entityId !== null && entityId !== void 0 ? entityId : "", "-").concat(foreignId !== null && foreignId !== void 0 ? foreignId : "", "-").concat(shortId !== null && shortId !== void 0 ? shortId : ""); if (fetchedStatus.current[uniqueKey]) return [2 /*return*/]; fetchedStatus.current[uniqueKey] = true; _a.label = 1; case 1: _a.trys.push([1, 8, , 9]); fetchedEntity = null; if (!entityId) return [3 /*break*/, 3]; return [4 /*yield*/, fetchEntity({ entityId: entityId, })]; case 2: fetchedEntity = _a.sent(); return [3 /*break*/, 7]; case 3: if (!foreignId) return [3 /*break*/, 5]; return [4 /*yield*/, fetchEntityByForeignId({ foreignId: foreignId, createIfNotFound: createIfNotFound, })]; case 4: fetchedEntity = _a.sent(); return [3 /*break*/, 7]; case 5: if (!shortId) return [3 /*break*/, 7]; return [4 /*yield*/, fetchEntityByShortId({ shortId: shortId, })]; case 6: fetchedEntity = _a.sent(); _a.label = 7; case 7: if (fetchedEntity) { setEntity(fetchedEntity); } return [3 /*break*/, 9]; case 8: err_5 = _a.sent(); handleError(err_5, "Fetching entity failed"); return [3 /*break*/, 9]; case 9: return [2 /*return*/]; } }); }); }; handleFetchEntity(); }, [ fetchEntity, fetchEntityByForeignId, fetchEntityByShortId, entityId, foreignId, shortId, entity, createIfNotFound, ]); return { entity: entity, callbacks: callbacks, entityCommentsTree: entityCommentsTree, comments: comments, newComments: newComments, highlightedComment: highlightedComment, loading: loading, hasMore: hasMore, submittingComment: submittingCommentState, loadMore: loadMore, sortBy: sortBy, setSortBy: setSortBy, pushMention: pushMention, selectedComment: selectedComment, setSelectedComment: setSelectedComment, repliedToComment: repliedToComment, setRepliedToComment: setRepliedToComment, showReplyBanner: showReplyBanner, setShowReplyBanner: setShowReplyBanner, addCommentsToTree: addCommentsToTree, removeCommentFromTree: removeCommentFromTree, handleShallowReply: handleShallowReply, handleDeepReply: handleDeepReply, createComment: handleCreateComment, updateComment: handleUpdateComment, deleteComment: handleDeleteComment, }; } export default useCommentSectionData; //# sourceMappingURL=useCommentSectionData.js.map