From 5d0df31ce281f5d80338e3cf6a983c004cb64bd6 Mon Sep 17 00:00:00 2001 From: xaralis <filip.varecha@fragaria.cz> Date: Fri, 18 Dec 2020 10:08:16 +0100 Subject: [PATCH] feat: announcement delete, post actions (ban user, hide post, announce/accept/reject proposal) --- package.json | 2 +- src/actions/posts.js | 117 +++++++++++++++++ src/actions/program.js | 2 +- src/actions/users.js | 10 ++ src/api.js | 2 +- src/containers/AnnoucementsContainer.jsx | 23 ++-- src/containers/PostsContainer.jsx | 153 ++++++++++++++++++----- src/hooks.js | 18 +++ src/stores.js | 11 ++ typings/cf2021.d.ts | 1 + 10 files changed, 292 insertions(+), 47 deletions(-) create mode 100644 src/actions/users.js create mode 100644 src/hooks.js diff --git a/package.json b/package.json index a0a7779..1c41852 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "^@?\\w" ], [ - "^(actions|components|containers|pages|utils|stores|keycloak)(/.*|$)" + "^(api|actions|config|hooks|components|containers|pages|utils|stores|keycloak)(/.*|$)" ], [ "^(test-utils)(/.*|$)" diff --git a/src/actions/posts.js b/src/actions/posts.js index 6d15bd7..017c74e 100644 --- a/src/actions/posts.js +++ b/src/actions/posts.js @@ -204,3 +204,120 @@ export const addProposal = createAsyncAction( }, } ); + +/** + * Hide existing post. + */ +export const hide = createAsyncAction( + /** + * @param {CF2021.Post} post + */ + async (post) => { + return successResult(post); + }, + { + postActionHook: ({ result }) => { + if (!result.error) { + PostStore.update((state) => { + state.items[result.payload.id].hidden = true; + }); + } + }, + } +); + +/** + * Announce procedure proposal. + */ +export const announceProposal = createAsyncAction( + /** + * @param {CF2021.ProposalPost} proposal + */ + async (proposal) => { + return successResult(proposal); + }, + { + shortCircuitHook: ({ args }) => { + if (args.type !== "procedure-proposal") { + return errorResult(); + } + + if (args.state !== "pending") { + return errorResult(); + } + + return false; + }, + postActionHook: ({ result }) => { + if (!result.error) { + PostStore.update((state) => { + state.items[result.payload.id].state = "announced"; + }); + } + }, + } +); + +/** + * Announce procedure proposal. + */ +export const acceptProposal = createAsyncAction( + /** + * @param {CF2021.ProposalPost} proposal + */ + async (proposal) => { + return successResult(proposal); + }, + { + shortCircuitHook: ({ args }) => { + if (args.type !== "procedure-proposal") { + return errorResult(); + } + + if (args.state !== "announced") { + return errorResult(); + } + + return false; + }, + postActionHook: ({ result }) => { + if (!result.error) { + PostStore.update((state) => { + state.items[result.payload.id].state = "accepted"; + }); + } + }, + } +); + +/** + * Reject procedure proposal. + */ +export const rejectProposal = createAsyncAction( + /** + * @param {CF2021.ProposalPost} proposal + */ + async (proposal) => { + return successResult(proposal); + }, + { + shortCircuitHook: ({ args }) => { + if (args.type !== "procedure-proposal") { + return errorResult(); + } + + if (args.state !== "announced") { + return errorResult(); + } + + return false; + }, + postActionHook: ({ result }) => { + if (!result.error) { + PostStore.update((state) => { + state.items[result.payload.id].state = "rejected"; + }); + } + }, + } +); diff --git a/src/actions/program.js b/src/actions/program.js index 7a70b07..f1d0fa3 100644 --- a/src/actions/program.js +++ b/src/actions/program.js @@ -1,7 +1,7 @@ -import { fetch } from "api"; import pick from "lodash/pick"; import { createAsyncAction, errorResult, successResult } from "pullstate"; +import { fetch } from "api"; import { ProgramStore } from "stores"; export const loadProgram = createAsyncAction( diff --git a/src/actions/users.js b/src/actions/users.js new file mode 100644 index 0000000..7e3d1ae --- /dev/null +++ b/src/actions/users.js @@ -0,0 +1,10 @@ +import { createAsyncAction, successResult } from "pullstate"; + +export const ban = createAsyncAction( + /** + * @param {number} userId + */ + async (userId) => { + return successResult(userId); + } +); diff --git a/src/api.js b/src/api.js index b5674b2..707c7c2 100644 --- a/src/api.js +++ b/src/api.js @@ -9,7 +9,7 @@ export const fetch = (url, opts) => { opts.headers = opts.headers || {}; if (isAuthenticated) { - // opts.headers.Authorization = "Bearer " + user.accessToken; + opts.headers.Authorization = "Bearer " + user.accessToken; } return baseFetch(process.env.REACT_APP_API_BASE_URL + url, opts); diff --git a/src/containers/AnnoucementsContainer.jsx b/src/containers/AnnoucementsContainer.jsx index 3ea32c8..daeafc6 100644 --- a/src/containers/AnnoucementsContainer.jsx +++ b/src/containers/AnnoucementsContainer.jsx @@ -7,11 +7,19 @@ import { import AnnouncementEditModal from "components/annoucements/AnnouncementEditModal"; import AnnouncementList from "components/annoucements/AnnouncementList"; import ModalConfirm from "components/modals/ModalConfirm"; +import { useModalConfirmControl } from "hooks"; import { AnnouncementStore } from "stores"; const AnnoucementsContainer = () => { - const [itemToDelete, setItemToDelete] = useState(null); const [itemToEdit, setItemToEdit] = useState(null); + + const [ + itemToDelete, + setItemToDelete, + onDeleteConfirm, + onDeleteCancel, + ] = useModalConfirmControl(deleteAnnouncement); + const items = AnnouncementStore.useState((state) => state.items); const confirmEdit = useCallback( @@ -28,15 +36,6 @@ const AnnoucementsContainer = () => { setItemToEdit(null); }, [setItemToEdit]); - const confirmDelete = useCallback(async () => { - await deleteAnnouncement.run(itemToDelete); - setItemToDelete(null); - }, [setItemToDelete, itemToDelete]); - - const cancelDelete = useCallback(() => { - setItemToDelete(null); - }, [setItemToDelete]); - return ( <> <AnnouncementList @@ -47,8 +46,8 @@ const AnnoucementsContainer = () => { /> <ModalConfirm isOpen={!!itemToDelete} - onConfirm={confirmDelete} - onCancel={cancelDelete} + onConfirm={onDeleteConfirm} + onCancel={onDeleteCancel} title="Opravdu chcete toto oznámení smazat?" yesActionLabel="Smazat" > diff --git a/src/containers/PostsContainer.jsx b/src/containers/PostsContainer.jsx index f1e7fea..0b512ec 100644 --- a/src/containers/PostsContainer.jsx +++ b/src/containers/PostsContainer.jsx @@ -1,11 +1,54 @@ import React from "react"; import pick from "lodash/pick"; -import { dislike, like, removeDislike, removeLike } from "actions/posts"; +import { + acceptProposal, + announceProposal, + dislike, + hide, + like, + rejectProposal, + removeDislike, + removeLike, +} from "actions/posts"; +import { ban } from "actions/users"; +import ModalConfirm from "components/modals/ModalConfirm"; import PostList from "components/posts/PostList"; +import { useModalConfirmControl } from "hooks"; import { PostStore } from "stores"; const PostsContainer = ({ className }) => { + const [ + userToBan, + setUserToBan, + onBanUserConfirm, + onBanUserCancel, + ] = useModalConfirmControl(ban); + const [ + postToHide, + setPostToHide, + onPostHideConfirm, + onPostHideCancel, + ] = useModalConfirmControl(hide); + const [ + postToAnnounce, + setPostToAnnounce, + onAnnounceConfirm, + onAnnounceCancel, + ] = useModalConfirmControl(announceProposal); + const [ + postToAccept, + setPostToAccept, + onAcceptConfirm, + onAcceptCancel, + ] = useModalConfirmControl(acceptProposal); + const [ + postToReject, + setPostToReject, + onRejectConfirm, + onRejectCancel, + ] = useModalConfirmControl(rejectProposal); + const { window, items } = PostStore.useState((state) => pick(state, ["window", "items"]) ); @@ -13,9 +56,6 @@ const PostsContainer = ({ className }) => { (state) => state.filters.flags === "archived" ); - // const onLike = (post) => like.run(); - // const onDislike = (post) => console.log("dislike", post); - /** * * @param {CF2021.Post} post @@ -45,38 +85,87 @@ const PostsContainer = ({ className }) => { const sliceStart = (window.page - 1) * window.perPage; const sliceEnd = window.page * window.perPage; - const onHide = (post) => { - console.log("hide", post); - }; + /** + * Ban a post's author. + * @param {CF2021.Post} post + */ const onBanUser = (post) => { - console.log("banUser", post); - }; - const onAnnounceProcedureProposal = (post) => { - console.log("announce", post); - }; - const onAcceptProcedureProposal = (post) => { - console.log("accept", post); - }; - const onRejectProcedureProposal = (post) => { - console.log("reject", post); + setUserToBan(post.author); }; return ( - <PostList - items={window.items - .slice(sliceStart, sliceEnd) - .map((postId) => items[postId])} - onLike={onLike} - onDislike={onDislike} - className={className} - dimArchived={!showingArchivedOnly} - displayActions={true} - onHide={onHide} - onBanUser={onBanUser} - onAnnounceProcedureProposal={onAnnounceProcedureProposal} - onAcceptProcedureProposal={onAcceptProcedureProposal} - onRejectProcedureProposal={onRejectProcedureProposal} - /> + <> + <PostList + items={window.items + .slice(sliceStart, sliceEnd) + .map((postId) => items[postId])} + onLike={onLike} + onDislike={onDislike} + className={className} + dimArchived={!showingArchivedOnly} + displayActions={true} + onHide={setPostToHide} + onBanUser={onBanUser} + onAnnounceProcedureProposal={setPostToAnnounce} + onAcceptProcedureProposal={setPostToAccept} + onRejectProcedureProposal={setPostToReject} + /> + <ModalConfirm + isOpen={!!userToBan} + onConfirm={onBanUserConfirm} + onCancel={onBanUserCancel} + title={`Zablokovat uživatele ${userToBan ? userToBan.name : null}?`} + yesActionLabel="Zablokovat" + > + Uživatel <strong>{userToBan ? userToBan.name : null}</strong> bude + zablokován a nebude dále moci vkládat nové příspěvky. Opravdu to chcete? + </ModalConfirm> + <ModalConfirm + isOpen={!!postToHide} + onConfirm={onPostHideConfirm} + onCancel={onPostHideCancel} + title="Skrýt příspěvek?" + yesActionLabel="Potvrdit" + > + Příspěvek se skryje a uživatelé ho neuvidí. Opravdu to chcete? + </ModalConfirm> + <ModalConfirm + isOpen={!!postToHide} + onConfirm={onPostHideConfirm} + onCancel={onPostHideCancel} + title="Skrýt příspěvek?" + yesActionLabel="Potvrdit" + > + Příspěvek se skryje a uživatelé ho neuvidí. Opravdu to chcete? + </ModalConfirm> + <ModalConfirm + isOpen={!!postToAnnounce} + onConfirm={onAnnounceConfirm} + onCancel={onAnnounceCancel} + title="Vyhlásit procedurální návrh?" + yesActionLabel="Vyhlásit návrh" + > + Procedurální návrh bude <strong>vyhlášen</strong>. Opravdu to chcete? + </ModalConfirm> + <ModalConfirm + isOpen={!!postToAccept} + onConfirm={onAcceptConfirm} + onCancel={onAcceptCancel} + title="Schválit procedurální návrh?" + yesActionLabel="Schválit návrh" + > + Procedurální návrh bude <strong>schválen</strong>. Opravdu to chcete? + </ModalConfirm> + <ModalConfirm + isOpen={!!postToReject} + onConfirm={onRejectConfirm} + onCancel={onRejectCancel} + title="Zamítnout procedurální návrh?" + yesActionLabel="Zamítnout návrh" + > + Procedurální návrh bude <strong>zamítnut</strong>. Opravdu to chcete? + </ModalConfirm> + </> ); }; diff --git a/src/hooks.js b/src/hooks.js new file mode 100644 index 0000000..6942ed6 --- /dev/null +++ b/src/hooks.js @@ -0,0 +1,18 @@ +import { useCallback, useState } from "react"; + +export const useModalConfirmControl = (actionFn) => { + const [item, setItem] = useState(null); + + const onActionConfirm = useCallback(() => { + if (item) { + actionFn.run(item); + setItem(null); + } + }, [item, setItem, actionFn]); + + const onActionCancel = useCallback(() => { + setItem(null); + }, [setItem]); + + return [item, setItem, onActionConfirm, onActionCancel]; +}; diff --git a/src/stores.js b/src/stores.js index aaf7353..599d26c 100644 --- a/src/stores.js +++ b/src/stores.js @@ -84,6 +84,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "cf", }, @@ -103,6 +104,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "cf", }, @@ -122,6 +124,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "cf", }, @@ -141,6 +144,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, @@ -160,6 +164,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, @@ -179,6 +184,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, @@ -198,6 +204,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, @@ -218,6 +225,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, @@ -246,6 +254,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, @@ -266,6 +275,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, @@ -286,6 +296,7 @@ const allPosts = [ "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", datetime: new Date(), author: { + id: 1, name: "John Doe", group: "KS Pardubický kraj", }, diff --git a/typings/cf2021.d.ts b/typings/cf2021.d.ts index 539ebe8..35ff79b 100644 --- a/typings/cf2021.d.ts +++ b/typings/cf2021.d.ts @@ -69,6 +69,7 @@ declare namespace CF2021 { id: string; datetime: Date; author: { + id: number; name: string; group: string; }; -- GitLab