2023-09-22 16:41:50 +02:00
|
|
|
import type { Reducer } from '@reduxjs/toolkit';
|
2024-01-08 11:57:40 +01:00
|
|
|
import { Record as ImmutableRecord, Stack } from 'immutable';
|
2023-05-25 22:42:37 +09:00
|
|
|
|
2024-06-20 13:56:52 +02:00
|
|
|
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
|
|
|
|
2023-05-25 22:42:37 +09:00
|
|
|
import type { ModalType } from '../actions/modal';
|
|
|
|
import { openModal, closeModal } from '../actions/modal';
|
|
|
|
|
2023-09-22 16:41:50 +02:00
|
|
|
export type ModalProps = Record<string, unknown>;
|
2023-05-25 22:42:37 +09:00
|
|
|
interface Modal {
|
|
|
|
modalType: ModalType;
|
|
|
|
modalProps: ModalProps;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Modal = ImmutableRecord<Modal>({
|
|
|
|
modalType: 'ACTIONS',
|
|
|
|
modalProps: ImmutableRecord({})(),
|
|
|
|
});
|
|
|
|
|
|
|
|
interface ModalState {
|
|
|
|
ignoreFocus: boolean;
|
|
|
|
stack: Stack<ImmutableRecord<Modal>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
const initialState = ImmutableRecord<ModalState>({
|
|
|
|
ignoreFocus: false,
|
|
|
|
stack: Stack(),
|
|
|
|
})();
|
|
|
|
type State = typeof initialState;
|
|
|
|
|
|
|
|
interface PopModalOption {
|
|
|
|
modalType: ModalType | undefined;
|
|
|
|
ignoreFocus: boolean;
|
|
|
|
}
|
|
|
|
const popModal = (
|
|
|
|
state: State,
|
2023-07-13 11:26:45 +02:00
|
|
|
{ modalType, ignoreFocus }: PopModalOption,
|
2023-05-25 22:42:37 +09:00
|
|
|
): State => {
|
|
|
|
if (
|
|
|
|
modalType === undefined ||
|
|
|
|
modalType === state.get('stack').get(0)?.get('modalType')
|
|
|
|
) {
|
|
|
|
return state
|
|
|
|
.set('ignoreFocus', !!ignoreFocus)
|
|
|
|
.update('stack', (stack) => stack.shift());
|
|
|
|
} else {
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const pushModal = (
|
|
|
|
state: State,
|
|
|
|
modalType: ModalType,
|
2023-07-13 11:26:45 +02:00
|
|
|
modalProps: ModalProps,
|
2025-01-21 12:34:22 +01:00
|
|
|
previousModalProps?: ModalProps,
|
2023-05-25 22:42:37 +09:00
|
|
|
): State => {
|
|
|
|
return state.withMutations((record) => {
|
|
|
|
record.set('ignoreFocus', false);
|
2025-01-21 12:34:22 +01:00
|
|
|
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;
|
|
|
|
});
|
2023-05-25 22:42:37 +09:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-09-22 16:41:50 +02:00
|
|
|
export const modalReducer: Reducer<State> = (state = initialState, action) => {
|
|
|
|
if (openModal.match(action))
|
|
|
|
return pushModal(
|
|
|
|
state,
|
|
|
|
action.payload.modalType,
|
|
|
|
action.payload.modalProps,
|
2025-01-21 12:34:22 +01:00
|
|
|
action.payload.previousModalProps,
|
2023-09-22 16:41:50 +02:00
|
|
|
);
|
|
|
|
else if (closeModal.match(action)) return popModal(state, action.payload);
|
|
|
|
// TODO: type those actions
|
2024-06-20 13:56:52 +02:00
|
|
|
else if (timelineDelete.match(action))
|
2023-09-22 16:41:50 +02:00
|
|
|
return state.update('stack', (stack) =>
|
|
|
|
stack.filterNot(
|
2024-06-20 13:56:52 +02:00
|
|
|
(modal) => modal.get('modalProps').statusId === action.payload.statusId,
|
2023-09-22 16:41:50 +02:00
|
|
|
),
|
|
|
|
);
|
|
|
|
else return state;
|
|
|
|
};
|