~cytrogen/masto-fe

382b2a506a14cef96ed57d4c120b4b9e11eae4b5 — fusagiko / takayamaki 2 years ago 6a59208
[Glitch] Rewrite actions/modal and reducers/modal with typescript

Port 38c6216082e67581e83d04b3096cdb020ad0edea to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
47 files changed, 556 insertions(+), 283 deletions(-)

M app/javascript/flavours/glitch/actions/blocks.js
M app/javascript/flavours/glitch/actions/boosts.js
M app/javascript/flavours/glitch/actions/compose.js
M app/javascript/flavours/glitch/actions/filters.js
M app/javascript/flavours/glitch/actions/local_settings.js
D app/javascript/flavours/glitch/actions/modal.js
A app/javascript/flavours/glitch/actions/modal.ts
M app/javascript/flavours/glitch/actions/mutes.js
M app/javascript/flavours/glitch/actions/onboarding.js
M app/javascript/flavours/glitch/actions/reports.js
M app/javascript/flavours/glitch/components/edited_timestamp/index.jsx
M app/javascript/flavours/glitch/containers/account_container.jsx
M app/javascript/flavours/glitch/containers/domain_container.jsx
M app/javascript/flavours/glitch/containers/dropdown_menu_container.js
M app/javascript/flavours/glitch/containers/notification_purge_buttons_container.js
M app/javascript/flavours/glitch/containers/status_container.js
M app/javascript/flavours/glitch/features/account_gallery/index.jsx
M app/javascript/flavours/glitch/features/account_timeline/containers/header_container.jsx
M app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js
M app/javascript/flavours/glitch/features/compose/containers/dropdown_container.js
M app/javascript/flavours/glitch/features/compose/containers/header_container.js
M app/javascript/flavours/glitch/features/compose/containers/navigation_container.js
M app/javascript/flavours/glitch/features/compose/containers/options_container.js
M app/javascript/flavours/glitch/features/compose/containers/privacy_dropdown_container.js
M app/javascript/flavours/glitch/features/direct_timeline/containers/conversation_container.js
M app/javascript/flavours/glitch/features/directory/components/account_card.jsx
M app/javascript/flavours/glitch/features/getting_started/index.jsx
M app/javascript/flavours/glitch/features/getting_started_misc/index.jsx
M app/javascript/flavours/glitch/features/interaction_modal/index.jsx
M app/javascript/flavours/glitch/features/list_timeline/index.jsx
M app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js
M app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx
M app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
M app/javascript/flavours/glitch/features/status/index.jsx
M app/javascript/flavours/glitch/features/ui/components/block_modal.jsx
M app/javascript/flavours/glitch/features/ui/components/compare_history_modal.jsx
M app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.jsx
M app/javascript/flavours/glitch/features/ui/components/header.jsx
M app/javascript/flavours/glitch/features/ui/components/link_footer.jsx
M app/javascript/flavours/glitch/features/ui/components/modal_root.jsx
M app/javascript/flavours/glitch/features/ui/components/mute_modal.jsx
M app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx
M app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js
M app/javascript/flavours/glitch/features/ui/containers/modal_container.js
M app/javascript/flavours/glitch/reducers/index.ts
D app/javascript/flavours/glitch/reducers/modal.js
A app/javascript/flavours/glitch/reducers/modal.ts
M app/javascript/flavours/glitch/actions/blocks.js => app/javascript/flavours/glitch/actions/blocks.js +1 -1
@@ 95,6 95,6 @@ export function initBlockModal(account) {
      account,
    });

    dispatch(openModal('BLOCK'));
    dispatch(openModal({ modalType: 'BLOCK' }));
  };
}

M app/javascript/flavours/glitch/actions/boosts.js => app/javascript/flavours/glitch/actions/boosts.js +4 -1
@@ 14,7 14,10 @@ export function initBoostModal(props) {
      privacy,
    });

    dispatch(openModal('BOOST', props));
    dispatch(openModal({
      modalType: 'BOOST',
      modalProps: props,
    }));
  };
}


M app/javascript/flavours/glitch/actions/compose.js => app/javascript/flavours/glitch/actions/compose.js +4 -1
@@ 416,7 416,10 @@ export function initMediaEditModal(id) {
      id,
    });

    dispatch(openModal('FOCAL_POINT', { id }));
    dispatch(openModal({
      modalType: 'FOCAL_POINT',
      modalProps: { id },
    }));
  };
}


M app/javascript/flavours/glitch/actions/filters.js => app/javascript/flavours/glitch/actions/filters.js +6 -3
@@ 15,9 15,12 @@ export const FILTERS_CREATE_SUCCESS = 'FILTERS_CREATE_SUCCESS';
export const FILTERS_CREATE_FAIL    = 'FILTERS_CREATE_FAIL';

