diff options
Diffstat (limited to 'app/javascript/flavours/glitch/features/local_settings')
5 files changed, 498 insertions, 0 deletions
diff --git a/app/javascript/flavours/glitch/features/local_settings/index.js b/app/javascript/flavours/glitch/features/local_settings/index.js new file mode 100644 index 000000000..4e4605ea9 --- /dev/null +++ b/app/javascript/flavours/glitch/features/local_settings/index.js @@ -0,0 +1,65 @@ +// Package imports. +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; + +// Our imports +import LocalSettingsPage from './page'; +import LocalSettingsNavigation from './navigation'; +import { closeModal } from 'flavours/glitch/actions/modal'; +import { changeLocalSetting } from 'flavours/glitch/actions/local_settings'; + +const mapStateToProps = state => ({ + settings: state.get('local_settings'), +}); + +const mapDispatchToProps = dispatch => ({ + onChange (setting, value) { + dispatch(changeLocalSetting(setting, value)); + }, + onClose () { + dispatch(closeModal()); + }, +}); + +class LocalSettings extends React.PureComponent { + + static propTypes = { + onChange: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + settings: ImmutablePropTypes.map.isRequired, + }; + + state = { + currentIndex: 0, + }; + + navigateTo = (index) => + this.setState({ currentIndex: +index }); + + render () { + + const { navigateTo } = this; + const { onChange, onClose, settings } = this.props; + const { currentIndex } = this.state; + + return ( + <div className='glitch modal-root__modal local-settings'> + <LocalSettingsNavigation + index={currentIndex} + onClose={onClose} + onNavigate={navigateTo} + /> + <LocalSettingsPage + index={currentIndex} + onChange={onChange} + settings={settings} + /> + </div> + ); + } + +} + +export default connect(mapStateToProps, mapDispatchToProps)(LocalSettings); diff --git a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js new file mode 100644 index 000000000..fc2167c0c --- /dev/null +++ b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js @@ -0,0 +1,71 @@ +// Package imports +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, defineMessages } from 'react-intl'; + +// Our imports +import LocalSettingsNavigationItem from './item'; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +const messages = defineMessages({ + general: { id: 'settings.general', defaultMessage: 'General' }, + collapsed: { id: 'settings.collapsed_statuses', defaultMessage: 'Collapsed toots' }, + media: { id: 'settings.media', defaultMessage: 'Media' }, + preferences: { id: 'settings.preferences', defaultMessage: 'Preferences' }, + close: { id: 'settings.close', defaultMessage: 'Close' }, +}); + +@injectIntl +export default class LocalSettingsNavigation extends React.PureComponent { + + static propTypes = { + index : PropTypes.number, + intl : PropTypes.object.isRequired, + onClose : PropTypes.func.isRequired, + onNavigate : PropTypes.func.isRequired, + }; + + render () { + + const { index, intl, onClose, onNavigate } = this.props; + + return ( + <nav className='glitch local-settings__navigation'> + <LocalSettingsNavigationItem + active={index === 0} + index={0} + onNavigate={onNavigate} + title={intl.formatMessage(messages.general)} + /> + <LocalSettingsNavigationItem + active={index === 1} + index={1} + onNavigate={onNavigate} + title={intl.formatMessage(messages.collapsed)} + /> + <LocalSettingsNavigationItem + active={index === 2} + index={2} + onNavigate={onNavigate} + title={intl.formatMessage(messages.media)} + /> + <LocalSettingsNavigationItem + active={index === 3} + href='/settings/preferences' + index={3} + icon='cog' + title={intl.formatMessage(messages.preferences)} + /> + <LocalSettingsNavigationItem + active={index === 4} + className='close' + index={4} + onNavigate={onClose} + title={intl.formatMessage(messages.close)} + /> + </nav> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/local_settings/navigation/item/index.js b/app/javascript/flavours/glitch/features/local_settings/navigation/item/index.js new file mode 100644 index 000000000..b67d479e7 --- /dev/null +++ b/app/javascript/flavours/glitch/features/local_settings/navigation/item/index.js @@ -0,0 +1,66 @@ +// Package imports +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +export default class LocalSettingsPage extends React.PureComponent { + + static propTypes = { + active: PropTypes.bool, + className: PropTypes.string, + href: PropTypes.string, + icon: PropTypes.string, + index: PropTypes.number.isRequired, + onNavigate: PropTypes.func, + title: PropTypes.string, + }; + + handleClick = (e) => { + const { index, onNavigate } = this.props; + if (onNavigate) { + onNavigate(index); + e.preventDefault(); + } + } + + render () { + const { handleClick } = this; + const { + active, + className, + href, + icon, + onNavigate, + title, + } = this.props; + + const finalClassName = classNames('glitch', 'local-settings__navigation__item', { + active, + }, className); + + const iconElem = icon ? <i className={`fa fa-fw fa-${icon}`} /> : null; + + if (href) return ( + <a + href={href} + className={finalClassName} + > + {iconElem} {title} + </a> + ); + else if (onNavigate) return ( + <a + onClick={handleClick} + role='button' + tabIndex='0' + className={finalClassName} + > + {iconElem} {title} + </a> + ); + else return null; + } + +} diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js new file mode 100644 index 000000000..62bf410c6 --- /dev/null +++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js @@ -0,0 +1,209 @@ +// Package imports +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; + +// Our imports +import LocalSettingsPageItem from './item'; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +const messages = defineMessages({ + layout_auto: { id: 'layout.auto', defaultMessage: 'Auto' }, + layout_desktop: { id: 'layout.desktop', defaultMessage: 'Desktop' }, + layout_mobile: { id: 'layout.single', defaultMessage: 'Mobile' }, + side_arm_none: { id: 'settings.side_arm.none', defaultMessage: 'None' }, +}); + +@injectIntl +export default class LocalSettingsPage extends React.PureComponent { + + static propTypes = { + index : PropTypes.number, + intl : PropTypes.object.isRequired, + onChange : PropTypes.func.isRequired, + settings : ImmutablePropTypes.map.isRequired, + }; + + pages = [ + ({ intl, onChange, settings }) => ( + <div className='glitch local-settings__page general'> + <h1><FormattedMessage id='settings.general' defaultMessage='General' /></h1> + <LocalSettingsPageItem + settings={settings} + item={['layout']} + id='mastodon-settings--layout' + options={[ + { value: 'auto', message: intl.formatMessage(messages.layout_auto) }, + { value: 'multiple', message: intl.formatMessage(messages.layout_desktop) }, + { value: 'single', message: intl.formatMessage(messages.layout_mobile) }, + ]} + onChange={onChange} + > + <FormattedMessage id='settings.layout' defaultMessage='Layout:' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['stretch']} + id='mastodon-settings--stretch' + onChange={onChange} + > + <FormattedMessage id='settings.wide_view' defaultMessage='Wide view (Desktop mode only)' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['navbar_under']} + id='mastodon-settings--navbar_under' + onChange={onChange} + > + <FormattedMessage id='settings.navbar_under' defaultMessage='Navbar at the bottom (Mobile only)' /> + </LocalSettingsPageItem> + <section> + <h2><FormattedMessage id='settings.compose_box_opts' defaultMessage='Compose box options' /></h2> + <LocalSettingsPageItem + settings={settings} + item={['side_arm']} + id='mastodon-settings--side_arm' + options={[ + { value: 'none', message: intl.formatMessage(messages.side_arm_none) }, + { value: 'direct', message: intl.formatMessage({ id: 'privacy.direct.short' }) }, + { value: 'private', message: intl.formatMessage({ id: 'privacy.private.short' }) }, + { value: 'unlisted', message: intl.formatMessage({ id: 'privacy.unlisted.short' }) }, + { value: 'public', message: intl.formatMessage({ id: 'privacy.public.short' }) }, + ]} + onChange={onChange} + > + <FormattedMessage id='settings.side_arm' defaultMessage='Secondary toot button:' /> + </LocalSettingsPageItem> + </section> + </div> + ), + ({ onChange, settings }) => ( + <div className='glitch local-settings__page collapsed'> + <h1><FormattedMessage id='settings.collapsed_statuses' defaultMessage='Collapsed toots' /></h1> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'enabled']} + id='mastodon-settings--collapsed-enabled' + onChange={onChange} + > + <FormattedMessage id='settings.enable_collapsed' defaultMessage='Enable collapsed toots' /> + </LocalSettingsPageItem> + <section> + <h2><FormattedMessage id='settings.auto_collapse' defaultMessage='Automatic collapsing' /></h2> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'auto', 'all']} + id='mastodon-settings--collapsed-auto-all' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + > + <FormattedMessage id='settings.auto_collapse_all' defaultMessage='Everything' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'auto', 'notifications']} + id='mastodon-settings--collapsed-auto-notifications' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + dependsOnNot={[['collapsed', 'auto', 'all']]} + > + <FormattedMessage id='settings.auto_collapse_notifications' defaultMessage='Notifications' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'auto', 'lengthy']} + id='mastodon-settings--collapsed-auto-lengthy' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + dependsOnNot={[['collapsed', 'auto', 'all']]} + > + <FormattedMessage id='settings.auto_collapse_lengthy' defaultMessage='Lengthy toots' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'auto', 'reblogs']} + id='mastodon-settings--collapsed-auto-reblogs' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + dependsOnNot={[['collapsed', 'auto', 'all']]} + > + <FormattedMessage id='settings.auto_collapse_reblogs' defaultMessage='Boosts' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'auto', 'replies']} + id='mastodon-settings--collapsed-auto-replies' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + dependsOnNot={[['collapsed', 'auto', 'all']]} + > + <FormattedMessage id='settings.auto_collapse_replies' defaultMessage='Replies' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'auto', 'media']} + id='mastodon-settings--collapsed-auto-media' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + dependsOnNot={[['collapsed', 'auto', 'all']]} + > + <FormattedMessage id='settings.auto_collapse_media' defaultMessage='Toots with media' /> + </LocalSettingsPageItem> + </section> + <section> + <h2><FormattedMessage id='settings.image_backgrounds' defaultMessage='Image backgrounds' /></h2> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'backgrounds', 'user_backgrounds']} + id='mastodon-settings--collapsed-user-backgrouns' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + > + <FormattedMessage id='settings.image_backgrounds_users' defaultMessage='Give collapsed toots an image background' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['collapsed', 'backgrounds', 'preview_images']} + id='mastodon-settings--collapsed-preview-images' + onChange={onChange} + dependsOn={[['collapsed', 'enabled']]} + > + <FormattedMessage id='settings.image_backgrounds_media' defaultMessage='Preview collapsed toot media' /> + </LocalSettingsPageItem> + </section> + </div> + ), + ({ onChange, settings }) => ( + <div className='glitch local-settings__page media'> + <h1><FormattedMessage id='settings.media' defaultMessage='Media' /></h1> + <LocalSettingsPageItem + settings={settings} + item={['media', 'letterbox']} + id='mastodon-settings--media-letterbox' + onChange={onChange} + > + <FormattedMessage id='settings.media_letterbox' defaultMessage='Letterbox media' /> + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['media', 'fullwidth']} + id='mastodon-settings--media-fullwidth' + onChange={onChange} + > + <FormattedMessage id='settings.media_fullwidth' defaultMessage='Full-width media previews' /> + </LocalSettingsPageItem> + </div> + ), + ]; + + render () { + const { pages } = this; + const { index, intl, onChange, settings } = this.props; + const CurrentPage = pages[index] || pages[0]; + + return <CurrentPage intl={intl} onChange={onChange} settings={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 new file mode 100644 index 000000000..66e84dfe1 --- /dev/null +++ b/app/javascript/flavours/glitch/features/local_settings/page/item/index.js @@ -0,0 +1,87 @@ +// Package imports +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +export default class LocalSettingsPageItem extends React.PureComponent { + + static propTypes = { + children: PropTypes.element.isRequired, + dependsOn: PropTypes.array, + dependsOnNot: PropTypes.array, + id: PropTypes.string.isRequired, + item: PropTypes.array.isRequired, + onChange: PropTypes.func.isRequired, + options: PropTypes.arrayOf(PropTypes.shape({ + value: PropTypes.string.isRequired, + message: PropTypes.string.isRequired, + })), + settings: ImmutablePropTypes.map.isRequired, + }; + + handleChange = e => { + const { target } = e; + const { item, onChange, options } = this.props; + if (options && options.length > 0) onChange(item, target.value); + else onChange(item, target.checked); + } + + render () { + const { handleChange } = this; + const { settings, item, id, options, children, dependsOn, dependsOnNot } = this.props; + let enabled = true; + + if (dependsOn) { + for (let i = 0; i < dependsOn.length; i++) { + enabled = enabled && settings.getIn(dependsOn[i]); + } + } + if (dependsOnNot) { + for (let i = 0; i < dependsOnNot.length; i++) { + enabled = enabled && !settings.getIn(dependsOnNot[i]); + } + } + + if (options && options.length > 0) { + const currentValue = settings.getIn(item); + const optionElems = options && options.length > 0 && options.map((opt) => ( + <option + key={opt.value} + value={opt.value} + > + {opt.message} + </option> + )); + return ( + <label className='glitch local-settings__page__item' htmlFor={id}> + <p>{children}</p> + <p> + <select + id={id} + disabled={!enabled} + onBlur={handleChange} + onChange={handleChange} + value={currentValue} + > + {optionElems} + </select> + </p> + </label> + ); + } else return ( + <label className='glitch local-settings__page__item' htmlFor={id}> + <input + id={id} + type='checkbox' + checked={settings.getIn(item)} + onChange={handleChange} + disabled={!enabled} + /> + {children} + </label> + ); + } + +} |