-
Notifications
You must be signed in to change notification settings - Fork 167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add stories moderation #2928
base: master
Are you sure you want to change the base?
Add stories moderation #2928
Conversation
Pull Request Test Coverage Report for Build 8973003737Details
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, thanks for working on this, my comments are as follows (mainly about the feature design):
export enum StoryStatus { | ||
Draft = 0, | ||
Pending, | ||
Rejected, | ||
Published | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, enum values should be UPPER_SNAKE_CASE (e.g. DRAFT
)
canEdit={isAuthor || hasWritePermissions} | ||
canDelete={isAuthor || hasWritePermissions} | ||
canPin={hasWritePermissions} | ||
isPinned={story.isPinned} | ||
/> | ||
); | ||
}} | ||
/> | ||
)} | ||
|
||
{storyLists.pending.length > 0 && ( | ||
<StoriesTable | ||
title="Pending Stories" | ||
headers={columns} | ||
stories={storyLists.pending | ||
// Filter out the YAML header from the content | ||
.map(story => ({ ...story, content: getYamlHeader(story.content).content })) | ||
.filter( | ||
story => | ||
// Always show pinned stories | ||
story.isPinned || story.authorName.toLowerCase().includes(query.toLowerCase()) | ||
)} | ||
storyActions={story => { | ||
const isAuthor = storiesUserId === story.authorId; | ||
const hasWritePermissions = | ||
storiesRole === StoriesRole.Moderator || storiesRole === StoriesRole.Admin; | ||
return ( | ||
<StoryActions | ||
storyId={story.id} | ||
handleDeleteStory={handleDeleteStory} | ||
handleRejectStory={handleRejectStory} | ||
handlePublishStory={handlePublishStory} | ||
canView // everyone has view permissions, even anonymous users | ||
canEdit={isAuthor || hasWritePermissions} | ||
canDelete={isAuthor || hasWritePermissions} | ||
canModerate={hasWritePermissions} | ||
isPending={true} | ||
/> | ||
); | ||
}} | ||
/> | ||
)} | ||
|
||
{storyLists.rejected.length > 0 && ( | ||
<StoriesTable | ||
title="Rejected Stories" | ||
headers={columns} | ||
stories={storyLists.rejected | ||
// Filter out the YAML header from the content | ||
.map(story => ({ ...story, content: getYamlHeader(story.content).content })) | ||
.filter( | ||
story => | ||
// Always show pinned stories | ||
story.isPinned || story.authorName.toLowerCase().includes(query.toLowerCase()) | ||
)} | ||
storyActions={story => { | ||
const isAuthor = storiesUserId === story.authorId; | ||
const hasWritePermissions = | ||
storiesRole === StoriesRole.Moderator || storiesRole === StoriesRole.Admin; | ||
return ( | ||
<StoryActions | ||
storyId={story.id} | ||
handleDeleteStory={handleDeleteStory} | ||
canView // everyone has view permissions, even anonymous users | ||
canEdit={isAuthor || hasWritePermissions} | ||
canDelete={isAuthor || hasWritePermissions} | ||
/> | ||
); | ||
}} | ||
/> | ||
)} | ||
|
||
{storyLists.draft.length > 0 && ( | ||
<StoriesTable | ||
title="Draft Stories" | ||
headers={columns} | ||
stories={storyLists.draft | ||
// Filter out the YAML header from the content | ||
.map(story => ({ ...story, content: getYamlHeader(story.content).content })) | ||
.filter( | ||
story => | ||
// Always show pinned stories | ||
story.isPinned || story.authorName.toLowerCase().includes(query.toLowerCase()) | ||
)} | ||
storyActions={story => { | ||
const isAuthor = storiesUserId === story.authorId; | ||
const hasWritePermissions = | ||
storiesRole === StoriesRole.Moderator || storiesRole === StoriesRole.Admin; | ||
return ( | ||
<StoryActions | ||
storyId={story.id} | ||
handleDeleteStory={handleDeleteStory} | ||
canView // everyone has view permissions, even anonymous users | ||
canEdit={isAuthor || hasWritePermissions} | ||
canDelete={isAuthor || hasWritePermissions} | ||
/> | ||
); | ||
}} | ||
/> | ||
)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
status === StoryStatus.Draft | ||
? '/draft' | ||
: status === StoryStatus.Pending | ||
? '/pending' | ||
: status === StoryStatus.Rejected | ||
? '/rejected' | ||
: status === StoryStatus.Published | ||
? '/published' | ||
: ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filters are generally done using a query parameter instead of a different route/path (which would conventionally mean a different resource.)
|
||
const draftStories: StoryListView[] = yield call(async () => { | ||
const resp = await getStories(tokens, StoryStatus.Draft); | ||
return resp ?? []; | ||
}); | ||
|
||
const pendingStories: StoryListView[] = yield call(async () => { | ||
const resp = await getStories(tokens, StoryStatus.Pending); | ||
return resp ?? []; | ||
}); | ||
|
||
const rejectedStories: StoryListView[] = yield call(async () => { | ||
const resp = await getStories(tokens, StoryStatus.Rejected); | ||
return resp ?? []; | ||
}); | ||
|
||
const publishedStories: StoryListView[] = yield call(async () => { | ||
const resp = await getStories(tokens, StoryStatus.Published); | ||
return resp ?? []; | ||
}); | ||
|
||
const allStories: StoryListViews = { | ||
draft: draftStories, | ||
pending: pendingStories, | ||
rejected: rejectedStories, | ||
published: publishedStories | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This design doesn't look right to me. To the user/the frontend, the "listing" of stories should be transparent.
In other words, the FE just makes one "list stories" query, and the backend returns a list of stories that the user has access to. The permissions/different types of stories should be transparent and the frontend can subsequently later filter the story by the status. You are adding a status field inside the story model in the backend but it's not being utilised here at all.
…nto stories-moderation
…nto stories-moderation
Description
source-academy/stories-backend#120
Type of change
How to test
Checklist