diff --git a/app/javascript/mastodon/actions/interactions.js b/app/javascript/mastodon/actions/interactions.js
index 57f2459c0..9d39b7a57 100644
--- a/app/javascript/mastodon/actions/interactions.js
+++ b/app/javascript/mastodon/actions/interactions.js
@@ -1,7 +1,11 @@
+import { boostModal } from 'mastodon/initial_state';
+
 import api, { getLinks } from '../api';
 
 import { fetchRelationships } from './accounts';
 import { importFetchedAccounts, importFetchedStatus } from './importer';
+import { unreblog, reblog } from './interactions_typed';
+import { openModal } from './modal';
 
 export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST';
 export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
@@ -432,3 +436,49 @@ export function unpinFail(status, error) {
     skipLoading: true,
   };
 }
+
+function toggleReblogWithoutConfirmation(status, privacy) {
+  return (dispatch) => {
+    if (status.get('reblogged')) {
+      dispatch(unreblog({ statusId: status.get('id') }));
+    } else {
+      dispatch(reblog({ statusId: status.get('id'), privacy }));
+    }
+  };
+}
+
+export function toggleReblog(statusId, skipModal = false) {
+  return (dispatch, getState) => {
+    const state = getState();
+    let status = state.statuses.get(statusId);
+
+    if (!status)
+      return;
+
+    // The reblog modal expects a pre-filled account in status
+    // TODO: fix this by having the reblog modal get a statusId and do the work itself
+    status = status.set('account', state.accounts.get(status.get('account')));
+
+    if (boostModal && !skipModal) {
+      dispatch(openModal({ modalType: 'BOOST', modalProps: { status, onReblog: toggleReblogWithoutConfirmation } }));
+    } else {
+      toggleReblogWithoutConfirmation(status);
+    }
+  };
+}
+
+export function toggleFavourite(statusId) {
+  return (dispatch, getState) => {
+    const state = getState();
+    const status = state.statuses.get(statusId);
+
+    if (!status)
+      return;
+
+    if (status.get('favourited')) {
+      dispatch(unfavourite(status));
+    } else {
+      dispatch(favourite(status));
+    }
+  };
+}
diff --git a/app/javascript/mastodon/containers/status_container.jsx b/app/javascript/mastodon/containers/status_container.jsx
index e2c0af353..8e34ace87 100644
--- a/app/javascript/mastodon/containers/status_container.jsx
+++ b/app/javascript/mastodon/containers/status_container.jsx
@@ -21,11 +21,9 @@ import {
   initAddFilter,
 } from '../actions/filters';
 import {
-  reblog,
-  favourite,
+  toggleReblog,
+  toggleFavourite,
   bookmark,
-  unreblog,
-  unfavourite,
   unbookmark,
   pin,
   unpin,
@@ -46,7 +44,7 @@ import {
   undoStatusTranslation,
 } from '../actions/statuses';
 import Status from '../components/status';
-import { boostModal, deleteModal } from '../initial_state';
+import { deleteModal } from '../initial_state';
 import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
 
 const messages = defineMessages({
@@ -94,28 +92,12 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
     });
   },
 
