about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2022-05-15 17:30:40 +0200
committerGitHub <noreply@github.com>2022-05-15 17:30:40 +0200
commitdc350be6f5a9f59385e6fbc7a06b5b9c0d57aec8 (patch)
treeef7ebcaf1937165a14b2c53264f1f4ffb224c1d9 /app/javascript/flavours/glitch
parentaa08399e6f4ff35a7ece05ebbeca4b9771c97927 (diff)
Use upstream's settings for CW auto-expand and column swiping (#1770)
* Use Mastodon server-side settings for automatically expanding toots with CWs

* Add modal warning about settings changes

* Use Mastodon server-side settings for disabling swiping
Diffstat (limited to 'app/javascript/flavours/glitch')
-rw-r--r--app/javascript/flavours/glitch/actions/importer/normalizer.js1
-rw-r--r--app/javascript/flavours/glitch/actions/local_settings.js53
-rw-r--r--app/javascript/flavours/glitch/components/modal_root.js4
-rw-r--r--app/javascript/flavours/glitch/containers/mastodon.js4
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/components/announcements.js3
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js83
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/index.js53
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/item/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/columns_area.js7
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js86
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/media_modal.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/modal_root.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js1
-rw-r--r--app/javascript/flavours/glitch/images/logo_warn_glitch.svg49
-rw-r--r--app/javascript/flavours/glitch/reducers/local_settings.js6
-rw-r--r--app/javascript/flavours/glitch/styles/components/local_settings.scss38
-rw-r--r--app/javascript/flavours/glitch/styles/components/modal.scss7
-rw-r--r--app/javascript/flavours/glitch/util/backend_links.js9
-rw-r--r--app/javascript/flavours/glitch/util/content_warning.js4
-rw-r--r--app/javascript/flavours/glitch/util/initial_state.js3
20 files changed, 396 insertions, 24 deletions
diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js
index bda15a9b0..c38af196a 100644
--- a/app/javascript/flavours/glitch/actions/importer/normalizer.js
+++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js
@@ -1,7 +1,6 @@
 import escapeTextContentForBrowser from 'escape-html';
 import emojify from 'flavours/glitch/util/emoji';
 import { unescapeHTML } from 'flavours/glitch/util/html';
-import { expandSpoilers } from 'flavours/glitch/util/initial_state';
 
 const domParser = new DOMParser();
 
diff --git a/app/javascript/flavours/glitch/actions/local_settings.js b/app/javascript/flavours/glitch/actions/local_settings.js
index 28660a4e8..856674eb3 100644
--- a/app/javascript/flavours/glitch/actions/local_settings.js
+++ b/app/javascript/flavours/glitch/actions/local_settings.js
@@ -1,4 +1,46 @@
+import { expandSpoilers, disableSwiping } from 'flavours/glitch/util/initial_state';
+import { openModal } from './modal';
+
 export const LOCAL_SETTING_CHANGE = 'LOCAL_SETTING_CHANGE';
