From 4c7491d81d492d92edeeff2dfb750e6f32f9d0fc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 9 Aug 2022 17:45:01 -0500 Subject: [PATCH] Strip down StatusContainer, offload actions into Status component itself --- app/soapbox/actions/compose.ts | 17 ++++ app/soapbox/actions/statuses.ts | 9 ++ app/soapbox/components/status.tsx | 94 ++++++++----------- app/soapbox/components/status_action_bar.tsx | 5 +- app/soapbox/containers/status_container.js | Bin 8596 -> 0 bytes app/soapbox/containers/status_container.tsx | 37 ++++++++ 6 files changed, 102 insertions(+), 60 deletions(-) delete mode 100644 app/soapbox/containers/status_container.js create mode 100644 app/soapbox/containers/status_container.tsx diff --git a/app/soapbox/actions/compose.ts b/app/soapbox/actions/compose.ts index ad8439307d..f33a9fa181 100644 --- a/app/soapbox/actions/compose.ts +++ b/app/soapbox/actions/compose.ts @@ -96,6 +96,8 @@ const messages = defineMessages({ uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' }, uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, view: { id: 'snackbar.view', defaultMessage: 'View' }, + replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, + replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, }); const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1); @@ -144,6 +146,20 @@ const replyCompose = (status: Status) => dispatch(openModal('COMPOSE')); }; +const replyComposeWithConfirmation = (status: Status, intl: IntlShape) => + (dispatch: AppDispatch, getState: () => RootState) => { + const state = getState(); + if (state.compose.text.trim().length !== 0) { + dispatch(openModal('CONFIRM', { + message: intl.formatMessage(messages.replyMessage), + confirm: intl.formatMessage(messages.replyConfirm), + onConfirm: () => dispatch(replyCompose(status)), + })); + } else { + dispatch(replyCompose(status)); + } + }; + const cancelReplyCompose = () => ({ type: COMPOSE_REPLY_CANCEL, }); @@ -739,6 +755,7 @@ export { setComposeToStatus, changeCompose, replyCompose, + replyComposeWithConfirmation, cancelReplyCompose, quoteCompose, cancelQuoteCompose, diff --git a/app/soapbox/actions/statuses.ts b/app/soapbox/actions/statuses.ts index b81dd7ce29..db15e7a216 100644 --- a/app/soapbox/actions/statuses.ts +++ b/app/soapbox/actions/statuses.ts @@ -297,6 +297,14 @@ const revealStatus = (ids: string[] | string) => { }; }; +const toggleStatusHidden = (status: Status) => { + if (status.hidden) { + return revealStatus(status.id); + } else { + return hideStatus(status.id); + } +}; + export { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS, @@ -336,4 +344,5 @@ export { toggleMuteStatus, hideStatus, revealStatus, + toggleStatusHidden, }; diff --git a/app/soapbox/components/status.tsx b/app/soapbox/components/status.tsx index b4546336ed..4c62766c8b 100644 --- a/app/soapbox/components/status.tsx +++ b/app/soapbox/components/status.tsx @@ -4,9 +4,14 @@ import { HotKeys } from 'react-hotkeys'; import { useIntl, FormattedMessage, defineMessages } from 'react-intl'; import { NavLink, useHistory } from 'react-router-dom'; +import { mentionCompose, replyComposeWithConfirmation } from 'soapbox/actions/compose'; +import { toggleFavourite, toggleReblog } from 'soapbox/actions/interactions'; +import { openModal } from 'soapbox/actions/modals'; +import { toggleStatusHidden } from 'soapbox/actions/statuses'; import Icon from 'soapbox/components/icon'; import AccountContainer from 'soapbox/containers/account_container'; import QuotedStatus from 'soapbox/features/status/containers/quoted_status_container'; +import { useAppDispatch, useSettings } from 'soapbox/hooks'; import { defaultMediaVisibility, textForScreenReader, getActualStatus } from 'soapbox/utils/status'; import StatusMedia from './status-media'; @@ -15,10 +20,9 @@ import StatusActionBar from './status_action_bar'; import StatusContent from './status_content'; import { HStack, Text } from './ui'; -import type { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import type { Map as ImmutableMap } from 'immutable'; import type { Account as AccountEntity, - Attachment as AttachmentEntity, Status as StatusEntity, } from 'soapbox/types/entities'; @@ -29,44 +33,18 @@ const messages = defineMessages({ reblogged_by: { id: 'status.reblogged_by', defaultMessage: '{name} reposted' }, }); -interface IStatus { +export interface IStatus { id?: string, - contextType?: string, status: StatusEntity, - account: AccountEntity, - otherAccounts: ImmutableList, - onClick: () => void, - onReply: (status: StatusEntity) => void, - onFavourite: (status: StatusEntity) => void, - onReblog: (status: StatusEntity, e?: KeyboardEvent) => void, - onQuote: (status: StatusEntity) => void, - onDelete: (status: StatusEntity) => void, - onEdit: (status: StatusEntity) => void, - onDirect: (status: StatusEntity) => void, - onChat: (status: StatusEntity) => void, - onMention: (account: StatusEntity['account']) => void, - onPin: (status: StatusEntity) => void, - onOpenMedia: (media: ImmutableList, index: number) => void, - onOpenVideo: (media: ImmutableMap | AttachmentEntity, startTime: number) => void, - onOpenAudio: (media: ImmutableMap, startTime: number) => void, - onBlock: (status: StatusEntity) => void, - onEmbed: (status: StatusEntity) => void, - onHeightChange: (status: StatusEntity) => void, - onToggleHidden: (status: StatusEntity) => void, - onShowHoverProfileCard: (status: StatusEntity) => void, - muted: boolean, - hidden: boolean, - unread: boolean, - onMoveUp: (statusId: string, featured?: boolean) => void, - onMoveDown: (statusId: string, featured?: boolean) => void, - getScrollPosition?: () => ScrollPosition | undefined, - updateScrollBottom?: (bottom: number) => void, - group: ImmutableMap, - displayMedia: string, - allowedEmoji: ImmutableList, - focusable: boolean, + onClick?: () => void, + muted?: boolean, + hidden?: boolean, + unread?: boolean, + onMoveUp?: (statusId: string, featured?: boolean) => void, + onMoveDown?: (statusId: string, featured?: boolean) => void, + group?: ImmutableMap, + focusable?: boolean, featured?: boolean, - withDismiss?: boolean, hideActionBar?: boolean, hoverable?: boolean, } @@ -76,15 +54,7 @@ const Status: React.FC = (props) => { status, focusable = true, hoverable = true, - onToggleHidden, - displayMedia, - onOpenMedia, - onOpenVideo, onClick, - onReply, - onFavourite, - onReblog, - onMention, onMoveUp, onMoveDown, muted, @@ -94,10 +64,12 @@ const Status: React.FC = (props) => { group, hideActionBar, } = props; - const intl = useIntl(); const history = useHistory(); + const dispatch = useAppDispatch(); + const settings = useSettings(); + const displayMedia = settings.get('displayMedia') as string; const didShowCard = useRef(false); const node = useRef(null); @@ -127,7 +99,7 @@ const Status: React.FC = (props) => { }; const handleExpandedToggle = (): void => { - onToggleHidden(actualStatus); + dispatch(toggleStatusHidden(actualStatus)); }; const handleHotkeyOpenMedia = (e?: KeyboardEvent): void => { @@ -138,29 +110,35 @@ const Status: React.FC = (props) => { if (firstAttachment) { if (firstAttachment.type === 'video') { - onOpenVideo(firstAttachment, 0); + dispatch(openModal('VIDEO', { media: firstAttachment, time: 0 })); } else { - onOpenMedia(status.media_attachments, 0); + dispatch(openModal('MEDIA', { media: status.media_attachments, index: 0 })); } } }; const handleHotkeyReply = (e?: KeyboardEvent): void => { e?.preventDefault(); - onReply(actualStatus); + dispatch(replyComposeWithConfirmation(actualStatus, intl)); }; const handleHotkeyFavourite = (): void => { - onFavourite(actualStatus); + toggleFavourite(actualStatus); }; const handleHotkeyBoost = (e?: KeyboardEvent): void => { - onReblog(actualStatus, e); + const modalReblog = () => dispatch(toggleReblog(actualStatus)); + const boostModal = settings.get('boostModal'); + if ((e && e.shiftKey) || !boostModal) { + modalReblog(); + } else { + dispatch(openModal('BOOST', { status: actualStatus, onReblog: modalReblog })); + } }; const handleHotkeyMention = (e?: KeyboardEvent): void => { e?.preventDefault(); - onMention(actualStatus.account); + dispatch(mentionCompose(actualStatus.account as AccountEntity)); }; const handleHotkeyOpen = (): void => { @@ -172,15 +150,19 @@ const Status: React.FC = (props) => { }; const handleHotkeyMoveUp = (e?: KeyboardEvent): void => { - onMoveUp(status.id, featured); + if (onMoveUp) { + onMoveUp(status.id, featured); + } }; const handleHotkeyMoveDown = (e?: KeyboardEvent): void => { - onMoveDown(status.id, featured); + if (onMoveDown) { + onMoveDown(status.id, featured); + } }; const handleHotkeyToggleHidden = (): void => { - onToggleHidden(actualStatus); + dispatch(toggleStatusHidden(actualStatus)); }; const handleHotkeyToggleSensitive = (): void => { diff --git a/app/soapbox/components/status_action_bar.tsx b/app/soapbox/components/status_action_bar.tsx index 412303417e..d8c27e3a3b 100644 --- a/app/soapbox/components/status_action_bar.tsx +++ b/app/soapbox/components/status_action_bar.tsx @@ -155,14 +155,11 @@ const StatusActionBar: React.FC = ({ status, withDismiss = fal dispatch(toggleBookmark(status)); }; - const modalReblog = () => { - dispatch(toggleReblog(status)); - }; - const handleReblogClick: React.EventHandler = e => { e.stopPropagation(); if (me) { + const modalReblog = () => dispatch(toggleReblog(status)); const boostModal = settings.get('boostModal'); if ((e && e.shiftKey) || !boostModal) { modalReblog(); diff --git a/app/soapbox/containers/status_container.js b/app/soapbox/containers/status_container.js deleted file mode 100644 index 987774aa78316c48abe222e4aafde27817c8c4f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8596 zcmeHNOLN;c5Wf3YAeWGuQRJSLIEiCBt*7IKg7bE zSEih@kE-5qV?^PW*)EmxN&-B3_Q3t+%($J7`0lJf*xIqN=uL;&4XPHmUh)| zR3U1PrldBkgt;a=PgJ#(t5J3*uI2PisTN<47Yo%?(e2qRc+6?3<+NU_yW>*S1~%fX zu2mg0ri@6CvK=s1i*5OEqF|4;n8NpssL+|@Ng-?4s^r0+jWQy9_Zyw)S&+CSARu@F z`DDbE*r;1kIPXy5s#eXG9#iP22LmE4RnK&Y)Cy%R`K@YdX{-lLRSUn+RIQ~OrM_-> z{S|qdD*d*V6+N^c1`L1-fLHzq zK9g6rK_Ih5H}1n72ZS-ona#{vYOD2~QwxAx^Ci^24atZ< zj_Bzav6p~0R$yEFez&~%|H3cEs{(9zWsP9cykj2$IS@FsTf&%Yb|=e{-3V5~x2az# zB~U@I%vvzuS;>l*Hd=vP)!Q|%L@{&zSs>pZJWH}KbRY0A2=TI5y?--+WS=~MlJX(Q zvw_&3u0H-x=WBm^LDf^6i{|dxT5B%U606i5#Tkx|`VP0uZiHjCY0V+47foFQaFh>> z4k2KjvC+KW)@mU{;nUr2#&J~;+>WF>?U?=j1=8sYA@ESS*{y@5Cvi6ByGg_*z_AOy zMH~~K)X%lrYJjPH!j9fDL??h8KrjSzLb{_i^5rPWQ*HWk5IDt@frnciWO{nnb%4HZ zqmo-bl8w!QY3j{EcwSzX z>P{4A8})~rGiN6;q)VP*_ZoD0j^RkVd)f1<7Tl=0*-dzqbVK$kU4A@ z!nK6v6ORF==!n0veTDU+fuQ0pF(#MHJs~rx`B;e}n|Klm2`b5bbK-?Sq*2I@iBgLY zXm3+a!n(xhbK)}Dp(%$ra8BBu z5fVQ$;@)JRCo@yaO+J~GqFR|XdvSEczKbD1Nx`qsHRajKR=@M90`Bv4TXOJ2Q7yE+e*jqtd*Tp27BxS8Z3V@1)wF?hI&)N24A@jhae{=l;)8# zOPs}G$OU`(l8KpK%cc2IJWSYMf3X*lKH7yowS4i>OhV8u4CDyjBuVt)^78rKfAGNfAe7*C=rU)*&R(E}3vzptncUJ$LY4>am(uX*pnCaV9HevHI8dGzn05SQ~acQ7)7#8K=Oxv$c8O%Uoyf`2& z4!}KeT1k$rC;WnjxSnM|ZhOod2z!z)alDBya?o?bZyfp;;(kD20-qOWr{~AE8B2L$ z=M{s1(tm?lqVm#>+zdfmBK)`W)3eJ|II@h%K5h#64`d(zdV2mW+4jx8mr#f;hPliD zpia*V_cVfO$d|;oA!s958v(K#UUeZVz|))Fm5eNoI@3*>W%$WXXSR8i(E#K9W5qXO zm$6%3HbNggJ~DhT_nuNhf`bO|bhl%NZ)1f9412ycjwS0^GdjhwUNEu}Mmb$G-)K{- zYW4R0quYek;N6=;x7;;-A1XQ<^~|d7gWWjLS%zb}!0}ZVD>^m>Fl-TELBZ>X!IP$K zL6})3fUBzOC?SrO=xj+^t*0!CEp)#e%yXM1{4v>wsIz&vRi~{>>Rxh%(_!E;JI5)d zPk+P?JC1V!fs-+VF8lMuXH~3=({Kg8PhIPp{NJf+<@>-srA?oGE9t4r(U$>ND6>g^ za#KI<*S@R$5v^&2^Gv({2M{fDAmwSmEbi?Et-BnACdvP2L=GU1|LKt9wPk1AKZS{Y NcOpH>$t&ExzX9wfyNUn+ diff --git a/app/soapbox/containers/status_container.tsx b/app/soapbox/containers/status_container.tsx new file mode 100644 index 0000000000..e5ac5014d1 --- /dev/null +++ b/app/soapbox/containers/status_container.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +import Status, { IStatus } from 'soapbox/components/status'; +import { useAppSelector } from 'soapbox/hooks'; +import { makeGetStatus } from 'soapbox/selectors'; + +interface IStatusContainer extends Omit { + id: string, + /** @deprecated Unused. */ + contextType?: any, + /** @deprecated Unused. */ + otherAccounts?: any, + /** @deprecated Unused. */ + withDismiss?: any, + /** @deprecated Unused. */ + getScrollPosition?: any, + /** @deprecated Unused. */ + updateScrollBottom?: any, +} + +const getStatus = makeGetStatus(); + +/** + * Legacy Status wrapper accepting a status ID instead of the full entity. + * @deprecated Use the Status component directly. + */ +const StatusContainer: React.FC = ({ id }) => { + const status = useAppSelector(state => getStatus(state, { id })); + + if (status) { + return ; + } else { + return null; + } +}; + +export default StatusContainer;