@replyke/express
Version:
Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.
208 lines (207 loc) • 8.99 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const models_1 = require("../../../models");
const sequelize_query_params_1 = require("../../../constants/sequelize-query-params");
const createNotification_1 = __importDefault(require("../../../helpers/createNotification"));
const updateUserReputation_1 = __importDefault(require("../../../helpers/updateUserReputation"));
const reputation_scores_1 = __importDefault(require("../../../constants/reputation-scores"));
const config_1 = require("../../../config");
exports.default = async (req, res) => {
try {
const { sequelize } = (0, config_1.getCoreConfig)();
const { userId: userIdProp, // Not passed from React libraries hook, but could be passed from server using the js sdk
entityId, foreignId, content, gif, mentions, parentId, referencedCommentId, attachments, metadata, createdAt, updatedAt, } = req.body;
const loggedInUserId = req.userId;
const projectId = req.project.id;
// Validate the presence of required fields.
if (!entityId) {
res.status(400).json({
error: "Missing entity ID",
code: "comment/missing-entity-id",
});
return;
}
if (!content && !gif && (attachments ?? []).length === 0) {
res.status(400).json({
error: "Missing required comment content",
code: "comment/missing-content",
});
return;
}
// Update the entity's comment or reply count
const entity = (await models_1.Entity.findOne({
where: { projectId, id: entityId },
}));
if (!entity) {
res
.status(404)
.json({ error: "Entity not found", code: "comment/entity-not-found" });
return;
}
let userId = null;
if ((req.isMaster || req.isService) && userIdProp) {
userId = userIdProp;
}
else if (loggedInUserId) {
userId = loggedInUserId;
}
if (!userId) {
res.status(400).json({
error: "Missing user ID",
code: "comment/missing-user-id",
});
return;
}
const newCommentData = {
projectId,
foreignId,
referenceId: foreignId,
userId,
entityId,
parentId,
content,
gif,
mentions,
referencedCommentId,
attachments,
metadata,
};
// Only master/service may even attempt to set createdAt or updatedAt
if (req.isMaster || req.isService) {
// parse timestamps if provided
const tsCreated = createdAt ? new Date(createdAt) : undefined;
const tsUpdated = updatedAt ? new Date(updatedAt) : undefined;
// updatedAt but no createdAt ⇒ error
if (tsUpdated && !tsCreated) {
res.status(400).json({
error: "Cannot set updatedAt without also setting createdAt.",
code: "comment/invalid-timestamp",
});
return;
}
if (tsCreated) {
// set createdAt
newCommentData.createdAt = tsCreated;
if (tsUpdated) {
// 4) both passed ⇒ updatedAt must be ≥ createdAt
if (tsUpdated < tsCreated) {
res.status(400).json({
error: "updatedAt must be the same or after createdAt.",
code: "comment/invalid-timestamp",
});
return;
}
newCommentData.updatedAt = tsUpdated;
}
else {
// 3) createdAt passed but no updatedAt ⇒ mirror createdAt
newCommentData.updatedAt = tsCreated;
}
}
}
const { comment } = await sequelize.transaction(async (transaction) => {
// Create the comment using Sequelize's create method
const comment = (await models_1.Comment.create(newCommentData, {
transaction,
}));
await (0, updateUserReputation_1.default)(userId, reputation_scores_1.default.createComment, transaction);
return { comment };
});
// Fetch the comment again with the associated user
const populatedComment = (await models_1.Comment.findOne({
where: { id: comment.id },
...sequelize_query_params_1.commentParams,
}));
const { handlers } = (0, config_1.getCoreConfig)();
await handlers.createComment({ projectId });
// Return the newly created comment or reply
res.status(201).json(populatedComment.toJSON());
// Fetch the user
const user = (await models_1.User.findByPk(userId));
if (!user) {
console.error("Logged in user object wasn't found after comment creation.");
return;
}
if (comment.parentId) {
const parentComment = (await models_1.Comment.findOne({
where: { id: comment.parentId },
}));
if (parentComment && parentComment.userId) {
(0, createNotification_1.default)(req, res, {
userId: parentComment.userId, // The recipient user ID, assumed here
projectId,
type: "comment-reply",
action: "open-comment",
metadata: {
entityId: entity.id,
entityShortId: entity.shortId,
entityTitle: entity.title,
entityContent: (entity.content || "").slice(0, 200),
commentId: comment.id, // This should stay and not the parent ID because we want this to be highlighted
commentContent: comment.content,
replyId: comment.parentId,
replyContent: "",
initiatorId: user.id,
initiatorName: user.name,
initiatorUsername: user.username,
initiatorAvatar: user.avatar,
},
});
}
}
else if (entity.userId) {
(0, createNotification_1.default)(req, res, {
userId: entity.userId, // The recipient user ID, assumed here
projectId,
type: "entity-comment",
action: "open-comment",
metadata: {
entityId: entity.id,
entityShortId: entity.shortId,
entityTitle: entity.title,
entityContent: (entity.content || "").slice(0, 200),
commentId: comment.id,
commentContent: comment.content,
initiatorId: user.id,
initiatorName: user.name,
initiatorUsername: user.username,
initiatorAvatar: user.avatar,
},
});
}
comment.mentions.forEach((mention) => {
// We check if the mention ID dons't equal the userId of the author of the entity as the author of the entity wil get a notification fo this comment regardless of th mention, and we don't want to send two
if (mention.id && mention.id !== entity.userId) {
(0, createNotification_1.default)(req, res, {
userId: mention.id, // The recipient user ID, assumed here
projectId,
type: "comment-mention",
action: "open-comment",
metadata: {
entityId: entity.id,
entityShortId: entity.shortId,
entityTitle: entity.title,
entityContent: (entity.content || "").slice(0, 200),
commentId: comment.id,
commentContent: comment.content,
initiatorId: loggedInUserId,
initiatorName: user.name,
initiatorUsername: user.username,
initiatorAvatar: user.avatar,
},
});
}
});
}
catch (err) {
console.error("Error posting a comment: ", err);
res.status(500).json({
error: "Internal server error",
code: "comment/server-error",
details: err.message,
});
}
};