From e6af69d56b7ac748fc8debcc95e7f75ae6ef3aa8 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 15 May 2022 17:30:40 +0200 Subject: [PATCH] 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 --- .../glitch/actions/importer/normalizer.js | 1 - .../flavours/glitch/actions/local_settings.js | 53 ++++++++++++ .../flavours/glitch/components/modal_root.js | 4 + .../flavours/glitch/containers/mastodon.js | 4 + .../components/announcements.js | 3 +- .../page/deprecated_item/index.js | 83 ++++++++++++++++++ .../features/local_settings/page/index.js | 53 +++++++++--- .../local_settings/page/item/index.js | 5 +- .../features/ui/components/columns_area.js | 7 +- .../components/deprecated_settings_modal.js | 86 +++++++++++++++++++ .../features/ui/components/media_modal.js | 2 + .../features/ui/components/modal_root.js | 2 + .../ui/containers/columns_area_container.js | 1 - .../glitch/images/logo_warn_glitch.svg | 49 +++++++++++ .../glitch/reducers/local_settings.js | 6 +- .../styles/components/local_settings.scss | 38 ++++++++ .../glitch/styles/components/modal.scss | 7 ++ .../flavours/glitch/util/backend_links.js | 9 ++ .../flavours/glitch/util/content_warning.js | 4 +- .../flavours/glitch/util/initial_state.js | 3 +- 20 files changed, 396 insertions(+), 24 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js create mode 100644 app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js create mode 100644 app/javascript/flavours/glitch/images/logo_warn_glitch.svg diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js index bda15a9b08..c38af196af 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 28660a4e82..856674eb3c 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 0595f6a0e5..056277447a 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 de8ea8ee2f..989e37024a 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 2f6d2de5cd..f7097e2ec1 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} /> ))} 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 0000000000..362bd97c0a --- /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 ( + + ); + }); + return ( +
+
+ {children} + {optionElems} +
+
+ ); + } else if (placeholder) { + return ( +
+ +
+ ); + } else return ( +
+ +
+ ); + } + +} 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 45d10d154e..4b86a8f6f1 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 { > - - + + + + + ) + }} + /> + + ), @@ -242,21 +259,35 @@ class LocalSettingsPage extends React.PureComponent { ({ intl, onChange, settings }) => (

- - + + + + + ) + }} + /> + + 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 5a68523f62..6b24e41435 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 6cbcd81ee0..bfb1ae4057 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 : ; const content = columnIndex !== -1 ? ( - + {links.map(this.renderView)} ) : ( 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 0000000000..9cb5a30b91 --- /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 ( +
+
+ + + + + + + ), + preferences: ( + + + + ), + }} + /> + +
+
    + { settings.map((setting_name) => ( +
  • + +
  • + )) } +
+
+
+ +
+
+
+
+
+
+ ); + } + +} 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 6974aab26c..baa5ff2756 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} 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 a975c4013d..8f18d93b73 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 b69842cd6b..1107be740d 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 0000000000..32c5854eed --- /dev/null +++ b/app/javascript/flavours/glitch/images/logo_warn_glitch.svg @@ -0,0 +1,49 @@ + + + + + + + + + + diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js index c115cad6ba..a16c337fca 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 0b7a74575c..db2b9f154e 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 61c292b193..90e0da02a9 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 2e5111a7f4..5b2dd8dbff 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 5e874a49c0..baeb97881f 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 7154e020be..5cbf8f6c36 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;