+export const LOCAL_SETTING_DELETE = 'LOCAL_SETTING_DELETE';
+
+export function checkDeprecatedLocalSettings() {
+  return (dispatch, getState) => {
+    const local_auto_unfold = getState().getIn(['local_settings', 'content_warnings', 'auto_unfold']);
+    const local_swipe_to_change_columns = getState().getIn(['local_settings', 'swipe_to_change_columns']);
+    let changed_settings = [];
+
+    if (local_auto_unfold !== null && local_auto_unfold !== undefined) {
+      if (local_auto_unfold === expandSpoilers) {
+        dispatch(deleteLocalSetting(['content_warnings', 'auto_unfold']));
+      } else {
+        changed_settings.push('user_setting_expand_spoilers');
+      }
+    }
+
+    if (local_swipe_to_change_columns !== null && local_swipe_to_change_columns !== undefined) {
+      if (local_swipe_to_change_columns === !disableSwiping) {
+        dispatch(deleteLocalSetting(['swipe_to_change_columns']));
+      } else {
+        changed_settings.push('user_setting_disable_swiping');
+      }
+    }
+
+    if (changed_settings.length > 0) {
+      dispatch(openModal('DEPRECATED_SETTINGS', {
+        settings: changed_settings,
+        onConfirm: () => dispatch(clearDeprecatedLocalSettings()),
+      }));
+    }
+  };
+};
+
+export function clearDeprecatedLocalSettings() {
+  return (dispatch) => {
+    dispatch(deleteLocalSetting(['content_warnings', 'auto_unfold']));
+    dispatch(deleteLocalSetting(['swipe_to_change_columns']));
+  };
+};
 
 export function changeLocalSetting(key, value) {
   return dispatch => {
@@ -12,6 +54,17 @@ export function changeLocalSetting(key, value) {
   };
 };
 
+export function deleteLocalSetting(key) {
+  return dispatch => {
+    dispatch({
+      type: LOCAL_SETTING_DELETE,
+      key,
+    });
+
+    dispatch(saveLocalSettings());
+  };
+};
+
 //  __TODO :__
 //  Right now `saveLocalSettings()` doesn't keep track of which user
 //  is currently signed in, but it might be better to give each user
diff --git a/app/javascript/flavours/glitch/components/modal_root.js b/app/javascript/flavours/glitch/components/modal_root.js
index 0595f6a0e..056277447 100644
--- a/app/javascript/flavours/glitch/components/modal_root.js
+++ b/app/javascript/flavours/glitch/components/modal_root.js
@@ -55,6 +55,10 @@ export default class ModalRoot extends React.PureComponent {
     window.addEventListener('keyup', this.handleKeyUp, false);
     window.addEventListener('keydown', this.handleKeyDown, false);
     this.history = this.context.router ? this.context.router.history : createBrowserHistory();
+
+    if (this.props.children) {
+      this._handleModalOpen();
+    }
   }
 
   componentWillReceiveProps (nextProps) {
diff --git a/app/javascript/flavours/glitch/containers/mastodon.js b/app/javascript/flavours/glitch/containers/mastodon.js
index de8ea8ee2..989e37024 100644
--- a/app/javascript/flavours/glitch/containers/mastodon.js
+++ b/app/javascript/flavours/glitch/containers/mastodon.js
@@ -8,6 +8,7 @@ import UI from 'flavours/glitch/features/ui';
 import { fetchCustomEmojis } from 'flavours/glitch/actions/custom_emojis';
 import { hydrateStore } from 'flavours/glitch/actions/store';
 import { connectUserStream } from 'flavours/glitch/actions/streaming';
+import { checkDeprecatedLocalSettings } from 'flavours/glitch/actions/local_settings';
 import { IntlProvider, addLocaleData } from 'react-intl';
 import { getLocale } from 'locales';
 import initialState from 'flavours/glitch/util/initial_state';
@@ -20,6 +21,9 @@ export const store = configureStore();
 const hydrateAction = hydrateStore(initialState);
 store.dispatch(hydrateAction);
 
+// check for deprecated local settings
+store.dispatch(checkDeprecatedLocalSettings());
+
 // load custom emojis
 store.dispatch(fetchCustomEmojis());
 
diff --git a/app/javascript/flavours/glitch/features/getting_started/components/announcements.js b/app/javascript/flavours/glitch/features/getting_started/components/announcements.js
index 2f6d2de5c..f7097e2ec 100644
--- a/app/javascript/flavours/glitch/features/getting_started/components/announcements.js
+++ b/app/javascript/flavours/glitch/features/getting_started/components/announcements.js
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
 import IconButton from 'flavours/glitch/components/icon_button';
 import Icon from 'flavours/glitch/components/icon';
 import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl';
-import { autoPlayGif, reduceMotion } from 'flavours/glitch/util/initial_state';
+import { autoPlayGif, reduceMotion, disableSwiping } from 'flavours/glitch/util/initial_state';
 import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg';
 import { mascot } from 'flavours/glitch/util/initial_state';
 import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light';
@@ -430,6 +430,7 @@ class Announcements extends ImmutablePureComponent {
                 removeReaction={this.props.removeReaction}
                 intl={intl}
                 selected={index === idx}
+                disabled={disableSwiping}
               />
             ))}
           </ReactSwipeableViews>
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js b/app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js
new file mode 100644
index 000000000..362bd97c0
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js
@@ -0,0 +1,83 @@
+//  Package imports
+import React from 'react';
+import PropTypes from 'prop-types';
+
+//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+export default class LocalSettingsPageItem extends React.PureComponent {
+
+  static propTypes = {
+    children: PropTypes.node.isRequired,
+    id: PropTypes.string.isRequired,
+    options: PropTypes.arrayOf(PropTypes.shape({
+      value: PropTypes.string.isRequired,
+      message: PropTypes.string.isRequired,
+      hint: PropTypes.string,
+    })),
+    value: PropTypes.any,
+    placeholder: PropTypes.string,
+  };
+
+  render () {
+    const { id, options, children, placeholder, value } = this.props;
+
+    if (options && options.length > 0) {
+      const currentValue = value;
+      const optionElems = options && options.length > 0 && options.map((opt) => {
+        let optionId = `${id}--${opt.value}`;
+        return (
+          <label htmlFor={optionId}>
+            <input
+              type='radio'
+              name={id}
+              id={optionId}
+              value={opt.value}
+              checked={currentValue === opt.value}
+              disabled
+            />
+            {opt.message}
+            {opt.hint && <span className='hint'>{opt.hint}</span>}
+          </label>
+        );
+      });
+      return (
+        <div className='glitch local-settings__page__item radio_buttons'>
+          <fieldset>
+            <legend>{children}</legend>
+            {optionElems}
+          </fieldset>
+        </div>
+      );
+    } else if (placeholder) {
+      return (
+        <div className='glitch local-settings__page__item string'>
+          <label htmlFor={id}>
+            <p>{children}</p>
+            <p>
+              <input
+                id={id}
+                type='text'
+                value={value}
+                placeholder={placeholder}
+                disabled
+              />
+            </p>
+          </label>
+        </div>
+      );
+    } else return (
+      <div className='glitch local-settings__page__item boolean'>
+        <label htmlFor={id}>
+          <input
+            id={id}
+            type='checkbox'
+            checked={value}
+            disabled
+          />
+          {children}
+        </label>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js
index 45d10d154..4b86a8f6f 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js
@@ -5,7 +5,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
 
 //  Our imports
+import { expandSpoilers, disableSwiping } from 'flavours/glitch/util/initial_state';
+import { preferenceLink } from 'flavours/glitch/util/backend_links';
 import LocalSettingsPageItem from './item';
+import DeprecatedLocalSettingsPageItem from './deprecated_item';
 
 //  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
@@ -146,14 +149,28 @@ class LocalSettingsPage extends React.PureComponent {
           >
             <FormattedMessage id='settings.navbar_under' defaultMessage='Navbar at the bottom (Mobile only)' />
           </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['swipe_to_change_columns']}
+          <DeprecatedLocalSettingsPageItem
             id='mastodon-settings--swipe_to_change_columns'
-            onChange={onChange}
+            value={!disableSwiping}
           >
             <FormattedMessage id='settings.swipe_to_change_columns' defaultMessage='Allow swiping to change columns (Mobile only)' />
-          </LocalSettingsPageItem>
+            <span className='hint'>
+              <FormattedMessage
+                id='settings.deprecated_setting'
+                defaultMessage="This setting is now controlled from Mastodon's {settings_page_link}"
+                values={{
+                  settings_page_link: (
+                    <a href={preferenceLink('user_setting_disable_swiping')}>
+                      <FormattedMessage
+                        id='settings.shared_settings_link'
+                        defaultMessage='user preferences'
+                      />
+                    </a>
+                  )
+                }}
+              />
+            </span>
+          </DeprecatedLocalSettingsPageItem>
         </section>
       </div>
     ),
@@ -242,21 +259,35 @@ class LocalSettingsPage extends React.PureComponent {
     ({ intl, onChange, settings }) => (
       <div className='glitch local-settings__page content_warnings'>
         <h1><FormattedMessage id='settings.content_warnings' defaultMessage='Content warnings' /></h1>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['content_warnings', 'auto_unfold']}
+        <DeprecatedLocalSettingsPageItem
           id='mastodon-settings--content_warnings-auto_unfold'
-          onChange={onChange}
+          value={expandSpoilers}
         >
           <FormattedMessage id='settings.enable_content_warnings_auto_unfold' defaultMessage='Automatically unfold content-warnings' />
-        </LocalSettingsPageItem>
+          <span className='hint'>
+            <FormattedMessage
+              id='settings.deprecated_setting'
+              defaultMessage="This setting is now controlled from Mastodon's {settings_page_link}"
+              values={{
+                settings_page_link: (
+                  <a href={preferenceLink('user_setting_expand_spoilers')}>
+                    <FormattedMessage
+                      id='settings.shared_settings_link'
+                      defaultMessage='user preferences'
+                    />
+                  </a>
+                )
+              }}
+            />
+          </span>
+        </DeprecatedLocalSettingsPageItem>
         <LocalSettingsPageItem
           settings={settings}
           item={['content_warnings', 'filter']}
           id='mastodon-settings--content_warnings-auto_unfold'
           onChange={onChange}
-          dependsOn={[['content_warnings', 'auto_unfold']]}
           placeholder={intl.formatMessage(messages.regexp)}
+          disabled={!expandSpoilers}
         >
           <FormattedMessage id='settings.content_warnings_filter' defaultMessage='Content warnings to not automatically unfold:' />
         </LocalSettingsPageItem>
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/item/index.js b/app/javascript/flavours/glitch/features/local_settings/page/item/index.js
index 5a68523f6..6b24e4143 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/item/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/item/index.js
@@ -21,6 +21,7 @@ export default class LocalSettingsPageItem extends React.PureComponent {
     })),
     settings: ImmutablePropTypes.map.isRequired,
     placeholder: PropTypes.string,