export const initAddFilter = (status, { contextType }) => dispatch =>
  dispatch(openModal('FILTER', {
    statusId: status?.get('id'),
    contextType: contextType,
  dispatch(openModal({
    modalType: 'FILTER',
    modalProps: {
      statusId: status?.get('id'),
      contextType: contextType,
    },
  }));

export const fetchFilters = () => (dispatch, getState) => {

M app/javascript/flavours/glitch/actions/local_settings.js => app/javascript/flavours/glitch/actions/local_settings.js +6 -3
@@ 28,9 28,12 @@ export function checkDeprecatedLocalSettings() {
    }

    if (changed_settings.length > 0) {
      dispatch(openModal('DEPRECATED_SETTINGS', {
        settings: changed_settings,
        onConfirm: () => dispatch(clearDeprecatedLocalSettings()),
      dispatch(openModal({
        modalType: 'DEPRECATED_SETTINGS',
        modalProps: {
          settings: changed_settings,
          onConfirm: () => dispatch(clearDeprecatedLocalSettings()),
        },
      }));
    }
  };

D app/javascript/flavours/glitch/actions/modal.js => app/javascript/flavours/glitch/actions/modal.js +0 -18
@@ 1,18 0,0 @@
export const MODAL_OPEN  = 'MODAL_OPEN';
export const MODAL_CLOSE = 'MODAL_CLOSE';

export function openModal(type, props) {
  return {
    type: MODAL_OPEN,
    modalType: type,
    modalProps: props,
  };
}

export function closeModal(type, options = { ignoreFocus: false }) {
  return {
    type: MODAL_CLOSE,
    modalType: type,
    ignoreFocus: options.ignoreFocus,
  };
}

A app/javascript/flavours/glitch/actions/modal.ts => app/javascript/flavours/glitch/actions/modal.ts +17 -0
@@ 0,0 1,17 @@
import { createAction } from '@reduxjs/toolkit';

import type { MODAL_COMPONENTS } from '../features/ui/components/modal_root';

export type ModalType = keyof typeof MODAL_COMPONENTS;

interface OpenModalPayload {
  modalType: ModalType;
  modalProps: unknown;
}
export const openModal = createAction<OpenModalPayload>('MODAL_OPEN');

interface CloseModalPayload {
  modalType: ModalType | undefined;
  ignoreFocus: boolean;
}
export const closeModal = createAction<CloseModalPayload>('MODAL_CLOSE');

M app/javascript/flavours/glitch/actions/mutes.js => app/javascript/flavours/glitch/actions/mutes.js +1 -1
@@ 98,7 98,7 @@ export function initMuteModal(account) {
      account,
    });

    dispatch(openModal('MUTE'));
    dispatch(openModal({ modalType: 'MUTE' }));
  };
}


M app/javascript/flavours/glitch/actions/onboarding.js => app/javascript/flavours/glitch/actions/onboarding.js +3 -1
@@ 6,7 6,9 @@ export function showOnboardingOnce() {
    const alreadySeen = getState().getIn(['settings', 'onboarded']);

    if (!alreadySeen) {
      dispatch(openModal('ONBOARDING'));
      dispatch(openModal({
        modalType: 'ONBOARDING',
      }));
      dispatch(changeSetting(['onboarded'], true));
      dispatch(saveSettings());
    }

M app/javascript/flavours/glitch/actions/reports.js => app/javascript/flavours/glitch/actions/reports.js +6 -3
@@ 7,9 7,12 @@ export const REPORT_SUBMIT_SUCCESS = 'REPORT_SUBMIT_SUCCESS';
export const REPORT_SUBMIT_FAIL    = 'REPORT_SUBMIT_FAIL';

export const initReport = (account, status) => dispatch =>
  dispatch(openModal('REPORT', {
    accountId: account.get('id'),
    statusId: status?.get('id'),
  dispatch(openModal({
    modalType: 'REPORT',
    modalProps: {
      accountId: account.get('id'),
      statusId: status?.get('id'),
    },
  }));

export const submitReport = (params, onSuccess, onFail) => (dispatch, getState) => {

M app/javascript/flavours/glitch/components/edited_timestamp/index.jsx => app/javascript/flavours/glitch/components/edited_timestamp/index.jsx +4 -1
@@ 15,7 15,10 @@ import DropdownMenu from './containers/dropdown_menu_container';
const mapDispatchToProps = (dispatch, { statusId }) => ({

  onItemClick (index) {
    dispatch(openModal('COMPARE_HISTORY', { index, statusId }));
    dispatch(openModal({
      modalType: 'COMPARE_HISTORY',
      modalProps: { index, statusId },
    }));
  },

});

M app/javascript/flavours/glitch/containers/account_container.jsx => app/javascript/flavours/glitch/containers/account_container.jsx +7 -4
@@ 35,10 35,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  onFollow (account) {
    if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
      if (unfollowModal) {
        dispatch(openModal('CONFIRM', {
          message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
          confirm: intl.formatMessage(messages.unfollowConfirm),
          onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
            confirm: intl.formatMessage(messages.unfollowConfirm),
            onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
          },
        }));
      } else {
        dispatch(unfollowAccount(account.get('id')));

M app/javascript/flavours/glitch/containers/domain_container.jsx => app/javascript/flavours/glitch/containers/domain_container.jsx +7 -4
@@ 18,10 18,13 @@ const makeMapStateToProps = () => {

const mapDispatchToProps = (dispatch, { intl }) => ({
  onBlockDomain (domain) {
    dispatch(openModal('CONFIRM', {
      message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
      confirm: intl.formatMessage(messages.blockDomainConfirm),
      onConfirm: () => dispatch(blockDomain(domain)),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
        confirm: intl.formatMessage(messages.blockDomainConfirm),
        onConfirm: () => dispatch(blockDomain(domain)),
      },
    }));
  },


M app/javascript/flavours/glitch/containers/dropdown_menu_container.js => app/javascript/flavours/glitch/containers/dropdown_menu_container.js +7 -4
@@ 13,10 13,13 @@ const mapStateToProps = state => ({

const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
  onOpen(id, onItemClick, keyboard) {
    dispatch(isUserTouching() ? openModal('ACTIONS', {
      status,
      actions: items,
      onClick: onItemClick,
    dispatch(isUserTouching() ? openModal({
      modalType: 'ACTIONS',
      modalProps: {
        status,
        actions: items,
        onClick: onItemClick,
      },
    }) : openDropdownMenu(id, keyboard, scrollKey));
  },


M app/javascript/flavours/glitch/containers/notification_purge_buttons_container.js => app/javascript/flavours/glitch/containers/notification_purge_buttons_container.js +7 -4
@@ 23,10 23,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onDeleteMarked() {
    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.clearMessage),
      confirm: intl.formatMessage(messages.clearConfirm),
      onConfirm: () => dispatch(deleteMarkedNotifications()),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.clearMessage),
        confirm: intl.formatMessage(messages.clearConfirm),
        onConfirm: () => dispatch(deleteMarkedNotifications()),
      },
    }));
  },


M app/javascript/flavours/glitch/containers/status_container.js => app/javascript/flavours/glitch/containers/status_container.js +50 -23
@@ 99,11 99,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
      let state = getState();

      if (state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.replyMessage),
          confirm: intl.formatMessage(messages.replyConfirm),
          onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_before_clearing_draft'], false)),
          onConfirm: () => dispatch(replyCompose(status, router)),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: intl.formatMessage(messages.replyMessage),
            confirm: intl.formatMessage(messages.replyConfirm),
            onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_before_clearing_draft'], false)),
            onConfirm: () => dispatch(replyCompose(status, router)),
          },
        }));
      } else {
        dispatch(replyCompose(status, router));


@@ 151,7 154,13 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
      if (e.shiftKey || !favouriteModal) {
        this.onModalFavourite(status);
      } else {
        dispatch(openModal('FAVOURITE', { status, onFavourite: this.onModalFavourite }));
        dispatch(openModal({
          modalType: 'FAVOURITE',
          modalProps: {
            status,
            onFavourite: this.onModalFavourite,
          },
        }));
      }
    }
  },


@@ 165,9 174,12 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
  },

  onEmbed (status) {
    dispatch(openModal('EMBED', {
      url: status.get('url'),
      onError: error => dispatch(showAlertForError(error)),
    dispatch(openModal({
      modalType: 'EMBED',
      modalProps: {
        url: status.get('url'),
        onError: error => dispatch(showAlertForError(error)),
      },
    }));
  },



@@ 175,10 187,13 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
    if (!deleteModal) {
      dispatch(deleteStatus(status.get('id'), history, withRedraft));
    } else {
      dispatch(openModal('CONFIRM', {
        message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
        confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
        onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
      dispatch(openModal({
        modalType: 'CONFIRM',
        modalProps: {
          message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
          confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
          onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
        },
      }));
    }
  },


@@ 187,10 202,13 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
    dispatch((_, getState) => {
      let state = getState();
      if (state.getIn(['compose', 'text']).trim().length !== 0) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.editMessage),
          confirm: intl.formatMessage(messages.editConfirm),
          onConfirm: () => dispatch(editStatus(status.get('id'), history)),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: intl.formatMessage(messages.editMessage),
            confirm: intl.formatMessage(messages.editConfirm),
            onConfirm: () => dispatch(editStatus(status.get('id'), history)),
          },
        }));
      } else {
        dispatch(editStatus(status.get('id'), history));


@@ 215,11 233,17 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
  },

  onOpenMedia (statusId, media, index, lang) {
    dispatch(openModal('MEDIA', { statusId, media, index, lang }));
    dispatch(openModal({
      modalType: 'MEDIA',
      modalProps: { statusId, media, index, lang },
    }));
  },

  onOpenVideo (statusId, media, lang, options) {
    dispatch(openModal('VIDEO', { statusId, media, lang, options }));
    dispatch(openModal({
      modalType: 'VIDEO',
      modalProps: { statusId, media, lang, options },
    }));
  },

  onBlock (status) {


@@ 264,10 288,13 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
  },

  onInteractionModal (type, status) {
    dispatch(openModal('INTERACTION', {
      type,
      accountId: status.getIn(['account', 'id']),
      url: status.get('url'),
    dispatch(openModal({
      modalType: 'INTERACTION',
      modalProps: {
        type,
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
      },
    }));
  },


M app/javascript/flavours/glitch/features/account_gallery/index.jsx => app/javascript/flavours/glitch/features/account_gallery/index.jsx +12 -3
@@ 149,14 149,23 @@ class AccountGallery extends ImmutablePureComponent {
    const lang = attachment.getIn(['status', 'language']);

    if (attachment.get('type') === 'video') {
      dispatch(openModal('VIDEO', { media: attachment, statusId, lang, options: { autoPlay: true } }));
      dispatch(openModal({
        modalType: 'VIDEO',
        modalProps: { media: attachment, statusId, lang, options: { autoPlay: true } },
      }));
    } else if (attachment.get('type') === 'audio') {
      dispatch(openModal('AUDIO', { media: attachment, statusId, lang, options: { autoPlay: true } }));
      dispatch(openModal({
        modalType: 'AUDIO',
        modalProps: { media: attachment, statusId, lang, options: { autoPlay: true } },
      }));
    } else {
      const media = attachment.getIn(['status', 'media_attachments']);
      const index = media.findIndex(x => x.get('id') === attachment.get('id'));

      dispatch(openModal('MEDIA', { media, index, statusId, lang }));
      dispatch(openModal({
        modalType: 'MEDIA',
        modalProps: { media, index, statusId, lang },
      }));
    }
  };


M app/javascript/flavours/glitch/features/account_timeline/containers/header_container.jsx => app/javascript/flavours/glitch/features/account_timeline/containers/header_container.jsx +44 -23
@@ 48,20 48,26 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  onFollow (account) {
    if (account.getIn(['relationship', 'following'])) {
      if (unfollowModal) {
        dispatch(openModal('CONFIRM', {
          message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
          confirm: intl.formatMessage(messages.unfollowConfirm),
          onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
            confirm: intl.formatMessage(messages.unfollowConfirm),
            onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
          },
        }));
      } else {
        dispatch(unfollowAccount(account.get('id')));
      }
    } else if (account.getIn(['relationship', 'requested'])) {
      if (unfollowModal) {
        dispatch(openModal('CONFIRM', {
          message: <FormattedMessage id='confirmations.cancel_follow_request.message' defaultMessage='Are you sure you want to withdraw your request to follow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
          confirm: intl.formatMessage(messages.cancelFollowRequestConfirm),
          onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: <FormattedMessage id='confirmations.cancel_follow_request.message' defaultMessage='Are you sure you want to withdraw your request to follow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
            confirm: intl.formatMessage(messages.cancelFollowRequestConfirm),
            onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
          },
        }));
      } else {
        dispatch(unfollowAccount(account.get('id')));


@@ 72,10 78,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onInteractionModal (account) {
    dispatch(openModal('INTERACTION', {
      type: 'follow',
      accountId: account.get('id'),
      url: account.get('url'),
    dispatch(openModal({
      modalType: 'INTERACTION',
      modalProps: {
        type: 'follow',
        accountId: account.get('id'),
        url: account.get('url'),
      },
    }));
  },



@@ 136,10 145,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onBlockDomain (domain) {
    dispatch(openModal('CONFIRM', {
      message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.' values={{ domain: <strong>{domain}</strong> }} />,
      confirm: intl.formatMessage(messages.blockDomainConfirm),
      onConfirm: () => dispatch(blockDomain(domain)),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.' values={{ domain: <strong>{domain}</strong> }} />,
        confirm: intl.formatMessage(messages.blockDomainConfirm),
        onConfirm: () => dispatch(blockDomain(domain)),
      },
    }));
  },



@@ 148,21 160,30 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onAddToList (account) {
    dispatch(openModal('LIST_ADDER', {
      accountId: account.get('id'),
    dispatch(openModal({
      modalType: 'LIST_ADDER',
      modalProps: {
        accountId: account.get('id'),
      },
    }));
  },

  onChangeLanguages (account) {
    dispatch(openModal('SUBSCRIBED_LANGUAGES', {
      accountId: account.get('id'),
    dispatch(openModal({
      modalType: 'SUBSCRIBED_LANGUAGES',
      modalProps: {
        accountId: account.get('id'),
      },
    }));
  },

  onOpenAvatar (account) {
    dispatch(openModal('IMAGE', {
      src: account.get('avatar'),
      alt: account.get('acct'),
    dispatch(openModal({
      modalType: 'IMAGE',
      modalProps: {
        src: account.get('avatar'),
        alt: account.get('acct'),
      },
    }));
  },


M app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js => app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js +17 -11
@@ 126,18 126,24 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onMediaDescriptionConfirm(routerHistory, mediaId, overriddenVisibility = null) {
    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.missingDescriptionMessage),
      confirm: intl.formatMessage(messages.missingDescriptionConfirm),
      onConfirm: () => {
        if (overriddenVisibility) {
          dispatch(changeComposeVisibility(overriddenVisibility));
        }
        dispatch(submitCompose(routerHistory));
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.missingDescriptionMessage),
        confirm: intl.formatMessage(messages.missingDescriptionConfirm),
        onConfirm: () => {
          if (overriddenVisibility) {
            dispatch(changeComposeVisibility(overriddenVisibility));
          }
          dispatch(submitCompose(routerHistory));
        },
        secondary: intl.formatMessage(messages.missingDescriptionEdit),
        onSecondary: () => dispatch(openModal({
          modalType: 'FOCAL_POINT',
          modalProps: { id: mediaId },
        })),
        onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_missing_media_description'], false)),
      },
      secondary: intl.formatMessage(messages.missingDescriptionEdit),
      onSecondary: () => dispatch(openModal('FOCAL_POINT', { id: mediaId })),
      onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_missing_media_description'], false)),
    }));
  },


M app/javascript/flavours/glitch/features/compose/containers/dropdown_container.js => app/javascript/flavours/glitch/features/compose/containers/dropdown_container.js +1 -1
@@ 7,7 7,7 @@ import Dropdown from '../components/dropdown';

const mapDispatchToProps = dispatch => ({
  isUserTouching,
  onModalOpen: props => dispatch(openModal('ACTIONS', props)),
  onModalOpen: props => dispatch(openModal({ modalType: 'ACTIONS', modalProps: props })),
  onModalClose: () => dispatch(closeModal()),
});


M app/javascript/flavours/glitch/features/compose/containers/header_container.js => app/javascript/flavours/glitch/features/compose/containers/header_container.js +9 -6
@@ 24,14 24,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  onSettingsClick (e) {
    e.preventDefault();
    e.stopPropagation();
    dispatch(openModal('SETTINGS', {}));
    dispatch(openModal({ modalType: 'SETTINGS', modalProps: {} }));
  },
  onLogout () {
    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.logoutMessage),
      confirm: intl.formatMessage(messages.logoutConfirm),
      closeWhenConfirm: false,
      onConfirm: () => logOut(),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.logoutMessage),
        confirm: intl.formatMessage(messages.logoutConfirm),
        closeWhenConfirm: false,
        onConfirm: () => logOut(),
      },
    }));
  },
});

M app/javascript/flavours/glitch/features/compose/containers/navigation_container.js => app/javascript/flavours/glitch/features/compose/containers/navigation_container.js +8 -5
@@ 21,11 21,14 @@ const mapStateToProps = state => {

const mapDispatchToProps = (dispatch, { intl }) => ({
  onLogout () {
    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.logoutMessage),
      confirm: intl.formatMessage(messages.logoutConfirm),
      closeWhenConfirm: false,
      onConfirm: () => logOut(),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.logoutMessage),
        confirm: intl.formatMessage(messages.logoutConfirm),
        closeWhenConfirm: false,
        onConfirm: () => logOut(),
      },
    }));
  },
});

M app/javascript/flavours/glitch/features/compose/containers/options_container.js => app/javascript/flavours/glitch/features/compose/containers/options_container.js +4 -1
@@ 46,7 46,10 @@ const mapDispatchToProps = (dispatch) => ({
  },

  onDoodleOpen() {
    dispatch(openModal('DOODLE', { noEsc: true, noClose: true }));
    dispatch(openModal({
      modalType: 'DOODLE',
      modalProps: { noEsc: true, noClose: true },
    }));
  },
});


