diff --git a/packages/client/src/comments/CommentActions.ts b/packages/client/src/comments/CommentActions.ts new file mode 100644 index 0000000..5cf62f5 --- /dev/null +++ b/packages/client/src/comments/CommentActions.ts @@ -0,0 +1,102 @@ +import {IHTTPClient} from '../http' +import {IComment, IAPIDef} from '@rondo/common' + +export enum ICommentActionTypes { + VOTE_UP = 'VOTE_UP', + VOTE_DOWN = 'VOTE_DOWN', + COMMENTS_RETRIEVE = 'COMMENTS_RETRIEVE', + COMMENT_ADD = 'COMMENT_ADD', + COMMENT_EDIT = 'COMMENT_EDIT', + COMMENT_REMOVE = 'COMMENT_DELETE', + + SPAM_MARK = 'SPAM_MARK', + SPAM_UNMARK = 'SPAM_UNMARK', +} + +export class CommentActions { + constructor(protected readonly http: IHTTPClient) {} + + voteUp(comment: IComment) { + return { + payload: this.http.post('/comments/:commentId/vote', null, { + commentId: comment.id, + }), + type: ICommentActionTypes.VOTE_UP, + } + } + + voteDown(comment: IComment) { + return { + payload: this.http.delete('/comments/:commentId/vote', null, { + commentId: comment.id, + }), + type: ICommentActionTypes.VOTE_DOWN, + } + } + + getComments(storyId: number) { + return { + payload: this.http.get('/story/:storyId/comments', null, { + storyId, + }), + type: ICommentActionTypes.COMMENTS_RETRIEVE, + } + } + + addComment(comment: IComment) { + if (!comment.parentId) { + // root comment + return { + payload: this.http.post('/story/:storyId/comments', comment, { + storyId: comment.storyId, + }), + type: ICommentActionTypes.COMMENT_ADD, + } + } + + // nested comment + return { + payload: this.http.post('/comments/:parentId', comment, { + parentId: comment.parentId, + }), + type: ICommentActionTypes.COMMENT_ADD, + } + } + + editComment(comment: IComment) { + return { + payload: this.http.put('/comments/:commentId', comment, { + commentId: comment.id, + }), + type: ICommentActionTypes.COMMENT_EDIT, + } + } + + removeComment(comment: IComment) { + return { + payload: this.http.delete('/comments/:commentId', null, { + commentId: comment.id, + }), + type: ICommentActionTypes.COMMENT_REMOVE, + } + } + + markAsSpam(comment: IComment) { + return { + payload: this.http.post('/comments/:commentId/spam', comment, { + commentId: comment.id, + }), + type: ICommentActionTypes.SPAM_MARK, + } + } + + unmarkAsSpam(comment: IComment) { + return { + payload: this.http.delete('/comments/:commentId/spam', null, { + commentId: comment.id, + }), + type: ICommentActionTypes.SPAM_UNMARK, + } + } + +} diff --git a/packages/client/src/comments/Comments.tsx b/packages/client/src/comments/Comments.tsx new file mode 100644 index 0000000..fbaa833 --- /dev/null +++ b/packages/client/src/comments/Comments.tsx @@ -0,0 +1,49 @@ +import React from 'react' +import {IComment} from '@rondo/common' + +export interface ICommentProps { + comment: IComment +} + +export class CommentVote extends React.PureComponent { + render() { + return ( + + ) + } +} + +export class CommentButtons extends React.PureComponent { + render() { + + } +} + +export class Comment extends React.PureComponent { + render() { + const {comment} = this.props + return ( +
+ {comment.score} +

{comment.message}

+ + +
+ ) + } +} + +export interface ICommentsProps { + comments: IComment[] +} + +export class Comments extends React.PureComponent { + render() { + const {comments} = this.props + return ( +
+ {comments.map(comment => )} +
+ ) + } +} diff --git a/packages/client/src/http/index.ts b/packages/client/src/http/index.ts index 57ed147..02772fa 100644 --- a/packages/client/src/http/index.ts +++ b/packages/client/src/http/index.ts @@ -1,2 +1,6 @@ export * from './HTTPClient' +export * from './IHTTPClient' export * from './IHeader' +export * from './IRequest' +export * from './IResponse' +export * from './ITypedRequestParams' diff --git a/packages/common/src/IAPIDef.ts b/packages/common/src/IAPIDef.ts index f3af39e..e31b74a 100644 --- a/packages/common/src/IAPIDef.ts +++ b/packages/common/src/IAPIDef.ts @@ -1,4 +1,5 @@ import {ICredentials} from './ICredentials' +import {IComment} from './IComment' import {IUser} from './IUser' export interface IAPIDef { @@ -15,7 +16,7 @@ export interface IAPIDef { } '/auth/logout': { 'get': {} - }, + } '/users/password': { 'post': { body: { @@ -31,4 +32,65 @@ export interface IAPIDef { } } } + '/story/:storyId/comments': { + 'get': { + response: IComment[], + params: { + storyId: number // TODO might have to change to string (url) + } + } + 'post': { + response: IComment, + params: { + storyId: number + } + } + } + '/comments/:parentId': { + 'post': { + response: IComment, + params: { + parentId: number + }, + body: IComment, + } + } + '/comments/:commentId': { + 'put': { + response: IComment, + body: IComment, + params: { + commentId: number + } + } + 'delete': { + params: { + commentId: number + } + } + } + '/comments/:commentId/vote': { + 'post': { + params: { + commentId: number + } + } + 'delete': { + params: { + commentId: number + } + } + } + '/comments/:commentId/spam': { + 'post': { + params: { + commentId: number + } + } + 'delete': { + params: { + commentId: number + } + } + } } diff --git a/packages/common/src/IComment.ts b/packages/common/src/IComment.ts new file mode 100644 index 0000000..9251630 --- /dev/null +++ b/packages/common/src/IComment.ts @@ -0,0 +1,8 @@ +export interface IComment { + id: number + storyId: number + message: string + score: number + parentId: number + children: IComment[] +} diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 20f1a79..390bcb9 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,4 +1,5 @@ export * from './IAPIDef' +export * from './IComment' export * from './ICredentials' export * from './IRoutes' export * from './IUser' diff --git a/packages/server/src/entities/Comment.ts b/packages/server/src/entities/Comment.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/server/src/entities/Site.ts b/packages/server/src/entities/Site.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/server/src/entities/Story.ts b/packages/server/src/entities/Story.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/server/src/entities/Team.ts b/packages/server/src/entities/Team.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/server/src/entities/TeamUser.ts b/packages/server/src/entities/TeamUser.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/server/src/routes/CommentRoutes.ts b/packages/server/src/routes/CommentRoutes.ts new file mode 100644 index 0000000..4a790e4 --- /dev/null +++ b/packages/server/src/routes/CommentRoutes.ts @@ -0,0 +1,68 @@ +import {AsyncRouter} from '../router' +import {BaseRoute} from './BaseRoute' +import {IAPIDef} from '@rondo/common' +import {ensureLoggedInApi} from '../middleware' + +export class CommentRoutes extends BaseRoute { + constructor( + protected readonly commentService: ICommentService, + protected readonly t: AsyncRouter, + ) { + super(t) + } + + setup(t: AsyncRouter) { + + t.get('/story/:storyId/comments', async req => { + const {storyId} = req.params + // TODO retrieve comments from story + return [] + }) + + t.use(ensureLoggedInApi) + + t.post('/story/:storyId/comments', async req => { + const comment = req.body + // TODO save a comment + return comment + }) + + t.post('/comments/:parentId', async req => { + const comment = req.body + // TODO save a comment + return comment + }) + + t.put('/comments/:commentId', async req => { + const comment = req.body + // TODO edit a comment + return comment + }) + + t.delete('/comments/:commentId', async req => { + const {commentId} = req.params + // TODO delete a comment + }) + + t.post('/comments/:commentId/vote', async req => { + const {commentId} = req.params + // TODO upvote a comment + }) + + t.delete('/comments/:commentId/vote', async req => { + const {commentId} = req.params + // TODO delete a vote + }) + + t.post('/comments/:commentId/spam', async req => { + const {commentId} = req.params + // TODO report comment as spam + }) + + t.delete('/comments/:commentId/spam', async req => { + const {commentId} = req.params + // TODO delete spam report + }) + + } +} diff --git a/packages/server/src/routes/LoginRoutes.ts b/packages/server/src/routes/LoginRoutes.ts index 6b1256c..a80ef44 100644 --- a/packages/server/src/routes/LoginRoutes.ts +++ b/packages/server/src/routes/LoginRoutes.ts @@ -33,6 +33,7 @@ export class LoginRoutes extends BaseRoute { } await req.logInPromise(user) res.redirect(req.baseUrl) + // TODO return user }) t.get('/auth/logout', async (req, res) => { diff --git a/packages/server/src/services/CommentService.ts b/packages/server/src/services/CommentService.ts new file mode 100644 index 0000000..249b399 --- /dev/null +++ b/packages/server/src/services/CommentService.ts @@ -0,0 +1,34 @@ +import {BaseService} from './BaseService' +import {IComment} from '@rondo/common' +import {ICommentService} from'./ICommentService' + +export class CommentService extends BaseService implements ICommentService { + + async find(storyId: number) { + } + + async saveRoot(comment: IComment, userId: number) { + } + + async save(comment: IComment, userId: number) { + } + + async edit(comment: IComment, userId: number) { + } + + async delete(comment: IComment, userId: number) { + } + + async upVote(commentId: number, userId: number) { + } + + async deleteVote(commentId: number, userId: number) { + } + + async markAsSpam(commentId: number, userId: number) { + } + + async unmarkAsSpam(commentId: number, userId: number) { + } + +} diff --git a/packages/server/src/services/ICommentService.ts b/packages/server/src/services/ICommentService.ts new file mode 100644 index 0000000..3655b3c --- /dev/null +++ b/packages/server/src/services/ICommentService.ts @@ -0,0 +1,21 @@ +import {IComment} from '@rondo/common' + +export interface ICommentService { + find(storyId: number): Promise + + saveRoot(comment: IComment, userId: number): Promise + + save(comment: IComment, userId: number): Promise + + edit(comment: IComment, userId: number): Promise + + delete(comment: IComment, userId: number): Promise + + upVote(commentId: number, userId: number): Promise + + deleteVote(commentId: number, userId: number): Promise + + markAsSpam(commentId: number, userId: number): Promise + + unmarkAsSpam(commentId: number, userId: number): Promise +}