Skip to content
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

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open

Add stories moderation #2928

wants to merge 18 commits into from

Conversation

keyansheng
Copy link

@keyansheng keyansheng commented Apr 12, 2024

Description

source-academy/stories-backend#120

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update
  • Code quality improvements

How to test

Checklist

  • I have tested this code
  • I have updated the documentation

@keyansheng keyansheng added the Enhancement New feature request label Apr 12, 2024
@coveralls
Copy link

coveralls commented Apr 12, 2024

Pull Request Test Coverage Report for Build 8973003737

Details

  • 1 of 65 (1.54%) changed or added relevant lines in 7 files are covered.
  • 3 unchanged lines in 2 files lost coverage.
  • Overall coverage decreased (-0.1%) to 31.969%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/features/stories/StoriesActions.ts 0 1 0.0%
src/features/stories/StoriesReducer.ts 0 1 0.0%
src/features/stories/storiesComponents/BackendAccess.ts 1 3 33.33%
src/pages/stories/StoryActions.tsx 0 4 0.0%
src/pages/stories/StoriesTable.tsx 0 6 0.0%
src/commons/sagas/StoriesSaga.ts 0 12 0.0%
src/pages/stories/Stories.tsx 0 38 0.0%
Files with Coverage Reduction New Missed Lines %
src/pages/stories/Stories.tsx 1 0.0%
src/commons/sagas/StoriesSaga.ts 2 1.28%
Totals Coverage Status
Change from base Build 8971907944: -0.1%
Covered Lines: 5011
Relevant Lines: 14690

💛 - Coveralls

@RichDom2185 RichDom2185 marked this pull request as ready for review April 12, 2024 10:42
Copy link
Member

@RichDom2185 RichDom2185 left a 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):

Comment on lines +44 to +49
export enum StoryStatus {
Draft = 0,
Pending,
Rejected,
Published
}
Copy link
Member

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)

Comment on lines +168 to +291
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}
/>
);
}}
/>
)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's extensive code duplication here:

image

Comment on lines +83 to +91
status === StoryStatus.Draft
? '/draft'
: status === StoryStatus.Pending
? '/pending'
: status === StoryStatus.Rejected
? '/rejected'
: status === StoryStatus.Published
? '/published'
: '';
Copy link
Member

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.)

Comment on lines +38 to +64

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
};
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature request
Development

Successfully merging this pull request may close these issues.

None yet

5 participants