UNPKG

@replyke/core

Version:

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

353 lines 19.4 kB
"use strict"; 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 }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = require("react"); var useCreateComment_1 = __importDefault(require("./useCreateComment")); var handleError_1 = require("../../utils/handleError"); var useDeleteComment_1 = __importDefault(require("./useDeleteComment")); var useUpdateComment_1 = __importDefault(require("./useUpdateComment")); var useEntityComments_1 = __importDefault(require("./useEntityComments")); var useFetchSingleComment_1 = __importDefault(require("./useFetchSingleComment")); var useUser_1 = __importDefault(require("../users/useUser")); var entities_1 = require("../entities"); function useCommentSectionData(props) { var _this = this; var entityProp = props.entity, entityId = props.entityId, referenceId = props.referenceId, 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, callbacks = _c === void 0 ? {} : _c, highlightedCommentId = props.highlightedCommentId; var _d = (0, react_1.useState)(entityProp), entity = _d[0], setEntity = _d[1]; var user = (0, useUser_1.default)().user; var _e = (0, useEntityComments_1.default)({ entityId: entity === null || entity === void 0 ? void 0 : entity.id, defaultSortBy: defaultSortBy, limit: limit }), entityCommentsTree = _e.entityCommentsTree, comments = _e.comments, newComments = _e.newComments, loadingState = _e.loading, // we use the state to trigger renders hasMoreState = _e.hasMore, sortBy = _e.sortBy, setSortBy = _e.setSortBy, loadMore = _e.loadMore, addCommentsToTree = _e.addCommentsToTree, removeCommentFromTree = _e.removeCommentFromTree; var createComment = (0, useCreateComment_1.default)(); var deleteComment = (0, useDeleteComment_1.default)(); var updateComment = (0, useUpdateComment_1.default)(); var fetchSingleComment = (0, useFetchSingleComment_1.default)(); var fetchSingleEntity = (0, entities_1.useFetchSingleEntity)(); var _f = (0, react_1.useState)(null), highlightedComment = _f[0], setHighlightedComment = _f[1]; var fetchingCommentIdRef = (0, react_1.useRef)(null); var fetchedStatus = (0, react_1.useRef)({}); // Track status by unique key var submittingComment = (0, react_1.useRef)(false); var _g = (0, react_1.useState)(false), submittingCommentState = _g[0], setSubmittingCommentState = _g[1]; // required to trigger rerenders var _h = (0, react_1.useState)(null), pushMention = _h[0], setPushMention = _h[1]; // const previousPushMention = useRef<null | UserLean>(null); var _j = (0, react_1.useState)(null), repliedToComment = _j[0], setRepliedToComment = _j[1]; var _k = (0, react_1.useState)(false), showReplyBanner = _k[0], setShowReplyBanner = _k[1]; var _l = (0, react_1.useState)(null), selectedComment = _l[0], setSelectedComment = _l[1]; // const handleSetPushMention = (user: UserLean | null) => { // console.log("handleSetPushMention called"); // if(!user?.username) // console.log("handleSetPushMention is valid"); // 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 = (0, react_1.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 = (0, react_1.useCallback)(function (comment) { var _a; setRepliedToComment({ id: (_a = comment.parentId) !== null && _a !== void 0 ? _a : undefined }); setPushMention(comment.user); }, [setRepliedToComment]); var handleCreateComment = (0, react_1.useCallback)(function (props) { return __awaiter(_this, void 0, void 0, function () { var content, gif, mentions, filteredMentions, TEMP_ID, tempNewComment, newCommentData, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: 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) { throw new Error("No user is authenticated"); } 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)) { throw new Error("Comment is too short"); } 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 = { projectId: "TEMP_PROJECT_ID", entityId: entity.id, content: content !== null && content !== void 0 ? content : null, gif: gif !== null && gif !== void 0 ? gif : null, mentions: filteredMentions, parentId: (repliedToComment === null || repliedToComment === void 0 ? void 0 : repliedToComment.id) || null, id: TEMP_ID, userId: user.id, user: user, upvotes: [], downvotes: [], createdAt: new Date(), updatedAt: new Date(), deletedAt: null, parentDeletedAt: null, repliesCount: 0, }; addCommentsToTree([tempNewComment], true); setRepliedToComment(null); setShowReplyBanner(false); setPushMention(null); _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); return [4 /*yield*/, createComment({ entityId: entity.id, parentCommentId: repliedToComment === null || repliedToComment === void 0 ? void 0 : repliedToComment.id, content: content, gif: gif, mentions: filteredMentions, })]; case 2: newCommentData = _a.sent(); if (newCommentData) { removeCommentFromTree(TEMP_ID); addCommentsToTree([newCommentData], true); } return [3 /*break*/, 5]; case 3: err_1 = _a.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); (0, handleError_1.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 = (0, react_1.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: _c.trys.push([0, 2, , 3]); removeCommentFromTree(commentId); return [4 /*yield*/, deleteComment({ commentId: commentId })]; case 1: _c.sent(); return [3 /*break*/, 3]; case 2: err_2 = _c.sent(); (0, handleError_1.handleError)(err_2, "Failed to delete comment"); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }, [deleteComment, removeCommentFromTree]); var handleUpdateComment = (0, react_1.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(); (0, handleError_1.handleError)(err_3, "Failed to update comment"); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }, [updateComment]); (0, react_1.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*/, fetchSingleComment({ 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(); (0, handleError_1.handleError)(err_4, "Fetching single comment failed"); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; if (highlightedCommentId) { handleFetchSingleComment(); } }, [highlightedCommentId, fetchSingleComment, addCommentsToTree]); (0, react_1.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 (!referenceId && !entityId && !shortId) return [2 /*return*/]; if (entity && entityId && entity.id === entityId) return [2 /*return*/]; if (entity && referenceId && entity.referenceId === referenceId) return [2 /*return*/]; if (entity && shortId && entity.shortId === shortId) return [2 /*return*/]; uniqueKey = "".concat(entityId !== null && entityId !== void 0 ? entityId : "", "-").concat(referenceId !== null && referenceId !== void 0 ? referenceId : "", "-").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, 3, , 4]); return [4 /*yield*/, fetchSingleEntity({ entityId: entityId, referenceId: referenceId, shortId: shortId, createIfNotFound: createIfNotFound, })]; case 2: fetchedEntity = _a.sent(); setEntity(fetchedEntity); return [3 /*break*/, 4]; case 3: err_5 = _a.sent(); (0, handleError_1.handleError)(err_5, "Fetching entity failed"); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; handleFetchEntity(); }, [ fetchSingleEntity, entityId, referenceId, shortId, entity, createIfNotFound, ]); return { entity: entity, callbacks: callbacks, entityCommentsTree: entityCommentsTree, comments: comments, newComments: newComments, highlightedComment: highlightedComment, loading: loadingState, // we use the state to trigger renders hasMore: hasMoreState, 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, }; } exports.default = useCommentSectionData; //# sourceMappingURL=useCommentSectionData.js.map