+    disabled: PropTypes.bool,
   };
 
   handleChange = e => {
@@ -33,8 +34,8 @@ export default class LocalSettingsPageItem extends React.PureComponent {
 
   render () {
     const { handleChange } = this;
-    const { settings, item, id, options, children, dependsOn, dependsOnNot, placeholder } = this.props;
-    let enabled = true;
+    const { settings, item, id, options, children, dependsOn, dependsOnNot, placeholder, disabled } = this.props;
+    let enabled = !disabled;
 
     if (dependsOn) {
       for (let i = 0; i < dependsOn.length; i++) {
diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.js b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
index 6cbcd81ee..bfb1ae405 100644
--- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js
+++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
@@ -8,6 +8,8 @@ import ReactSwipeableViews from 'react-swipeable-views';
 import TabsBar, { links, getIndex, getLink } from './tabs_bar';
 import { Link } from 'react-router-dom';
 
+import { disableSwiping } from 'flavours/glitch/util/initial_state';
+
 import BundleContainer from '../containers/bundle_container';
 import ColumnLoading from './column_loading';
 import DrawerLoading from './drawer_loading';
@@ -63,7 +65,6 @@ class ColumnsArea extends ImmutablePureComponent {
   static propTypes = {
     intl: PropTypes.object.isRequired,
     columns: ImmutablePropTypes.list.isRequired,
-    swipeToChangeColumns: PropTypes.bool,
     singleColumn: PropTypes.bool,
     children: PropTypes.node,
     navbarUnder: PropTypes.bool,
@@ -210,7 +211,7 @@ class ColumnsArea extends ImmutablePureComponent {
   }
 
   render () {
-    const { columns, children, singleColumn, swipeToChangeColumns, intl, navbarUnder, openSettings } = this.props;
+    const { columns, children, singleColumn, intl, navbarUnder, openSettings } = this.props;
     const { shouldAnimate, renderComposePanel } = this.state;
 
     const columnIndex = getIndex(this.context.router.history.location.pathname);
@@ -219,7 +220,7 @@ class ColumnsArea extends ImmutablePureComponent {
       const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
 
       const content = columnIndex !== -1 ? (
-        <ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={!swipeToChangeColumns}>
+        <ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={disableSwiping}>
           {links.map(this.renderView)}
         </ReactSwipeableViews>
       ) : (
diff --git a/app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js b/app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js
new file mode 100644
index 000000000..9cb5a30b9
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js
@@ -0,0 +1,86 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import { preferenceLink } from 'flavours/glitch/util/backend_links';
+import Button from 'flavours/glitch/components/button';
+import Icon from 'flavours/glitch/components/icon';
+import illustration from 'flavours/glitch/images/logo_warn_glitch.svg';
+
+const messages = defineMessages({
+  discardChanges: { id: 'confirmations.deprecated_settings.confirm', defaultMessage: 'Use Mastodon preferences' },
+  user_setting_expand_spoilers: { id: 'settings.enable_content_warnings_auto_unfold', defaultMessage: 'Automatically unfold content-warnings' },
+  user_setting_disable_swiping: { id: 'settings.swipe_to_change_columns', defaultMessage: 'Allow swiping to change columns (Mobile only)' },
+});
+
+export default @injectIntl
+class DeprecatedSettingsModal extends React.PureComponent {
+
+  static propTypes = {
+    settings: ImmutablePropTypes.list.isRequired,
+    onClose: PropTypes.func.isRequired,
+    onConfirm: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+  };
+
+  componentDidMount() {
+    this.button.focus();
+  }
+
+  handleClick = () => {
+    this.props.onConfirm();
+    this.props.onClose();
+  }
+
+  setRef = (c) => {
+    this.button = c;
+  }
+
+  render () {
+    const { settings, intl } = this.props;
+
+    return (
+      <div className='modal-root__modal confirmation-modal'>
+        <div className='confirmation-modal__container'>
+
+          <img src={illustration} className='modal-warning' alt='' />
+
+          <FormattedMessage
+            id='confirmations.deprecated_settings.message'
+            defaultMessage='Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:'
+            values={{
+              app_settings: (
+                <strong className='deprecated-settings-label'>
+                  <Icon id='cogs' /> <FormattedMessage id='navigation_bar.app_settings' defaultMessage='App settings' />
+                </strong>
+              ),
+              preferences: (
+                <strong className='deprecated-settings-label'>
+                  <Icon id='cog' /> <FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' />
+                </strong>
+              ),
+            }}
+          />
+
+          <div className='deprecated-settings-info'>
+            <ul>
+              { settings.map((setting_name) => (
+                <li>
+                  <a href={preferenceLink(setting_name)}><FormattedMessage {...messages[setting_name]} /></a>
+                </li>
+              )) }
+            </ul>
+          </div>
+        </div>
+
+        <div>
+          <div className='confirmation-modal__action-bar'>
+            <div />
+            <Button text={intl.formatMessage(messages.discardChanges)} onClick={this.handleClick} ref={this.setRef} />
+          </div>
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/ui/components/media_modal.js b/app/javascript/flavours/glitch/features/ui/components/media_modal.js
index 6974aab26..baa5ff275 100644
--- a/app/javascript/flavours/glitch/features/ui/components/media_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/media_modal.js
@@ -12,6 +12,7 @@ import Icon from 'flavours/glitch/components/icon';
 import GIFV from 'flavours/glitch/components/gifv';
 import Footer from 'flavours/glitch/features/picture_in_picture/components/footer';
 import { getAverageFromBlurhash } from 'flavours/glitch/blurhash';
+import { disableSwiping } from 'flavours/glitch/util/initial_state';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
@@ -227,6 +228,7 @@ class MediaModal extends ImmutablePureComponent {
             onChangeIndex={this.handleSwipe}
             onTransitionEnd={this.handleTransitionEnd}
             index={index}
+            disabled={disableSwiping}
           >
             {content}
           </ReactSwipeableViews>
diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
index a975c4013..8f18d93b7 100644
--- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js
+++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
@@ -14,6 +14,7 @@ import AudioModal from './audio_modal';
 import DoodleModal from './doodle_modal';
 import ConfirmationModal from './confirmation_modal';
 import FocalPointModal from './focal_point_modal';
+import DeprecatedSettingsModal from './deprecated_settings_modal';
 import {
   OnboardingModal,
   MuteModal,
@@ -40,6 +41,7 @@ const MODAL_COMPONENTS = {
   'BLOCK': BlockModal,
   'REPORT': ReportModal,
   'SETTINGS': SettingsModal,
+  'DEPRECATED_SETTINGS': () => Promise.resolve({ default: DeprecatedSettingsModal }),
   'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
   'EMBED': EmbedModal,
   'LIST_EDITOR': ListEditor,
diff --git a/app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js b/app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js
index b69842cd6..1107be740 100644
--- a/app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js
+++ b/app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js
@@ -4,7 +4,6 @@ import { openModal } from 'flavours/glitch/actions/modal';
 
 const mapStateToProps = state => ({
   columns: state.getIn(['settings', 'columns']),
-  swipeToChangeColumns: state.getIn(['local_settings', 'swipe_to_change_columns']),
 });
 
 const mapDispatchToProps = dispatch => ({
diff --git a/app/javascript/flavours/glitch/images/logo_warn_glitch.svg b/app/javascript/flavours/glitch/images/logo_warn_glitch.svg
new file mode 100644
index 000000000..32c5854ee
--- /dev/null
+++ b/app/javascript/flavours/glitch/images/logo_warn_glitch.svg
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   viewBox="0 0 216.41507 232.00976"
+   version="1.1"
+   id="svg6"
+   sodipodi:docname="logo_warn_glitch.svg"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs10" />
+  <sodipodi:namedview
+     id="namedview8"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="1.7951831"
+     inkscape:cx="-30.916067"
+     inkscape:cy="90.241493"
+     inkscape:window-width="1920"
+     inkscape:window-height="1011"
+     inkscape:window-x="0"
+     inkscape:window-y="32"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg6" />
+  <g
+     id="g2025">
+    <path
+       d="M211.80683 139.0875c-3.1825 16.36625-28.4925 34.2775-57.5625 37.74875-15.16 1.80875-30.0825 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.3925 27.9425 21.115.7225 39.91625-5.20625 39.91625-5.20625l.86875 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23183 213.82 1.40558 165.31125.20808 116.09125c-.36375-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67058 3.45375 78.20308.2425 107.86433 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.97625 14.7525 32.97625 65.0825 0 0 .4125 37.13375-4.6 62.915"
+       fill="#3088d4"
+       id="path2" />
+    <path
+       d="m 124.52893,137.75645 c 0,9.01375 -7.30875,16.32125 -16.3225,16.32125 -9.01375,0 -16.32125,-7.3075 -16.32125,-16.32125 0,-9.01375 7.3075,-16.3225 16.32125,-16.3225 9.01375,0 16.3225,7.30875 16.3225,16.3225"
+       fill="#ffffff"
+       id="path4"
+       sodipodi:nodetypes="csssc" />
+    <path
+       id="path1121"
+       d="m 108.20703,25.453125 c -9.013749,0 -16.322264,7.308516 -16.322264,16.322266 0,5.31808 2.555126,37.386806 6.492187,67.763669 4.100497,4.20028 15.890147,3.77063 19.660157,-0.01 3.9367,-30.375272 6.49219,-62.4364 6.49219,-67.753909 0,-9.01375 -7.30852,-16.322266 -16.32227,-16.322266 z"
+       style="fill:#ffffff"
+       sodipodi:nodetypes="ssccsss" />
+  </g>
+</svg>
diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js
index c115cad6b..a16c337fc 100644
--- a/app/javascript/flavours/glitch/reducers/local_settings.js
+++ b/app/javascript/flavours/glitch/reducers/local_settings.js
@@ -3,13 +3,12 @@ import { Map as ImmutableMap } from 'immutable';
 
 //  Our imports.
 import { STORE_HYDRATE } from 'flavours/glitch/actions/store';
-import { LOCAL_SETTING_CHANGE } from 'flavours/glitch/actions/local_settings';
+import { LOCAL_SETTING_CHANGE, LOCAL_SETTING_DELETE } from 'flavours/glitch/actions/local_settings';
 
 const initialState = ImmutableMap({
   layout    : 'auto',
   stretch   : true,
   navbar_under : false,
-  swipe_to_change_columns: true,
   side_arm  : 'none',
   side_arm_reply_mode : 'keep',
   show_reply_count : false,
@@ -26,7 +25,6 @@ const initialState = ImmutableMap({
   tag_misleading_links: true,
   rewrite_mentions: 'no',
   content_warnings : ImmutableMap({
-    auto_unfold : false,
     filter      : null,
   }),
   collapsed : ImmutableMap({
@@ -66,6 +64,8 @@ export default function localSettings(state = initialState, action) {
     return hydrate(state, action.state.get('local_settings'));
   case LOCAL_SETTING_CHANGE:
     return state.setIn(action.key, action.value);
+  case LOCAL_SETTING_DELETE:
+    return state.deleteIn(action.key);
   default:
     return state;
   }
diff --git a/app/javascript/flavours/glitch/styles/components/local_settings.scss b/app/javascript/flavours/glitch/styles/components/local_settings.scss
index 0b7a74575..db2b9f154 100644
--- a/app/javascript/flavours/glitch/styles/components/local_settings.scss
+++ b/app/javascript/flavours/glitch/styles/components/local_settings.scss
@@ -98,6 +98,18 @@
 
 .glitch.local-settings__page__item {
   margin-bottom: 2px;
+
+  .hint a {
+    color: $lighter-text-color;
+    font-weight: 500;
+    text-decoration: underline;
+
+    &:active,
+    &:focus,
+    &:hover {
+      text-decoration: none;
+    }
+  }
 }
 
 .glitch.local-settings__page__item.string,
@@ -120,3 +132,29 @@
     }
   }
 }
+
+.deprecated-settings-label {
+  white-space: nowrap;
+}
+
+.deprecated-settings-info {
+  text-align: start;
+
+  ul {
+    padding: 10px;
+    margin-left: 12px;
+    list-style: disc inside;
+  }
+
+  a {
+    color: $lighter-text-color;
+    font-weight: 500;
+    text-decoration: underline;
+
+    &:active,
+    &:focus,
+    &:hover {
+      text-decoration: none;
+    }
+  }
+}
diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss
index 61c292b19..90e0da02a 100644
--- a/app/javascript/flavours/glitch/styles/components/modal.scss
+++ b/app/javascript/flavours/glitch/styles/components/modal.scss
@@ -1279,3 +1279,10 @@
   pointer-events: auto;
   z-index: 9999;
 }
+
+img.modal-warning {
+  display: block;
+  margin: auto;
+  margin-bottom: 15px;
+  width: 60px;
+}
diff --git a/app/javascript/flavours/glitch/util/backend_links.js b/app/javascript/flavours/glitch/util/backend_links.js
index 2e5111a7f..5b2dd8dbf 100644
--- a/app/javascript/flavours/glitch/util/backend_links.js
+++ b/app/javascript/flavours/glitch/util/backend_links.js
@@ -7,3 +7,12 @@ export const statusAdminLink = (account_id, status_id) => `/admin/accounts/${acc
 export const filterEditLink = (id) => `/filters/${id}/edit`;
 export const relationshipsLink = '/relationships';
 export const securityLink = '/auth/edit';
+export const preferenceLink = (setting_name) => {
+  switch (setting_name) {
+  case 'user_setting_expand_spoilers':
+  case 'user_setting_disable_swiping':
+    return `/settings/preferences/appearance#${setting_name}`;
+  default:
+    return preferencesLink;
+  }
+};
diff --git a/app/javascript/flavours/glitch/util/content_warning.js b/app/javascript/flavours/glitch/util/content_warning.js
index 5e874a49c..baeb97881 100644
--- a/app/javascript/flavours/glitch/util/content_warning.js
+++ b/app/javascript/flavours/glitch/util/content_warning.js
@@ -1,5 +1,7 @@
+import { expandSpoilers } from 'flavours/glitch/util/initial_state';
+
 export function autoUnfoldCW (settings, status) {
-  if (!settings.getIn(['content_warnings', 'auto_unfold'])) {
+  if (!expandSpoilers) {
     return false;
   }
 
diff --git a/app/javascript/flavours/glitch/util/initial_state.js b/app/javascript/flavours/glitch/util/initial_state.js
index 7154e020b..5cbf8f6c3 100644
--- a/app/javascript/flavours/glitch/util/initial_state.js
+++ b/app/javascript/flavours/glitch/util/initial_state.js
@@ -13,8 +13,8 @@ const getMeta = (prop) => initialState && initialState.meta && initialState.meta
 
 export const reduceMotion = getMeta('reduce_motion');
 export const autoPlayGif = getMeta('auto_play_gif');
-export const displaySensitiveMedia = getMeta('display_sensitive_media');
 export const displayMedia = getMeta('display_media') || (getMeta('display_sensitive_media') ? 'show_all' : 'default');
+export const expandSpoilers = getMeta('expand_spoilers');
 export const unfollowModal = getMeta('unfollow_modal');
 export const boostModal = getMeta('boost_modal');
 export const favouriteModal = getMeta('favourite_modal');
@@ -37,5 +37,6 @@ export const useBlurhash = getMeta('use_blurhash');
 export const usePendingItems = getMeta('use_pending_items');
 export const useSystemEmojiFont = getMeta('system_emoji_font');
 export const showTrends = getMeta('trends');
+export const disableSwiping = getMeta('disable_swiping');
 
 export default initialState;