-  onModalReblog (status, privacy) {
-    if (status.get('reblogged')) {
-      dispatch(unreblog({ statusId: status.get('id') }));
-    } else {
-      dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
-    }
-  },
-
   onReblog (status, e) {
-    if ((e && e.shiftKey) || !boostModal) {
-      this.onModalReblog(status);
-    } else {
-      dispatch(openModal({ modalType: 'BOOST', modalProps: { status, onReblog: this.onModalReblog } }));
-    }
+    dispatch(toggleReblog(status.get('id'), e.shiftKey));
   },
 
   onFavourite (status) {
-    if (status.get('favourited')) {
-      dispatch(unfavourite(status));
-    } else {
-      dispatch(favourite(status));
-    }
+    dispatch(toggleFavourite(status.get('id')));
   },
 
   onBookmark (status) {
diff --git a/app/javascript/mastodon/features/notifications/containers/notification_container.js b/app/javascript/mastodon/features/notifications/containers/notification_container.js
index 7b7e6e919..98a3cf8b1 100644
--- a/app/javascript/mastodon/features/notifications/containers/notification_container.js
+++ b/app/javascript/mastodon/features/notifications/containers/notification_container.js
@@ -2,17 +2,13 @@ import { connect } from 'react-redux';
 
 import { mentionCompose } from '../../../actions/compose';
 import {
-  reblog,
-  favourite,
-  unreblog,
-  unfavourite,
+  toggleFavourite,
+  toggleReblog,
 } from '../../../actions/interactions';
-import { openModal } from '../../../actions/modal';
 import {
   hideStatus,
   revealStatus,
 } from '../../../actions/statuses';
-import { boostModal } from '../../../initial_state';
 import { makeGetNotification, makeGetStatus, makeGetReport } from '../../../selectors';
 import Notification from '../components/notification';
 
@@ -38,28 +34,12 @@ const mapDispatchToProps = dispatch => ({
     dispatch(mentionCompose(account));
   },
 
-  onModalReblog (status, privacy) {
-    dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
-  },
-
   onReblog (status, e) {
-    if (status.get('reblogged')) {
-      dispatch(unreblog({ statusId: status.get('id') }));
-    } else {
-      if (e.shiftKey || !boostModal) {
-        this.onModalReblog(status);
-      } else {
-        dispatch(openModal({ modalType: 'BOOST', modalProps: { status, onReblog: this.onModalReblog } }));
-      }
-    }
+    dispatch(toggleReblog(status.get('id'), e.shiftKey));
   },
 
   onFavourite (status) {
-    if (status.get('favourited')) {
-      dispatch(unfavourite(status));
-    } else {
-      dispatch(favourite(status));
-    }
+    dispatch(toggleFavourite(status.get('id')));
   },
 
   onToggleHidden (status) {
diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
index 8404bf61b..d5226eb34 100644
--- a/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
+++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
@@ -15,11 +15,11 @@ import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
 import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
 import StarIcon from '@/material-icons/400-24px/star.svg?react';
 import { replyCompose } from 'mastodon/actions/compose';
-import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
+import { toggleReblog, toggleFavourite } from 'mastodon/actions/interactions';
 import { openModal } from 'mastodon/actions/modal';
 import { IconButton } from 'mastodon/components/icon_button';
 import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
-import { me, boostModal } from 'mastodon/initial_state';
+import { me } from 'mastodon/initial_state';
 import { makeGetStatus } from 'mastodon/selectors';
 import { WithRouterPropTypes } from 'mastodon/utils/react_router';
 
@@ -104,11 +104,7 @@ class Footer extends ImmutablePureComponent {
     const { signedIn } = this.props.identity;
 
     if (signedIn) {
-      if (status.get('favourited')) {
-        dispatch(unfavourite(status));
-      } else {
-        dispatch(favourite(status));
-      }
+      dispatch(toggleFavourite(status.get('id')));
     } else {
       dispatch(openModal({
         modalType: 'INTERACTION',
@@ -121,23 +117,12 @@ class Footer extends ImmutablePureComponent {
     }
   };
 
-  _performReblog = (status, privacy) => {
-    const { dispatch } = this.props;
-    dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
-  };
-
   handleReblogClick = e => {
     const { dispatch, status } = this.props;
     const { signedIn } = this.props.identity;
 
     if (signedIn) {
-      if (status.get('reblogged')) {
-        dispatch(unreblog({ statusId: status.get('id') }));
-      } else if ((e && e.shiftKey) || !boostModal) {
-        this._performReblog(status);
-      } else {
-        dispatch(openModal({ modalType: 'BOOST', modalProps: { status, onReblog: this._performReblog } }));
-      }
+      dispatch(toggleReblog(status.get('id'), e && e.shiftKey));
     } else {
       dispatch(openModal({
         modalType: 'INTERACTION',
diff --git a/app/javascript/mastodon/features/status/containers/detailed_status_container.js b/app/javascript/mastodon/features/status/containers/detailed_status_container.js
index f85c3822d..f37860d25 100644
--- a/app/javascript/mastodon/features/status/containers/detailed_status_container.js
+++ b/app/javascript/mastodon/features/status/containers/detailed_status_container.js
@@ -10,10 +10,8 @@ import {
   directCompose,
 } from '../../../actions/compose';
 import {
-  reblog,
-  favourite,
-  unreblog,
-  unfavourite,
+  toggleReblog,
+  toggleFavourite,
   pin,
   unpin,
 } from '../../../actions/interactions';
@@ -27,7 +25,7 @@ import {
   hideStatus,
   revealStatus,
 } from '../../../actions/statuses';
-import { boostModal, deleteModal } from '../../../initial_state';
+import { deleteModal } from '../../../initial_state';
 import { makeGetStatus, makeGetPictureInPicture } from '../../../selectors';
 import DetailedStatus from '../components/detailed_status';
 
@@ -73,28 +71,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     });
   },
 
-  onModalReblog (status, privacy) {
-    dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
-  },
-
   onReblog (status, e) {
-    if (status.get('reblogged')) {
-      dispatch(unreblog({ statusId: status.get('id') }));
-    } else {
-      if (e.shiftKey || !boostModal) {
-        this.onModalReblog(status);
-      } else {
-        dispatch(openModal({ modalType: 'BOOST', modalProps: { status, onReblog: this.onModalReblog } }));
-      }
-    }
+    dispatch(toggleReblog(status.get('id'), e.shiftKey));
   },
 
   onFavourite (status) {
-    if (status.get('favourited')) {
-      dispatch(unfavourite(status));
-    } else {
-      dispatch(favourite(status));
-    }
+    dispatch(toggleFavourite(status.get('id')));
   },
 
   onPin (status) {
diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx
index b0fbea9e2..e9df3697a 100644
--- a/app/javascript/mastodon/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -38,12 +38,10 @@ import {
   unblockDomain,
 } from '../../actions/domain_blocks';
 import {
-  favourite,
-  unfavourite,
+  toggleFavourite,
   bookmark,
   unbookmark,
-  reblog,
-  unreblog,
+  toggleReblog,
   pin,
   unpin,
 } from '../../actions/interactions';
@@ -64,7 +62,7 @@ import {
 import ColumnHeader from '../../components/column_header';
 import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
 import StatusContainer from '../../containers/status_container';
-import { boostModal, deleteModal } from '../../initial_state';
+import { deleteModal } from '../../initial_state';
 import { makeGetStatus, makeGetPictureInPicture } from '../../selectors';
 import Column from '../ui/components/column';
 import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
@@ -244,11 +242,7 @@ class Status extends ImmutablePureComponent {
     const { signedIn } = this.props.identity;
 
     if (signedIn) {
-      if (status.get('favourited')) {
-        dispatch(unfavourite(status));
-      } else {
-        dispatch(favourite(status));
-      }
+      dispatch(toggleFavourite(status.get('id')));
     } else {
       dispatch(openModal({
         modalType: 'INTERACTION',
@@ -298,24 +292,12 @@ class Status extends ImmutablePureComponent {
     }
   };
 
-  handleModalReblog = (status, privacy) => {
-    this.props.dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
-  };
-
   handleReblogClick = (status, e) => {
     const { dispatch } = this.props;
     const { signedIn } = this.props.identity;
 
     if (signedIn) {
-      if (status.get('reblogged')) {
-        dispatch(unreblog({ statusId: status.get('id') }));
-      } else {
-        if ((e && e.shiftKey) || !boostModal) {
-          this.handleModalReblog(status);
-        } else {
-          dispatch(openModal({ modalType: 'BOOST', modalProps: { status, onReblog: this.handleModalReblog } }));
-        }
-      }
+      dispatch(toggleReblog(status.get('id'), e && e.shiftKey));
     } else {
       dispatch(openModal({
         modalType: 'INTERACTION',