From b21c22639673db3a1bc35e0dffbf88b1c604eb31 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 21 Jan 2025 15:13:37 +0100 Subject: [PATCH] Change how the modal stack is handled --- app/javascript/mastodon/actions/modal.ts | 1 - .../features/alt_text_modal/index.tsx | 17 +++------- .../features/ui/components/modal_root.jsx | 31 ++++++++----------- .../features/ui/containers/modal_container.js | 7 ++--- app/javascript/mastodon/reducers/modal.ts | 30 +++--------------- 5 files changed, 23 insertions(+), 63 deletions(-) diff --git a/app/javascript/mastodon/actions/modal.ts b/app/javascript/mastodon/actions/modal.ts index 49af176a11..ab03e46765 100644 --- a/app/javascript/mastodon/actions/modal.ts +++ b/app/javascript/mastodon/actions/modal.ts @@ -9,7 +9,6 @@ export type ModalType = keyof typeof MODAL_COMPONENTS; interface OpenModalPayload { modalType: ModalType; modalProps: ModalProps; - previousModalProps?: ModalProps; } export const openModal = createAction('MODAL_OPEN'); diff --git a/app/javascript/mastodon/features/alt_text_modal/index.tsx b/app/javascript/mastodon/features/alt_text_modal/index.tsx index 88ffb7c477..18d17b75ac 100644 --- a/app/javascript/mastodon/features/alt_text_modal/index.tsx +++ b/app/javascript/mastodon/features/alt_text_modal/index.tsx @@ -262,11 +262,6 @@ const Preview: React.FC<{ } }; -interface RestoreProps { - previousDescription: string; - previousPosition: FocalPoint; -} - interface Props { mediaId: string; onClose: () => void; @@ -275,15 +270,14 @@ interface Props { interface ConfirmationMessage { message: string; confirm: string; - props?: RestoreProps; } export interface ModalRef { getCloseConfirmationMessage: () => null | ConfirmationMessage; } -export const AltTextModal = forwardRef>( - ({ mediaId, previousDescription, previousPosition, onClose }, ref) => { +export const AltTextModal = forwardRef( + ({ mediaId, onClose }, ref) => { const intl = useIntl(); const dispatch = useAppDispatch(); const media = useAppSelector((state) => @@ -302,18 +296,15 @@ export const AltTextModal = forwardRef>( const focusY = (media?.getIn(['meta', 'focus', 'y'], 0) as number | undefined) ?? 0; const [description, setDescription] = useState( - previousDescription ?? (media?.get('description') as string | undefined) ?? '', ); const [position, setPosition] = useState( - previousPosition ?? [focusX / 2 + 0.5, focusY / -2 + 0.5], + [focusX / 2 + 0.5, focusY / -2 + 0.5], ); const [isDetecting, setIsDetecting] = useState(false); const [isSaving, setIsSaving] = useState(false); - const dirtyRef = useRef( - previousDescription || previousPosition ? true : false, - ); + const dirtyRef = useRef(false); const type = media?.get('type') as string; const valid = length(description) <= MAX_LENGTH; diff --git a/app/javascript/mastodon/features/ui/components/modal_root.jsx b/app/javascript/mastodon/features/ui/components/modal_root.jsx index 5e44b53fbd..5228d84463 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.jsx +++ b/app/javascript/mastodon/features/ui/components/modal_root.jsx @@ -3,6 +3,8 @@ import { PureComponent } from 'react'; import { Helmet } from 'react-helmet'; +import ImmutablePropTypes from 'react-immutable-proptypes'; + import Base from 'mastodon/components/modal_root'; import { AltTextModal } from 'mastodon/features/alt_text_modal'; import { @@ -78,8 +80,7 @@ export const MODAL_COMPONENTS = { export default class ModalRoot extends PureComponent { static propTypes = { - type: PropTypes.string, - props: PropTypes.object, + modals: ImmutablePropTypes.list.isRequired, onClose: PropTypes.func.isRequired, ignoreFocus: PropTypes.bool, }; @@ -89,7 +90,7 @@ export default class ModalRoot extends PureComponent { }; getSnapshotBeforeUpdate () { - return { visible: !!this.props.type }; + return { visible: !!this.props.modals.get([0, 'type']) }; } componentDidUpdate (prevProps, prevState, { visible }) { @@ -129,25 +130,19 @@ export default class ModalRoot extends PureComponent { }; render () { - const { type, props, ignoreFocus } = this.props; + const { modals, ignoreFocus } = this.props; const { backgroundColor } = this.state; - const visible = !!type; + const visible = !modals.isEmpty(); return ( - {visible && ( - <> - - {(SpecificComponent) => { - return ; - }} - - - - - - - )} + {visible && modals.toArray().map((modal, index) => ( + + {(SpecificComponent) => { + return ; + }} + + ))} ); } diff --git a/app/javascript/mastodon/features/ui/containers/modal_container.js b/app/javascript/mastodon/features/ui/containers/modal_container.js index fe87380431..4ff3918e47 100644 --- a/app/javascript/mastodon/features/ui/containers/modal_container.js +++ b/app/javascript/mastodon/features/ui/containers/modal_container.js @@ -1,14 +1,12 @@ +import { List as ImmutableList } from 'immutable'; import { connect } from 'react-redux'; import { openModal, closeModal } from '../../../actions/modal'; import ModalRoot from '../components/modal_root'; -const defaultProps = {}; - const mapStateToProps = state => ({ ignoreFocus: state.getIn(['modal', 'ignoreFocus']), - type: state.getIn(['modal', 'stack', 0, 'modalType'], null), - props: state.getIn(['modal', 'stack', 0, 'modalProps'], defaultProps), + modals: state.getIn(['modal', 'stack'], ImmutableList()), }); const mapDispatchToProps = dispatch => ({ @@ -16,7 +14,6 @@ const mapDispatchToProps = dispatch => ({ if (confirmationMessage) { dispatch( openModal({ - previousModalProps: confirmationMessage.props, modalType: 'CONFIRM', modalProps: { message: confirmationMessage.message, diff --git a/app/javascript/mastodon/reducers/modal.ts b/app/javascript/mastodon/reducers/modal.ts index e287626ff2..eca598d533 100644 --- a/app/javascript/mastodon/reducers/modal.ts +++ b/app/javascript/mastodon/reducers/modal.ts @@ -5,16 +5,19 @@ import { timelineDelete } from 'mastodon/actions/timelines_typed'; import type { ModalType } from '../actions/modal'; import { openModal, closeModal } from '../actions/modal'; +import { uuid } from '../uuid'; export type ModalProps = Record; interface Modal { modalType: ModalType; modalProps: ModalProps; + modalKey: string; } const Modal = ImmutableRecord({ modalType: 'ACTIONS', modalProps: ImmutableRecord({})(), + modalKey: '', }); interface ModalState { @@ -52,35 +55,11 @@ const pushModal = ( state: State, modalType: ModalType, modalProps: ModalProps, - previousModalProps?: ModalProps, ): State => { return state.withMutations((record) => { record.set('ignoreFocus', false); record.update('stack', (stack) => { - let tmp = stack; - - // With this option, we update the previously opened modal, so that when the - // current (new) modal is closed, the previous modal is re-opened with different - // props. Specifically, this is useful for the confirmation modal. - if (previousModalProps) { - const previousModal = tmp.first() as Modal | undefined; - - if (previousModal) { - tmp = tmp.shift().unshift( - Modal({ - modalType: previousModal.modalType, - modalProps: { - ...previousModal.modalProps, - ...previousModalProps, - }, - }), - ); - } - } - - tmp = tmp.unshift(Modal({ modalType, modalProps })); - - return tmp; + return stack.unshift(Modal({ modalType, modalProps, modalKey: uuid() })); }); }); }; @@ -91,7 +70,6 @@ export const modalReducer: Reducer = (state = initialState, action) => { state, action.payload.modalType, action.payload.modalProps, - action.payload.previousModalProps, ); else if (closeModal.match(action)) return popModal(state, action.payload); // TODO: type those actions