M app/javascript/flavours/glitch/features/compose/containers/privacy_dropdown_container.js => app/javascript/flavours/glitch/features/compose/containers/privacy_dropdown_container.js +8 -2
@@ 17,8 17,14 @@ const mapDispatchToProps = dispatch => ({
  },

  isUserTouching,
  onModalOpen: props => dispatch(openModal('ACTIONS', props)),
  onModalClose: () => dispatch(closeModal()),
  onModalOpen: props => dispatch(openModal({
    modalType: 'ACTIONS',
    modalProps: props,
  })),
  onModalClose: () => dispatch(closeModal({
    modalType: undefined,
    ignoreFocus: false,
  })),

});


M app/javascript/flavours/glitch/features/direct_timeline/containers/conversation_container.js => app/javascript/flavours/glitch/features/direct_timeline/containers/conversation_container.js +7 -4
@@ 42,10 42,13 @@ const mapDispatchToProps = (dispatch, { intl, conversationId }) => ({
      let state = getState();

      if (state.getIn(['compose', 'text']).trim().length !== 0) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.replyMessage),
          confirm: intl.formatMessage(messages.replyConfirm),
          onConfirm: () => dispatch(replyCompose(status, router)),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: intl.formatMessage(messages.replyMessage),
            confirm: intl.formatMessage(messages.replyConfirm),
            onConfirm: () => dispatch(replyCompose(status, router)),
          },
        }));
      } else {
        dispatch(replyCompose(status, router));

M app/javascript/flavours/glitch/features/directory/components/account_card.jsx => app/javascript/flavours/glitch/features/directory/components/account_card.jsx +20 -15
@@ 53,27 53,32 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
    if (account.getIn(['relationship', 'following'])) {
      if (unfollowModal) {
        dispatch(
          openModal('CONFIRM', {
            message: (
              <FormattedMessage
                id='confirmations.unfollow.message'
                defaultMessage='Are you sure you want to unfollow {name}?'
                values={{ name: <strong>@{account.get('acct')}</strong> }}
              />
            ),
            confirm: intl.formatMessage(messages.unfollowConfirm),
            onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
          }),
          openModal({
            modalType: 'CONFIRM',
            modalProps: {
              message: (
                <FormattedMessage
                  id='confirmations.unfollow.message'
                  defaultMessage='Are you sure you want to unfollow {name}?'
                  values={{ name: <strong>@{account.get('acct')}</strong> }}
                />
              ),
              confirm: intl.formatMessage(messages.unfollowConfirm),
              onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
            } }),
        );
      } else {
        dispatch(unfollowAccount(account.get('id')));
      }
    } else if (account.getIn(['relationship', 'requested'])) {
      if (unfollowModal) {
        dispatch(openModal('CONFIRM', {
          message: <FormattedMessage id='confirmations.cancel_follow_request.message' defaultMessage='Are you sure you want to withdraw your request to follow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
          confirm: intl.formatMessage(messages.cancelFollowRequestConfirm),
          onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: <FormattedMessage id='confirmations.cancel_follow_request.message' defaultMessage='Are you sure you want to withdraw your request to follow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
            confirm: intl.formatMessage(messages.cancelFollowRequestConfirm),
            onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
          },
        }));
      } else {
        dispatch(unfollowAccount(account.get('id')));

M app/javascript/flavours/glitch/features/getting_started/index.jsx => app/javascript/flavours/glitch/features/getting_started/index.jsx +4 -1
@@ 69,7 69,10 @@ const makeMapStateToProps = () => {
const mapDispatchToProps = dispatch => ({
  fetchFollowRequests: () => dispatch(fetchFollowRequests()),
  fetchLists: () => dispatch(fetchLists()),
  openSettings: () => dispatch(openModal('SETTINGS', {})),
  openSettings: () => dispatch(openModal({
    modalType: 'SETTINGS',
    modalProps: {},
  })),
});

const badgeDisplay = (number, limit) => {

M app/javascript/flavours/glitch/features/getting_started_misc/index.jsx => app/javascript/flavours/glitch/features/getting_started_misc/index.jsx +6 -2
@@ 38,11 38,15 @@ class GettingStartedMisc extends ImmutablePureComponent {
  };

  openOnboardingModal = () => {
    this.props.dispatch(openModal('ONBOARDING'));
    this.props.dispatch(openModal({
      modalType: 'ONBOARDING',
    }));
  };

  openFeaturedAccountsModal = () => {
    this.props.dispatch(openModal('PINNED_ACCOUNTS_EDITOR'));
    this.props.dispatch(openModal({
      modalType: 'PINNED_ACCOUNTS_EDITOR',
    }));
  };

  render () {

M app/javascript/flavours/glitch/features/interaction_modal/index.jsx => app/javascript/flavours/glitch/features/interaction_modal/index.jsx +5 -2
@@ 18,8 18,11 @@ const mapStateToProps = (state, { accountId }) => ({

const mapDispatchToProps = (dispatch) => ({
  onSignupClick() {
    dispatch(closeModal());
    dispatch(openModal('CLOSED_REGISTRATIONS'));
    dispatch(closeModal({
      modalType: undefined,
      ignoreFocus: false,
    }));
    dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
  },
});


M app/javascript/flavours/glitch/features/list_timeline/index.jsx => app/javascript/flavours/glitch/features/list_timeline/index.jsx +18 -12
@@ 114,24 114,30 @@ class ListTimeline extends PureComponent {
  };

  handleEditClick = () => {
    this.props.dispatch(openModal('LIST_EDITOR', { listId: this.props.params.id }));
    this.props.dispatch(openModal({
      modalType: 'LIST_EDITOR',
      modalProps: { listId: this.props.params.id },
    }));
  };

  handleDeleteClick = () => {
    const { dispatch, columnId, intl } = this.props;
    const { id } = this.props.params;

    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.deleteMessage),
      confirm: intl.formatMessage(messages.deleteConfirm),
      onConfirm: () => {
        dispatch(deleteList(id));

        if (columnId) {
          dispatch(removeColumn(columnId));
        } else {
          this.context.router.history.push('/lists');
        }
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.deleteMessage),
        confirm: intl.formatMessage(messages.deleteConfirm),
        onConfirm: () => {
          dispatch(deleteList(id));

          if (columnId) {
            dispatch(removeColumn(columnId));
          } else {
            this.context.router.history.push('/lists');
          }
        },
      },
    }));
  };

M app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js => app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js +7 -4
@@ 61,10 61,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onClear () {
    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.clearMessage),
      confirm: intl.formatMessage(messages.clearConfirm),
      onConfirm: () => dispatch(clearNotifications()),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.clearMessage),
        confirm: intl.formatMessage(messages.clearConfirm),
        onConfirm: () => dispatch(clearNotifications()),
      },
    }));
  },


