Add initial comments setup
This commit is contained in:
parent
6ee5077c88
commit
10e5775bf4
102
packages/client/src/comments/CommentActions.ts
Normal file
102
packages/client/src/comments/CommentActions.ts
Normal file
@ -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<IAPIDef>) {}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
49
packages/client/src/comments/Comments.tsx
Normal file
49
packages/client/src/comments/Comments.tsx
Normal file
@ -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<ICommentProps> {
|
||||||
|
render() {
|
||||||
|
const {comment} = this.props
|
||||||
|
return (
|
||||||
|
<div className='comment'>
|
||||||
|
<span className='score'>{comment.score}</span>
|
||||||
|
<p>{comment.message}</p>
|
||||||
|
|
||||||
|
<Comments comments={comment.children} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICommentsProps {
|
||||||
|
comments: IComment[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Comments extends React.PureComponent<ICommentsProps> {
|
||||||
|
render() {
|
||||||
|
const {comments} = this.props
|
||||||
|
return (
|
||||||
|
<div className='comments'>
|
||||||
|
{comments.map(comment => <Comment comment={comment} />)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,2 +1,6 @@
|
|||||||
export * from './HTTPClient'
|
export * from './HTTPClient'
|
||||||
|
export * from './IHTTPClient'
|
||||||
export * from './IHeader'
|
export * from './IHeader'
|
||||||
|
export * from './IRequest'
|
||||||
|
export * from './IResponse'
|
||||||
|
export * from './ITypedRequestParams'
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {ICredentials} from './ICredentials'
|
import {ICredentials} from './ICredentials'
|
||||||
|
import {IComment} from './IComment'
|
||||||
import {IUser} from './IUser'
|
import {IUser} from './IUser'
|
||||||
|
|
||||||
export interface IAPIDef {
|
export interface IAPIDef {
|
||||||
@ -15,7 +16,7 @@ export interface IAPIDef {
|
|||||||
}
|
}
|
||||||
'/auth/logout': {
|
'/auth/logout': {
|
||||||
'get': {}
|
'get': {}
|
||||||
},
|
}
|
||||||
'/users/password': {
|
'/users/password': {
|
||||||
'post': {
|
'post': {
|
||||||
body: {
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
packages/common/src/IComment.ts
Normal file
8
packages/common/src/IComment.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface IComment {
|
||||||
|
id: number
|
||||||
|
storyId: number
|
||||||
|
message: string
|
||||||
|
score: number
|
||||||
|
parentId: number
|
||||||
|
children: IComment[]
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
export * from './IAPIDef'
|
export * from './IAPIDef'
|
||||||
|
export * from './IComment'
|
||||||
export * from './ICredentials'
|
export * from './ICredentials'
|
||||||
export * from './IRoutes'
|
export * from './IRoutes'
|
||||||
export * from './IUser'
|
export * from './IUser'
|
||||||
|
|||||||
0
packages/server/src/entities/Comment.ts
Normal file
0
packages/server/src/entities/Comment.ts
Normal file
0
packages/server/src/entities/Site.ts
Normal file
0
packages/server/src/entities/Site.ts
Normal file
0
packages/server/src/entities/Story.ts
Normal file
0
packages/server/src/entities/Story.ts
Normal file
0
packages/server/src/entities/Team.ts
Normal file
0
packages/server/src/entities/Team.ts
Normal file
0
packages/server/src/entities/TeamUser.ts
Normal file
0
packages/server/src/entities/TeamUser.ts
Normal file
68
packages/server/src/routes/CommentRoutes.ts
Normal file
68
packages/server/src/routes/CommentRoutes.ts
Normal file
@ -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<IAPIDef> {
|
||||||
|
constructor(
|
||||||
|
protected readonly commentService: ICommentService,
|
||||||
|
protected readonly t: AsyncRouter<IAPIDef>,
|
||||||
|
) {
|
||||||
|
super(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(t: AsyncRouter<IAPIDef>) {
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,6 +33,7 @@ export class LoginRoutes extends BaseRoute<IAPIDef> {
|
|||||||
}
|
}
|
||||||
await req.logInPromise(user)
|
await req.logInPromise(user)
|
||||||
res.redirect(req.baseUrl)
|
res.redirect(req.baseUrl)
|
||||||
|
// TODO return user
|
||||||
})
|
})
|
||||||
|
|
||||||
t.get('/auth/logout', async (req, res) => {
|
t.get('/auth/logout', async (req, res) => {
|
||||||
|
|||||||
34
packages/server/src/services/CommentService.ts
Normal file
34
packages/server/src/services/CommentService.ts
Normal file
@ -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) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
21
packages/server/src/services/ICommentService.ts
Normal file
21
packages/server/src/services/ICommentService.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {IComment} from '@rondo/common'
|
||||||
|
|
||||||
|
export interface ICommentService {
|
||||||
|
find(storyId: number): Promise<IComment>
|
||||||
|
|
||||||
|
saveRoot(comment: IComment, userId: number): Promise<IComment>
|
||||||
|
|
||||||
|
save(comment: IComment, userId: number): Promise<IComment>
|
||||||
|
|
||||||
|
edit(comment: IComment, userId: number): Promise<IComment>
|
||||||
|
|
||||||
|
delete(comment: IComment, userId: number): Promise<IComment>
|
||||||
|
|
||||||
|
upVote(commentId: number, userId: number): Promise<void>
|
||||||
|
|
||||||
|
deleteVote(commentId: number, userId: number): Promise<void>
|
||||||
|
|
||||||
|
markAsSpam(commentId: number, userId: number): Promise<void>
|
||||||
|
|
||||||
|
unmarkAsSpam(commentId: number, userId: number): Promise<void>
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user