From 563db365f74313b83ffa6f05d600561a92817bcb Mon Sep 17 00:00:00 2001 From: Amit S Namboothiry Date: Sat, 10 Feb 2024 17:37:59 +0530 Subject: [PATCH] Add comments count to UI --- api/posts-service/get.js | 96 +++++++++++++++++++++++++++++++------- api/posts-service/list.js | 78 +++++++++++++++++++++++++++++++ package-lock.json | 4 +- package.json | 2 +- src/containers/Content.css | 42 +++++++++++++++-- src/containers/Content.js | 89 +++++++++++++++++++++++++++++++---- 6 files changed, 279 insertions(+), 32 deletions(-) diff --git a/api/posts-service/get.js b/api/posts-service/get.js index cc4da971..2f431363 100644 --- a/api/posts-service/get.js +++ b/api/posts-service/get.js @@ -2,21 +2,78 @@ import * as dynamoDbLib from "../libs/dynamodb-lib"; import * as userNameLib from "../libs/username-lib"; import { success, failure } from "../libs/response-lib"; +async function countReplyComments(commentId) { + const params = { + TableName: "NaadanChordsComments", + ScanFilter: { + commentId: { + ComparisonOperator: "EQ", + AttributeValueList: [commentId], + }, + }, + }; + const comment = await dynamoDbLib.call("scan", params); + if (comment.Items && comment.Items.length === 1) { + let count = 1; + let commentItem = comment.Items[0]; + let replies = commentItem.replies || []; + for (let i = 0; i < replies.length; i++) { + const replyCommentsCount = await countReplyComments(replies[i]); + count += replyCommentsCount; + } + return count; + } + return 0; +} + +async function appendCommentsCount(item) { + let filterExpression = "contains(postId, :postId)"; + let expressionAttributeValues = {}; + expressionAttributeValues[`:postId`] = item.postId; + + let params = { + TableName: "NaadanChordsComments", + FilterExpression: filterExpression, + ExpressionAttributeValues: expressionAttributeValues, + }; + + try { + let commentsResult = await dynamoDbLib.call("scan", params); + let comments = commentsResult.Items; + let commentsCount = 0; + + for (let i = 0; i < comments.length; i++) { + let commentItem = comments[i]; + let replies = commentItem.replies || []; + commentsCount += 1; + for (let j = 0; j < replies.length; j++) { + const replyCommentsCount = await countReplyComments(replies[j]); + commentsCount += replyCommentsCount; + } + } + item.commentsCount = commentsCount; + } catch (e) { + item.commentsError = e; + } + + return item; +} + async function appendRating(item) { const params = { TableName: "NaadanChordsRatings", Key: { - postId: item.postId - } + postId: item.postId, + }, }; try { let ratingResult = await dynamoDbLib.call("get", params); - if(ratingResult && ratingResult.hasOwnProperty("Item")) { + if (ratingResult && ratingResult.hasOwnProperty("Item")) { item.rating = ratingResult.Item.rating; item.ratingCount = ratingResult.Item.count; } - } catch(e) { + } catch (e) { item.ratingError = e; } @@ -26,7 +83,7 @@ async function appendRating(item) { function retryLoop(postId) { let keywords = postId.split("-"); - if(keywords.length > 1) { + if (keywords.length > 1) { keywords.pop(); return retryGet(keywords.join("-")); } else { @@ -38,28 +95,29 @@ async function retryGet(postId) { let params = { TableName: "NaadanChords", ScanFilter: { - "postId": { + postId: { ComparisonOperator: "CONTAINS", - AttributeValueList: [postId] - } - } + AttributeValueList: [postId], + }, + }, }; - if(postId.length > 2) { + if (postId.length > 2) { try { const result = await dynamoDbLib.call("scan", params); - if(result.Items.length > 0) { + if (result.Items.length > 0) { let finalResult = result.Items[0]; let userId = finalResult.userId; //Get full attributes of author let authorAttributes = await userNameLib.getAuthorAttributes(userId); finalResult.authorName = authorAttributes.authorName; - finalResult.userName = authorAttributes.preferredUsername ?? authorAttributes.userName; + finalResult.userName = + authorAttributes.preferredUsername ?? authorAttributes.userName; finalResult.authorPicture = authorAttributes.picture; //Do not expose userId - delete(finalResult.userId); + delete finalResult.userId; finalResult = await appendRating(finalResult); return success(finalResult); @@ -78,8 +136,8 @@ export async function main(event, context) { const params = { TableName: "NaadanChords", Key: { - postId: event.pathParameters.id - } + postId: event.pathParameters.id, + }, }; try { @@ -90,13 +148,15 @@ export async function main(event, context) { //Get full attributes of author let authorAttributes = await userNameLib.getAuthorAttributes(userId); result.Item.authorName = authorAttributes.authorName; - result.Item.userName = authorAttributes.preferredUsername ?? authorAttributes.userName; + result.Item.userName = + authorAttributes.preferredUsername ?? authorAttributes.userName; result.Item.authorPicture = authorAttributes.picture; //Do not expose userId - delete(result.Item.userId); + delete result.Item.userId; let finalResult = await appendRating(result.Item); + finalResult = await appendCommentsCount(result.Item); return success(finalResult); } else { return retryGet(event.pathParameters.id); @@ -104,4 +164,4 @@ export async function main(event, context) { } catch (e) { return failure({ status: false, error: e }); } -} \ No newline at end of file +} diff --git a/api/posts-service/list.js b/api/posts-service/list.js index 0cef9293..a1230547 100644 --- a/api/posts-service/list.js +++ b/api/posts-service/list.js @@ -2,6 +2,81 @@ import * as dynamoDbLib from "../libs/dynamodb-lib"; import * as userNameLib from "../libs/username-lib"; import * as searchFilterLib from "../libs/searchfilter-lib"; +async function countReplyComments(commentId) { + const params = { + TableName: "NaadanChordsComments", + ScanFilter: { + commentId: { + ComparisonOperator: "EQ", + AttributeValueList: [commentId], + }, + }, + }; + const comment = await dynamoDbLib.call("scan", params); + if (comment.Items && comment.Items.length === 1) { + let count = 1; + let commentItem = comment.Items[0]; + let replies = commentItem.replies || []; + for (let i = 0; i < replies.length; i++) { + const replyCommentsCount = await countReplyComments(replies[i]); + count += replyCommentsCount; + } + return count; + } + return 0; +} + +async function appendCommentsCount(result) { + let items = result.Items; + let filterExpression = ""; + let expressionAttributeValues = {}; + + for (let i = 0; i < items.length; i++) { + let postId = items[i].postId; + if (filterExpression) { + filterExpression += ` OR contains(postId, :postId${i})`; + } else { + filterExpression = `contains(postId, :postId${i})`; + } + expressionAttributeValues[`:postId${i}`] = postId; + } + + let params = { + TableName: "NaadanChordsComments", + FilterExpression: filterExpression, + ExpressionAttributeValues: expressionAttributeValues, + }; + + try { + let commentsResult = await dynamoDbLib.call("scan", params); + let comments = commentsResult.Items; + let commentsObject = {}; + + for (let i = 0; i < comments.length; i++) { + let commentItem = comments[i]; + let replies = commentItem.replies || []; + commentsObject[commentItem.postId] = + (commentsObject[commentItem.postId] || 0) + 1; + for (let j = 0; j < replies.length; j++) { + const replyCommentsCount = await countReplyComments(replies[j]); + commentsObject[commentItem.postId] += replyCommentsCount; + } + } + + for (let i = 0; i < items.length; i++) { + if (commentsObject.hasOwnProperty(items[i].postId)) { + items[i].commentsCount = commentsObject[items[i].postId]; + } + } + + result.Items = items; + } catch (e) { + result.commentsError = e; + } + + return result; +} + async function appendRatings(result) { let items = result.Items; let filterExpression = ""; @@ -213,6 +288,9 @@ export async function main(event, context, callback) { //append ratings let finalResult = await appendRatings(result); + //append comments count + finalResult = await appendCommentsCount(result); + return finalResult; } catch (e) { return { status: false, error: e }; diff --git a/package-lock.json b/package-lock.json index 229cf6ea..eda1f11c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "naadan-chords", - "version": "0.80.3", + "version": "0.80.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "naadan-chords", - "version": "0.80.3", + "version": "0.80.4", "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.34", "@fortawesome/free-brands-svg-icons": "^5.15.2", diff --git a/package.json b/package.json index 2d2fbac0..05d4c404 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "naadan-chords", - "version": "0.80.3", + "version": "0.80.4", "homepage": "https://www.naadanchords.com/", "private": true, "dependencies": { diff --git a/src/containers/Content.css b/src/containers/Content.css index 2c45e872..60b1ebe7 100644 --- a/src/containers/Content.css +++ b/src/containers/Content.css @@ -31,7 +31,7 @@ padding: 0.1em 0.4em 0.02em 0.4em; vertical-align: text-bottom; cursor: pointer; - line-height: 0.93rem; + line-height: 1.05rem; } .Content .post small .badge { @@ -141,6 +141,37 @@ top: -2px; } +.Content .rating-comments-container { + margin-top: 7px; +} + +.Content .rating-comments-container.post-list { + float: right; + margin-top: 0; +} + +.Content .post-comment-count { + margin-left: 0; + cursor: pointer; +} + +.Content .post-comment-count.post-list { + margin-left: 10px; +} + +.Content .post-comment-count .comment-text-container { + font-weight: bold; +} + +.Content .post-comment-count .comment-icon { + margin-left: 4px; + font-size: 11px; +} + +.Content .post-comment-count.post-list .comment-icon { + font-size: 9px; +} + .Content .rate-container { position: relative; } @@ -195,6 +226,10 @@ display: none; } + .Content .post small .post-comment-count span.separator { + display: inline-block; + } + .Content .post small .meta-time-container { display: block; position: relative; @@ -213,8 +248,9 @@ display: block; } - .Content .post-rating.post-list { - display: block; + .Content .rating-comments-container { + display: flex; + float: none; } .Content .postList .post small span.separator { diff --git a/src/containers/Content.js b/src/containers/Content.js index 84b78c73..93c22f6e 100644 --- a/src/containers/Content.js +++ b/src/containers/Content.js @@ -22,6 +22,7 @@ import { faRandom, faHistory, faUserCircle, + faCommentAlt, } from "@fortawesome/free-solid-svg-icons"; import { slugify, capitalizeFirstLetter } from "../libs/utils"; import Sidebar from "./Sidebar"; @@ -302,7 +303,10 @@ export default class Content extends Component { {post.authorName} - {this.renderRating(post, true)} + + {this.renderRating(post, true)} + {this.renderCommentCount(post, true)} + ))} @@ -315,6 +319,30 @@ export default class Content extends Component { } }; + renderCommentLink = (isPostList) => { + if (!isPostList) { + return ( + +
+ { + e.preventDefault(); + this.ratingEl.current.scrollIntoView({ + behavior: "instant", + block: "start", + }); + this.ratingEl.current.click(); + }} + > + Click here to add a comment + +
+ ); + } + }; + renderRateLink = (isPostList) => { if (!isPostList) { return ( @@ -366,7 +394,6 @@ export default class Content extends Component { renderRating = (post, isPostList) => { return ( - | - 0 ? "" : "d-none"}`}> - ({post.ratingCount}) - + {!isPostList && ( + 0 ? "" : "d-none"}`}> + ({post.ratingCount}) + + )} + + + + ); + }; + + commentPopover = (post, isPostList) => { + if (post.commentsCount) { + return ( + + {post.commentsCount} comment{post.commentsCount > 1 ? "s" : ""} + .{this.renderCommentLink(isPostList)} + + ); + } else { + return ( + + No comments yet. +
+ + Why don't you start a discussion? :) + + {this.renderCommentLink(isPostList)} +
+ ); + } + }; + + renderCommentCount = (post, isPostList) => { + return ( + + {!isPostList && |} + + + {post.commentsCount || 0} + @@ -430,7 +500,7 @@ export default class Content extends Component { > {capitalizeFirstLetter(post.category)} - | + |
post.createdAt && ( <> - | + | )}
- {this.renderRating(post)} +
+ {this.renderRating(post)} + {this.renderCommentCount(post)} +