M app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx => app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx +28 -16
@@ 76,19 76,25 @@ class Footer extends ImmutablePureComponent {

    if (signedIn) {
      if (askReplyConfirmation) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.replyMessage),
          confirm: intl.formatMessage(messages.replyConfirm),
          onConfirm: this._performReply,
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: intl.formatMessage(messages.replyMessage),
            confirm: intl.formatMessage(messages.replyConfirm),
            onConfirm: this._performReply,
          },
        }));
      } else {
        this._performReply();
      }
    } else {
      dispatch(openModal('INTERACTION', {
        type: 'reply',
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
      dispatch(openModal({
        modalType: 'INTERACTION',
        modalProps: {
          type: 'reply',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
        },
      }));
    }
  };


@@ 104,10 110,13 @@ class Footer extends ImmutablePureComponent {
        dispatch(favourite(status));
      }
    } else {
      dispatch(openModal('INTERACTION', {
        type: 'favourite',
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
      dispatch(openModal({
        modalType: 'INTERACTION',
        modalProps: {
          type: 'favourite',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
        },
      }));
    }
  };


@@ 130,10 139,13 @@ class Footer extends ImmutablePureComponent {
        dispatch(initBoostModal({ status, onReblog: this._performReblog }));
      }
    } else {
      dispatch(openModal('INTERACTION', {
        type: 'reblog',
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
      dispatch(openModal({
        modalType: 'INTERACTION',
        modalProps: {
          type: 'reblog',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
        },
      }));
    }
  };

M app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js => app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js +28 -13
@@ 58,10 58,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
    dispatch((_, getState) => {
      let state = getState();
      if (state.getIn(['compose', 'text']).trim().length !== 0) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.replyMessage),
          confirm: intl.formatMessage(messages.replyConfirm),
          onConfirm: () => dispatch(replyCompose(status, router)),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: intl.formatMessage(messages.replyMessage),
            confirm: intl.formatMessage(messages.replyConfirm),
            onConfirm: () => dispatch(replyCompose(status, router)),
          },
        }));
      } else {
        dispatch(replyCompose(status, router));


@@ 102,9 105,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onEmbed (status) {
    dispatch(openModal('EMBED', {
      url: status.get('url'),
      onError: error => dispatch(showAlertForError(error)),
    dispatch(openModal({
      modalType: 'EMBED',
      modalProps: {
        url: status.get('url'),
        onError: error => dispatch(showAlertForError(error)),
      },
    }));
  },



@@ 112,10 118,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
    if (!deleteModal) {
      dispatch(deleteStatus(status.get('id'), history, withRedraft));
    } else {
      dispatch(openModal('CONFIRM', {
        message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
        confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
        onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
      dispatch(openModal({
        modalType: 'CONFIRM',
        modalProps: {
          message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
          confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
          onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
        },
      }));
    }
  },


@@ 129,11 138,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
  },

  onOpenMedia (media, index, lang) {
    dispatch(openModal('MEDIA', { media, index, lang }));
    dispatch(openModal({
      modalType: 'MEDIA',
      modalProps: { media, index, lang },
    }));
  },

  onOpenVideo (media, lang, options) {
    dispatch(openModal('VIDEO', { media, lang, options }));
    dispatch(openModal({
      modalType: 'VIDEO',
      modalProps: { media, lang, options },
    }));
  },

  onBlock (status) {

M app/javascript/flavours/glitch/features/status/index.jsx => app/javascript/flavours/glitch/features/status/index.jsx +55 -25
@@ 289,14 289,23 @@ class Status extends ImmutablePureComponent {
        if ((e && e.shiftKey) || !favouriteModal) {
          this.handleModalFavourite(status);
        } else {
          dispatch(openModal('FAVOURITE', { status, onFavourite: this.handleModalFavourite }));
          dispatch(openModal({
            modalType: 'FAVOURITE',
            modalProps: {
              status,
              onFavourite: this.handleModalFavourite,
            },
          }));
        }
      }
    } else {
      dispatch(openModal('INTERACTION', {
        type: 'favourite',
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
      dispatch(openModal({
        modalType: 'INTERACTION',
        modalProps: {
          type: 'favourite',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
        },
      }));
    }
  };


@@ 315,20 324,26 @@ class Status extends ImmutablePureComponent {

    if (signedIn) {
      if (askReplyConfirmation) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.replyMessage),
          confirm: intl.formatMessage(messages.replyConfirm),
          onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_before_clearing_draft'], false)),
          onConfirm: () => dispatch(replyCompose(status, this.context.router.history)),
        dispatch(openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: intl.formatMessage(messages.replyMessage),
            confirm: intl.formatMessage(messages.replyConfirm),
            onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_before_clearing_draft'], false)),
            onConfirm: () => dispatch(replyCompose(status, this.context.router.history)),
          },
        }));
      } else {
        dispatch(replyCompose(status, this.context.router.history));
      }
    } else {
      dispatch(openModal('INTERACTION', {
        type: 'reply',
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
      dispatch(openModal({
        modalType: 'INTERACTION',
        modalProps: {
          type: 'reply',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
        },
      }));
    }
  };


@@ 356,10 371,13 @@ class Status extends ImmutablePureComponent {
        dispatch(initBoostModal({ status, onReblog: this.handleModalReblog }));
      }
    } else {
      dispatch(openModal('INTERACTION', {
        type: 'reblog',
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
      dispatch(openModal({
        modalType: 'INTERACTION',
        modalProps: {
          type: 'reblog',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
        },
      }));
    }
  };


@@ 378,10 396,13 @@ class Status extends ImmutablePureComponent {
    if (!deleteModal) {
      dispatch(deleteStatus(status.get('id'), history, withRedraft));
    } else {
      dispatch(openModal('CONFIRM', {
        message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
        confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
        onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
      dispatch(openModal({
        modalType: 'CONFIRM',
        modalProps: {
          message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
          confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
          onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
        },
      }));
    }
  };


@@ 399,11 420,17 @@ class Status extends ImmutablePureComponent {
  };

  handleOpenMedia = (media, index, lang) => {
    this.props.dispatch(openModal('MEDIA', { statusId: this.props.status.get('id'), media, index, lang }));
    this.props.dispatch(openModal({
      modalType: 'MEDIA',
      modalProps: { statusId: this.props.status.get('id'), media, index, lang },
    }));
  };

  handleOpenVideo = (media, lang, options) => {
    this.props.dispatch(openModal('VIDEO', { statusId: this.props.status.get('id'), media, lang, options }));
    this.props.dispatch(openModal({
      modalType: 'VIDEO',
      modalProps: { statusId: this.props.status.get('id'), media, lang, options },
    }));
  };

  handleHotkeyOpenMedia = e => {


@@ 470,7 497,10 @@ class Status extends ImmutablePureComponent {
  };

  handleEmbed = (status) => {
    this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
    this.props.dispatch(openModal({
      modalType: 'EMBED',
      modalProps: { url: status.get('url') },
    }));
  };

  handleHotkeyToggleSensitive = () => {

M app/javascript/flavours/glitch/features/ui/components/block_modal.jsx => app/javascript/flavours/glitch/features/ui/components/block_modal.jsx +4 -1
@@ 34,7 34,10 @@ const mapDispatchToProps = dispatch => {
    },

    onClose() {
      dispatch(closeModal());
      dispatch(closeModal({
        modalType: undefined,
        ignoreFocus: false,
      }));
    },
  };
};

M app/javascript/flavours/glitch/features/ui/components/compare_history_modal.jsx => app/javascript/flavours/glitch/features/ui/components/compare_history_modal.jsx +4 -1
@@ 23,7 23,10 @@ const mapStateToProps = (state, { statusId }) => ({
const mapDispatchToProps = dispatch => ({

  onClose() {
    dispatch(closeModal());
    dispatch(closeModal({
      modalType: undefined,
      ignoreFocus: false,
    }));
  },

});

M app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.jsx => app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.jsx +8 -5
@@ 23,11 23,14 @@ const mapStateToProps = (state) => ({

const mapDispatchToProps = (dispatch, { intl }) => ({
  onLogout () {
    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.logoutMessage),
      confirm: intl.formatMessage(messages.logoutConfirm),
      closeWhenConfirm: false,
      onConfirm: () => logOut(),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.logoutMessage),
        confirm: intl.formatMessage(messages.logoutConfirm),
        closeWhenConfirm: false,
        onConfirm: () => logOut(),
      },
    }));
  },
});

M app/javascript/flavours/glitch/features/ui/components/header.jsx => app/javascript/flavours/glitch/features/ui/components/header.jsx +1 -1
@@ 27,7 27,7 @@ const mapStateToProps = (state) => ({

const mapDispatchToProps = (dispatch) => ({
  openClosedRegistrationsModal() {
    dispatch(openModal('CLOSED_REGISTRATIONS'));
    dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
  },
});


M app/javascript/flavours/glitch/features/ui/components/link_footer.jsx => app/javascript/flavours/glitch/features/ui/components/link_footer.jsx +8 -5
@@ 19,11 19,14 @@ const messages = defineMessages({

const mapDispatchToProps = (dispatch, { intl }) => ({
  onLogout () {
    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.logoutMessage),
      confirm: intl.formatMessage(messages.logoutConfirm),
      closeWhenConfirm: false,
      onConfirm: () => logOut(),
    dispatch(openModal({
      modalType: 'CONFIRM',
      modalProps: {
        message: intl.formatMessage(messages.logoutMessage),
        confirm: intl.formatMessage(messages.logoutConfirm),
        closeWhenConfirm: false,
        onConfirm: () => logOut(),
      },
    }));
  },
});

M app/javascript/flavours/glitch/features/ui/components/modal_root.jsx => app/javascript/flavours/glitch/features/ui/components/modal_root.jsx +1 -1
@@ 40,7 40,7 @@ import VideoModal from './video_modal';



const MODAL_COMPONENTS = {
export const MODAL_COMPONENTS = {
  'MEDIA': () => Promise.resolve({ default: MediaModal }),
  'ONBOARDING': OnboardingModal,
  'VIDEO': () => Promise.resolve({ default: VideoModal }),

M app/javascript/flavours/glitch/features/ui/components/mute_modal.jsx => app/javascript/flavours/glitch/features/ui/components/mute_modal.jsx +4 -1
@@ 34,7 34,10 @@ const mapDispatchToProps = dispatch => {
    },

    onClose() {
      dispatch(closeModal());
      dispatch(closeModal({
        modalType: undefined,
        ignoreFocus: false,
      }));
    },

    onToggleNotifications() {

M app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx => app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx +1 -1
@@ 10,7 10,7 @@ const SignInBanner = () => {
  const dispatch = useAppDispatch();

  const openClosedRegistrationsModal = useCallback(
    () => dispatch(openModal('CLOSED_REGISTRATIONS')),
    () => dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' })),
    [dispatch],
  );


M app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js => app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js +4 -1
@@ 12,7 12,10 @@ const mapDispatchToProps = dispatch => ({
  openSettings (e) {
    e.preventDefault();
    e.stopPropagation();
    dispatch(openModal('SETTINGS', {}));
    dispatch(openModal({
      modalType: 'SETTINGS',
      modalProps: {},
    }));
  },
});


M app/javascript/flavours/glitch/features/ui/containers/modal_container.js => app/javascript/flavours/glitch/features/ui/containers/modal_container.js +14 -6
@@ 14,14 14,22 @@ const mapDispatchToProps = dispatch => ({
  onClose (confirmationMessage, ignoreFocus = false) {
    if (confirmationMessage) {
      dispatch(
        openModal('CONFIRM', {
          message: confirmationMessage.message,
          confirm: confirmationMessage.confirm,
          onConfirm: () => dispatch(closeModal(undefined, { ignoreFocus })),
        }),
        openModal({
          modalType: 'CONFIRM',
          modalProps: {
            message: confirmationMessage.message,
            confirm: confirmationMessage.confirm,
            onConfirm: () => dispatch(closeModal({
              modalType: undefined,
              ignoreFocus: { ignoreFocus },
            })),
          } }),
      );
    } else {
      dispatch(closeModal(undefined, { ignoreFocus }));
      dispatch(closeModal({
        modalType: undefined,
        ignoreFocus: { ignoreFocus },
      }));
    }
  },
});

M app/javascript/flavours/glitch/reducers/index.ts => app/javascript/flavours/glitch/reducers/index.ts +2 -2
@@ 26,7 26,7 @@ import local_settings from './local_settings';
import markers from './markers';
import media_attachments from './media_attachments';
import meta from './meta';
import modal from './modal';
import { modalReducer } from './modal';
import mutes from './mutes';
import notifications from './notifications';
import picture_in_picture from './picture_in_picture';


@@ 52,7 52,7 @@ const reducers = {
  meta,
  alerts,
  loadingBar: loadingBarReducer,
  modal,
  modal: modalReducer,
  user_lists,
  domain_lists,
  status_lists,

D app/javascript/flavours/glitch/reducers/modal.js => app/javascript/flavours/glitch/reducers/modal.js +0 -40
@@ 1,40 0,0 @@
import { Stack as ImmutableStack, Map as ImmutableMap } from 'immutable';

import { COMPOSE_UPLOAD_CHANGE_SUCCESS } from 'flavours/glitch/actions/compose';
import { MODAL_OPEN, MODAL_CLOSE } from 'flavours/glitch/actions/modal';
import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines';

const initialState = ImmutableMap({
  ignoreFocus: false,
  stack: ImmutableStack(),
});

const popModal = (state, { modalType, ignoreFocus }) => {
  if (modalType === undefined || modalType === state.getIn(['stack', 0, 'modalType'])) {
    return state.set('ignoreFocus', !!ignoreFocus).update('stack', stack => stack.shift());
  } else {
    return state;
  }
};

const pushModal = (state, modalType, modalProps) => {
  return state.withMutations(map => {
    map.set('ignoreFocus', false);
    map.update('stack', stack => stack.unshift(ImmutableMap({ modalType, modalProps })));
  });
};

export default function modal(state = initialState, action) {
  switch(action.type) {
  case MODAL_OPEN:
    return pushModal(state, action.modalType, action.modalProps);
  case MODAL_CLOSE:
    return popModal(state, action);
  case COMPOSE_UPLOAD_CHANGE_SUCCESS:
    return popModal(state, { modalType: 'FOCAL_POINT', ignoreFocus: false });
  case TIMELINE_DELETE:
    return state.update('stack', stack => stack.filterNot((modal) => modal.get('modalProps').statusId === action.id));
  default:
    return state;
  }
}

A app/javascript/flavours/glitch/reducers/modal.ts => app/javascript/flavours/glitch/reducers/modal.ts +94 -0
@@ 0,0 1,94 @@
import { Record as ImmutableRecord, Stack } from 'immutable';

import type { PayloadAction } from '@reduxjs/toolkit';

import { COMPOSE_UPLOAD_CHANGE_SUCCESS } from '../actions/compose';
import type { ModalType } from '../actions/modal';
import { openModal, closeModal } from '../actions/modal';
import { TIMELINE_DELETE } from '../actions/timelines';

type ModalProps = Record<string, unknown>;
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,
  { modalType, ignoreFocus }: PopModalOption
): 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,
  modalProps: ModalProps
): State => {
  return state.withMutations((record) => {
    record.set('ignoreFocus', false);
    record.update('stack', (stack) =>
      stack.unshift(Modal({ modalType, modalProps }))
    );
  });
};

export function modalReducer(
  state: State = initialState,
  action: PayloadAction<{
    modalType: ModalType;
    ignoreFocus: boolean;
    modalProps: Record<string, unknown>;
  }>
) {
  switch (action.type) {
    case openModal.type:
      return pushModal(
        state,
        action.payload.modalType,
        action.payload.modalProps
      );
    case closeModal.type:
      return popModal(state, action.payload);
    case COMPOSE_UPLOAD_CHANGE_SUCCESS:
      return popModal(state, { modalType: 'FOCAL_POINT', ignoreFocus: false });
    case TIMELINE_DELETE:
      return state.update('stack', (stack) =>
        stack.filterNot(
          // @ts-expect-error TIMELINE_DELETE action is not typed yet.
          (modal) => modal.get('modalProps').statusId === action.id
        )
      );
    default:
      return state;
  }
}