diff --git a/backend/src/controllers/blog.controller.ts b/backend/src/controllers/blog.controller.ts index 5fbde7f..41387bb 100644 --- a/backend/src/controllers/blog.controller.ts +++ b/backend/src/controllers/blog.controller.ts @@ -18,6 +18,8 @@ import { ERROR, Like, LikedUser, + NumOfCommentsReq, + NumOfCommentsRes, RemoveLikeReq, RemoveLikeRes, SpaceBlogsReq, @@ -43,6 +45,7 @@ export interface blogController { unLikeBlog: HandlerWithParams<{ blogId: string }, RemoveLikeReq, RemoveLikeRes>; getBlogLikes: HandlerWithParams<{ blogId: string }, BlogLikesReq, BlogLikesRes>; getBlogLikesList: HandlerWithParams<{ blogId: string }, BlogLikesListReq, BlogLikesListRes>; + getNumOfComments: HandlerWithParams<{ blogId: string }, NumOfCommentsReq, NumOfCommentsRes>; } export class BlogController implements blogController { @@ -51,6 +54,16 @@ export class BlogController implements blogController { constructor(db: DataStoreDao) { this.db = db; } + + getNumOfComments: HandlerWithParams<{ blogId: string }, NumOfCommentsReq, NumOfCommentsRes> = + async (req, res) => { + const blogId = req.params.blogId; + if (!blogId) return res.status(400).send({ error: ERROR.PARAMS_MISSING }); + + const numOfComments = await this.db.getNumOfComments(blogId); + return res.status(200).send({ numOfComments }); + }; + likeBlog: HandlerWithParams<{ blogId: string }, CreateLikeReq, CreateLikeRes> = async ( req, res diff --git a/backend/src/controllers/user.controller.ts b/backend/src/controllers/user.controller.ts index 4f3c8d3..609e31e 100644 --- a/backend/src/controllers/user.controller.ts +++ b/backend/src/controllers/user.controller.ts @@ -1,6 +1,7 @@ import { AllUnReadMsgsReq, AllUnReadMsgsRes, + DefaultSpaceId, ERROR, FollowUserReq, FollowUserRes, @@ -188,7 +189,7 @@ export class UserController implements userController { }; await this.db.createUser(user); - await this.db.addMember({ spaceId: this.db.defaultSpcId, memberId: user.id, isAdmin: false }); + await this.db.addMember({ spaceId: DefaultSpaceId, memberId: user.id, isAdmin: false }); return res.send({ jwt: createToken(user.id), username: user.username, id: user.id }); }; diff --git a/backend/src/dataStore/dao/Blog.dao.ts b/backend/src/dataStore/dao/Blog.dao.ts index 7e68bc5..3b3179e 100644 --- a/backend/src/dataStore/dao/Blog.dao.ts +++ b/backend/src/dataStore/dao/Blog.dao.ts @@ -7,6 +7,7 @@ export interface BlogDao { deleteBlog(blogId: string): Promise; getComments(blogId: string): Promise; + getNumOfComments(blogId: string): Promise; deleteComments(blogId: string): Promise; blogLikes(blogId: string, userId: string): Promise<{ likes: number; isLiked: boolean }>; blogLikesList(blogId: string): Promise; diff --git a/backend/src/dataStore/dao/Space.dao.ts b/backend/src/dataStore/dao/Space.dao.ts index 5ac5e90..9ce35b8 100644 --- a/backend/src/dataStore/dao/Space.dao.ts +++ b/backend/src/dataStore/dao/Space.dao.ts @@ -1,7 +1,6 @@ import { Blog, ChatMessage, LastReadMsg, Space, SpaceMember } from '@nest/shared'; export interface SpaceDao { - defaultSpcId: string; createSpace(space: Space): Promise; updateSpace(space: Space): Promise; getSpace(spaceId: string): Promise; diff --git a/backend/src/dataStore/sql/SqlDataStore.class.ts b/backend/src/dataStore/sql/SqlDataStore.class.ts index 61d7004..45bd894 100644 --- a/backend/src/dataStore/sql/SqlDataStore.class.ts +++ b/backend/src/dataStore/sql/SqlDataStore.class.ts @@ -20,33 +20,37 @@ import { DataStoreDao } from '..'; export class SqlDataStore implements DataStoreDao { private pool!: Pool; - public defaultSpcId = '1'; + private prodProps: mysql.PoolOptions = { + host: process.env.MY_SQL_DB_HOST, + user: process.env.MY_SQL_DB_USER, + database: process.env.MY_SQL_DB_DATABASE, + password: process.env.MY_SQL_DB_PASSWORD, + socketPath: process.env.MY_SQL_DB_SOCKET_PATH, + }; + private devProps: mysql.PoolOptions = { + host: process.env.MY_SQL_DB_HOST, + user: process.env.MY_SQL_DB_USER, + database: process.env.MY_SQL_DB_DATABASE, + password: process.env.MY_SQL_DB_PASSWORD, + }; async runDB() { - const prodProps: mysql.PoolOptions = { - host: process.env.MY_SQL_DB_HOST, - user: process.env.MY_SQL_DB_USER, - database: process.env.MY_SQL_DB_DATABASE, - password: process.env.MY_SQL_DB_PASSWORD, - socketPath: process.env.MY_SQL_DB_SOCKET_PATH, - }; - - const devProps: mysql.PoolOptions = { - host: process.env.MY_SQL_DB_HOST, - user: process.env.MY_SQL_DB_USER, - database: process.env.MY_SQL_DB_DATABASE, - password: process.env.MY_SQL_DB_PASSWORD, - }; - this.pool = mysql .createPool({ - ...(process.env.NODE_ENV === 'prod' ? prodProps : devProps), + ...(process.env.NODE_ENV === 'prod' ? this.prodProps : this.devProps), }) .promise(); - return this; } + async getNumOfComments(blogId: string): Promise { + const query = ` + SELECT COUNT(*) AS comments FROM comments WHERE blogId=? + `; + const [rows] = await this.pool.query(query, blogId); + return rows[0]['comments'] as Promise; + } + async getLastMsgId(spaceId: string): Promise { const query = ` SELECT id FROM chat WHERE spaceId=? ORDER BY timestamp DESC LIMIT 1 @@ -411,13 +415,13 @@ export class SqlDataStore implements DataStoreDao { async getUserCard(userId: string, cardOwnerId: string): Promise { const [rows] = await this.pool.query( ` - SELECT users.id, users.username, users.email, users.timestamp, - (SELECT COUNT(*) FROM follows WHERE follows.followingId = users.id) AS followersNum, - (SELECT COUNT(*) FROM follows WHERE follows.followerId = users.id) AS followingNum, - (SELECT COUNT(*) FROM follows WHERE follows.followerId = ? AND follows.followingId = users.id) AS isFollowing - FROM users - WHERE users.id = ? - `, + SELECT users.id, users.username, users.email, users.timestamp, + (SELECT COUNT(*) FROM follows WHERE follows.followingId = users.id) AS followersNum, + (SELECT COUNT(*) FROM follows WHERE follows.followerId = users.id) AS followingNum, + (SELECT COUNT(*) FROM follows WHERE follows.followerId = ? AND follows.followingId = users.id) AS isFollowing + FROM users + WHERE users.id = ? + `, [userId, cardOwnerId] ); diff --git a/backend/src/server.ts b/backend/src/server.ts index 5415f40..5d6411f 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -63,6 +63,7 @@ import { errorHandler } from './middleware/errorHandler'; app.get(ENDPOINT.GET_BLOG_LIKES_LIST, requireAuth, asyncHandler(blog.getBlogLikesList)); app.post(ENDPOINT.LIKE_BLOG, requireAuth, checkEmptyInput, asyncHandler(blog.likeBlog)); app.delete(ENDPOINT.UNLIKE_BLOG, requireAuth, asyncHandler(blog.unLikeBlog)); + app.get(ENDPOINT.NUM_OF_COMMENTS, requireAuth, asyncHandler(blog.getNumOfComments)); // app.get(ENDPOINT.TEST_INFINITE_SCROLL, requireAuth, asyncHandler(blog.testInfiniteScrollBlogs)); // *Comment diff --git a/frontend/src/components/BlogIcon.tsx b/frontend/src/components/BlogIcon.tsx index 3feddea..24a5065 100644 --- a/frontend/src/components/BlogIcon.tsx +++ b/frontend/src/components/BlogIcon.tsx @@ -1,20 +1,24 @@ -import { Blog } from '@nest/shared'; -import { formatDistanceToNow } from 'date-fns'; +import { Blog, DefaultSpaceId } from '@nest/shared'; +import { LiaCommentSolid } from 'react-icons/lia'; +import { RiGroup2Fill } from 'react-icons/ri'; import { Link } from 'react-router-dom'; -import { isArabic } from '../utils/assists'; +import { useCommCounts } from '../hooks/useBlog'; +import { formatTimeShort, isArabic } from '../utils/assists'; import { LikeBlogButton } from './LikeBlogButton'; import { MyMarkdown } from './MyMarkdown'; export const BlogIcon: React.FC<{ post: Blog }> = ({ post }) => { + const { numOfComments } = useCommCounts(post.id!); + return (
- -
-

{post.title}

-
- +
+ +

{post.title}

+ +
@@ -23,28 +27,23 @@ export const BlogIcon: React.FC<{ post: Blog }> = ({ post }) => { - {post.spaceId !== '1' && ( - - Spaced + + {numOfComments.data?.numOfComments} + + + {post.spaceId !== DefaultSpaceId && ( + + )} +
-
- -
- {/*

{post.content}

*/} +
diff --git a/frontend/src/components/MyMarkdown.tsx b/frontend/src/components/MyMarkdown.tsx index f14d4b0..398faf3 100644 --- a/frontend/src/components/MyMarkdown.tsx +++ b/frontend/src/components/MyMarkdown.tsx @@ -3,28 +3,32 @@ import ReactMarkdown from 'react-markdown'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vs } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { isArabic } from '../utils/assists'; + export const MyMarkdown: FC<{ markdown: string }> = ({ markdown }) => { return ( - - ) : ( - - {children} - - ); - }, - }} - /> +
+ + ) : ( + + {children} + + ); + }, + }} + /> +
); }; diff --git a/frontend/src/components/NotificationMenu.tsx b/frontend/src/components/NotificationMenu.tsx index 9b4d454..c8759be 100644 --- a/frontend/src/components/NotificationMenu.tsx +++ b/frontend/src/components/NotificationMenu.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import { TbMessageCirclePlus } from 'react-icons/tb'; import { Link } from 'react-router-dom'; diff --git a/frontend/src/components/ShortForm.tsx b/frontend/src/components/ShortForm.tsx index c878aa7..3c2fa56 100644 --- a/frontend/src/components/ShortForm.tsx +++ b/frontend/src/components/ShortForm.tsx @@ -5,11 +5,12 @@ import { useParams } from 'react-router-dom'; import { useCreateShort } from '../hooks/useBlog'; import { isArabic } from '../utils/assists'; +import { MyMarkdown } from './MyMarkdown'; export const ShortForm = () => { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); - // const remaining = ShortLength - content.length; + const [preview, setPreview] = useState(false); const id = useParams().id || DefaultSpaceId; const { createShortMutation } = useCreateShort(id, title, content); @@ -29,6 +30,15 @@ export const ShortForm = () => { <> {createShortMutation.isError &&

{createShortMutation.error.message}

}
+
+ + +
+ { value={title} onChange={e => setTitle(e.target.value)} /> -