diff --git a/src/containers/AnnoucementsContainer.jsx b/src/containers/AnnoucementsContainer.jsx index 2e3de91c9c5dda03da08556937aa8c69c5bc3d95..a5aa7aafaa38631bd31cf3c12edf375788db2f00 100644 --- a/src/containers/AnnoucementsContainer.jsx +++ b/src/containers/AnnoucementsContainer.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React from "react"; import { deleteAnnouncement, @@ -11,13 +11,10 @@ import AnnouncementList from "components/annoucements/AnnouncementList"; import { CardBody } from "components/cards"; import ErrorMessage from "components/ErrorMessage"; import ModalConfirm from "components/modals/ModalConfirm"; -import { useActionState, useItemActionConfirm } from "hooks"; +import { useItemActionConfirm } from "hooks"; import { AnnouncementStore, AuthStore } from "stores"; const AnnoucementsContainer = ({ className }) => { - const [itemToEdit, setItemToEdit] = useState(null); - const [confirmingEdit, setConfirmingEdit] = useState(false); - const [editError, setEditError] = useState(null); const { 2: loadResult } = loadAnnouncements.useWatch(); const [ @@ -25,45 +22,25 @@ const AnnoucementsContainer = ({ className }) => { setItemToDelete, onDeleteConfirm, onDeleteCancel, + deleteState, ] = useItemActionConfirm(deleteAnnouncement); - const [deletingAnnouncement, deletingAnnouncementError] = useActionState( - deleteAnnouncement, - itemToDelete - ); + const [ + itemToEdit, + setItemToEdit, + onEditConfirm, + onEditCancel, + editState, + ] = useItemActionConfirm(updateAnnouncement, (item, payload) => ({ + item, + payload, + })); const { isAuthenticated, user } = AuthStore.useState(); const items = AnnouncementStore.useState((state) => state.itemIds.map((id) => state.items[id]) ); - const confirmEdit = useCallback( - async (payload) => { - if (itemToEdit && payload) { - setConfirmingEdit(true); - - const result = await updateAnnouncement.run({ - item: itemToEdit, - payload, - }); - - if (!result.error) { - setItemToEdit(null); - setEditError(null); - } else { - setEditError(result.message); - } - - setConfirmingEdit(false); - } - }, - [itemToEdit, setItemToEdit] - ); - - const cancelEdit = useCallback(() => { - setItemToEdit(null); - }, [setItemToEdit]); - return ( <div className={className}> {loadResult && loadResult.error && ( @@ -84,8 +61,8 @@ const AnnoucementsContainer = ({ className }) => { isOpen={!!itemToDelete} onConfirm={onDeleteConfirm} onCancel={onDeleteCancel} - confirming={deletingAnnouncement} - error={deletingAnnouncementError} + confirming={deleteState.loading} + error={deleteState.error} title="Opravdu smazat?" yesActionLabel="Smazat" > @@ -95,10 +72,10 @@ const AnnoucementsContainer = ({ className }) => { <AnnouncementEditModal isOpen={true} announcement={itemToEdit} - onConfirm={confirmEdit} - onCancel={cancelEdit} - confirming={confirmingEdit} - error={editError} + onConfirm={onEditConfirm} + onCancel={onEditCancel} + confirming={editState.loading} + error={editState.error} /> )} </div> diff --git a/src/containers/PostsContainer.jsx b/src/containers/PostsContainer.jsx index 5daa03d0479e6388d69f5460919c7b4a920602d8..01b902e28855d5ab8f75c08e9744f5bf10071331 100644 --- a/src/containers/PostsContainer.jsx +++ b/src/containers/PostsContainer.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React from "react"; import pick from "lodash/pick"; import { @@ -25,46 +25,58 @@ import { useActionState, useItemActionConfirm } from "hooks"; import { AuthStore, PostStore } from "stores"; const PostsContainer = ({ className, showAddPostCta }) => { - const [postToEdit, setPostToEdit] = useState(null); - const [confirmingEdit, setConfirmingEdit] = useState(false); - const [editError, setEditError] = useState(null); - const [ userToBan, setUserToBan, onBanUserConfirm, onBanUserCancel, + banUserState, ] = useItemActionConfirm(ban); const [ userToUnban, setUserToUnban, onUnbanUserConfirm, onUnbanUserCancel, + unbanUserState, ] = useItemActionConfirm(unban); const [ postToHide, setPostToHide, onPostHideConfirm, onPostHideCancel, + postHideState, ] = useItemActionConfirm(hide); const [ postToArchive, setPostToArchive, onPostArchiveConfirm, onPostArchiveCancel, + postArchiveState, ] = useItemActionConfirm(archive); const [ postToAnnounce, setPostToAnnounce, onAnnounceConfirm, onAnnounceCancel, + announceState, ] = useItemActionConfirm(announceProposal); const [ postToAccept, setPostToAccept, onAcceptConfirm, onAcceptCancel, + acceptState, ] = useItemActionConfirm(acceptProposal); + const [ + postToEdit, + setPostToEdit, + onEditConfirm, + onEditCancel, + editState, + ] = useItemActionConfirm(edit, (item, newContent) => ({ + post: item, + newContent, + })); const [ postToReject, setPostToReject, @@ -92,24 +104,6 @@ const PostsContainer = ({ className, showAddPostCta }) => { (state) => state.filters.flags === "archived" ); - const [banningUser, banningUserError] = useActionState(ban, userToBan); - const [unbanningUser, unbanningUserError] = useActionState( - unban, - userToUnban - ); - const [hidingPost, hidingPostError] = useActionState(hide, postToHide); - const [archivingPost, archivingPostError] = useActionState( - archive, - postToArchive - ); - const [announcingProposal, announcingProposalError] = useActionState( - announceProposal, - postToAnnounce - ); - const [acceptingProposal, acceptingProposalError] = useActionState( - acceptProposal, - postToAccept - ); const [rejectingProposal, rejectingProposalError] = useActionState( rejectProposal, { @@ -138,30 +132,6 @@ const PostsContainer = ({ className, showAddPostCta }) => { const { 2: loadResult } = loadPosts.useWatch(); - const confirmEdit = useCallback( - async (newContent) => { - if (postToEdit && newContent) { - setConfirmingEdit(true); - - const result = await edit.run({ post: postToEdit, newContent }); - - if (!result.error) { - setPostToEdit(null); - setEditError(null); - } else { - setEditError(result.message); - } - - setConfirmingEdit(false); - } - }, - [postToEdit, setPostToEdit, setConfirmingEdit] - ); - - const cancelEdit = useCallback(() => { - setPostToEdit(null); - }, [setPostToEdit]); - /** * Ban a post's author. * @param {CF2021.Post} post @@ -212,8 +182,8 @@ const PostsContainer = ({ className, showAddPostCta }) => { isOpen={!!userToBan} onConfirm={onBanUserConfirm} onCancel={onBanUserCancel} - confirming={banningUser} - error={banningUserError} + confirming={banUserState.loading} + error={banUserState.error} title={`Zablokovat uživatele ${userToBan ? userToBan.name : null}?`} yesActionLabel="Zablokovat" > @@ -224,8 +194,8 @@ const PostsContainer = ({ className, showAddPostCta }) => { isOpen={!!userToUnban} onConfirm={onUnbanUserConfirm} onCancel={onUnbanUserCancel} - confirming={unbanningUser} - error={unbanningUserError} + confirming={unbanUserState.loading} + error={unbanUserState.error} title={`Odblokovat uživatele ${userToUnban ? userToUnban.name : null}?`} yesActionLabel="Odblokovat" > @@ -237,8 +207,8 @@ const PostsContainer = ({ className, showAddPostCta }) => { isOpen={!!postToHide} onConfirm={onPostHideConfirm} onCancel={onPostHideCancel} - confirming={hidingPost} - error={hidingPostError} + confirming={postHideState.loading} + error={postHideState.error} title="Skrýt příspěvek?" yesActionLabel="Potvrdit" > @@ -248,8 +218,8 @@ const PostsContainer = ({ className, showAddPostCta }) => { isOpen={!!postToArchive} onConfirm={onPostArchiveConfirm} onCancel={onPostArchiveCancel} - confirming={archivingPost} - error={archivingPostError} + confirming={postArchiveState.loading} + error={postArchiveState.error} title="Archivovat příspěvek?" yesActionLabel="Potvrdit" > @@ -260,8 +230,8 @@ const PostsContainer = ({ className, showAddPostCta }) => { isOpen={!!postToAnnounce} onConfirm={onAnnounceConfirm} onCancel={onAnnounceCancel} - confirming={announcingProposal} - error={announcingProposalError} + confirming={announceState.loading} + error={announceState.errror} title="Vyhlásit procedurální návrh?" yesActionLabel="Vyhlásit návrh" > @@ -271,8 +241,8 @@ const PostsContainer = ({ className, showAddPostCta }) => { isOpen={!!postToAccept} onConfirm={onAcceptConfirm} onCancel={onAcceptCancel} - confirming={acceptingProposal} - error={acceptingProposalError} + confirming={acceptState.loading} + error={acceptState.error} title="Schválit procedurální návrh?" yesActionLabel="Schválit návrh" > @@ -364,10 +334,10 @@ const PostsContainer = ({ className, showAddPostCta }) => { <PostEditModal isOpen={true} post={postToEdit} - onConfirm={confirmEdit} - onCancel={cancelEdit} - confirming={confirmingEdit} - error={editError} + onConfirm={onEditConfirm} + onCancel={onEditCancel} + confirming={editState.loading} + error={editState.error} /> )} </> diff --git a/src/hooks.js b/src/hooks.js index 648d410a509a226d729052f04bd2296cc194e38e..fcb59e27570bd86cc37817fc2b9b05c2b1b83ef1 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -6,27 +6,34 @@ const baseActionParamsBuilder = (item, args) => { export const useItemActionConfirm = (actionFn, actionParamsBuilder = null) => { const [item, setItem] = useState(null); + const [actionArgs, setActionArgs] = useState(null); const onActionConfirm = useCallback( async (args) => { if (item) { - const result = await actionFn.run( - (actionParamsBuilder || baseActionParamsBuilder)(item, args) + const newActionArgs = (actionParamsBuilder || baseActionParamsBuilder)( + item, + args ); + setActionArgs(newActionArgs); + const result = await actionFn.run(newActionArgs); if (!result.error) { setItem(null); } } }, - [item, setItem, actionFn, actionParamsBuilder] + [item, setItem, actionFn, actionParamsBuilder, setActionArgs] ); const onActionCancel = useCallback(() => { setItem(null); }, [setItem]); - return [item, setItem, onActionConfirm, onActionCancel]; + const [loading, error] = useActionState(actionFn, actionArgs); + const unwrappedActionState = { loading, error }; + + return [item, setItem, onActionConfirm, onActionCancel, unwrappedActionState]; }; export const useActionConfirm = (actionFn, actionArgs) => { diff --git a/src/ws/handlers/announcements.js b/src/ws/handlers/announcements.js index 1519be00007b4db4244f971dca0d443b0e7b0a03..460fcef7631e2d25429eafba1e5992b696b4e14c 100644 --- a/src/ws/handlers/announcements.js +++ b/src/ws/handlers/announcements.js @@ -1,5 +1,6 @@ import has from "lodash/has"; +import { markdownConverter } from "markdown"; import { AnnouncementStore } from "stores"; import { parseRawAnnouncement, syncAnnoucementItemIds } from "utils"; @@ -8,6 +9,9 @@ export const handleAnnouncementChanged = (payload) => { if (state.items[payload.id]) { if (has(payload, "content")) { state.items[payload.id].content = payload.content; + state.items[payload.id].contentHtml = markdownConverter.makeHtml( + payload.content + ); } if (has(payload, "link")) { state.items[payload.id].link = payload.link;