diff options
Diffstat (limited to 'app/javascript/glitch')
12 files changed, 396 insertions, 241 deletions
diff --git a/app/javascript/glitch/components/settings/container.js b/app/javascript/glitch/components/local_settings/container.js index 5dfe228c0..6c202a4e7 100644 --- a/app/javascript/glitch/components/settings/container.js +++ b/app/javascript/glitch/components/local_settings/container.js @@ -6,22 +6,19 @@ import { closeModal } from 'mastodon/actions/modal'; // Our imports // import { changeLocalSetting } from 'glitch/actions/local_settings'; -import Settings from 'glitch/components/settings'; +import LocalSettings from '.'; const mapStateToProps = state => ({ settings: state.get('local_settings'), }); const mapDispatchToProps = dispatch => ({ - toggleSetting (setting, e) { - dispatch(changeLocalSetting(setting, e.target.checked)); - }, - changeSetting (setting, e) { - dispatch(changeLocalSetting(setting, e.target.value)); + onChange (setting, value) { + dispatch(changeLocalSetting(setting, value)); }, onClose () { dispatch(closeModal()); }, }); -export default connect(mapStateToProps, mapDispatchToProps)(Settings); +export default connect(mapStateToProps, mapDispatchToProps)(LocalSettings); diff --git a/app/javascript/glitch/components/local_settings/index.js b/app/javascript/glitch/components/local_settings/index.js new file mode 100644 index 000000000..7f7b93de4 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/index.js @@ -0,0 +1,50 @@ +// Package imports +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; + +// Our imports +import LocalSettingsPage from './page'; +import LocalSettingsNavigation from './navigation'; + +// Stylesheet imports +import './style'; + +export default 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> + ); + } + +} diff --git a/app/javascript/glitch/components/local_settings/navigation/index.js b/app/javascript/glitch/components/local_settings/navigation/index.js new file mode 100644 index 000000000..1f72cc824 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/navigation/index.js @@ -0,0 +1,74 @@ +// Package imports +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, defineMessages } from 'react-intl'; + +// Our imports +import LocalSettingsNavigationItem from './item'; + +// Stylesheet imports +import './style'; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +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/glitch/components/local_settings/navigation/item/index.js b/app/javascript/glitch/components/local_settings/navigation/item/index.js new file mode 100644 index 000000000..1676aa404 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/navigation/item/index.js @@ -0,0 +1,69 @@ +// Package imports +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +// Stylesheet imports +import './style'; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +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/glitch/components/local_settings/navigation/item/style.scss b/app/javascript/glitch/components/local_settings/navigation/item/style.scss new file mode 100644 index 000000000..505c86912 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/navigation/item/style.scss @@ -0,0 +1,27 @@ +@import 'variables'; + +.glitch.local-settings__navigation__item { + display: block; + padding: 15px 20px; + color: inherit; + background: $primary-text-color; + border-bottom: 1px $ui-primary-color solid; + cursor: pointer; + text-decoration: none; + outline: none; + transition: background .3s; + + &:hover { + background: $ui-secondary-color; + } + + &.active { + background: $ui-highlight-color; + color: $primary-text-color; + } + + &.close, &.close:hover { + background: $error-value-color; + color: $primary-text-color; + } +} diff --git a/app/javascript/glitch/components/local_settings/navigation/style.scss b/app/javascript/glitch/components/local_settings/navigation/style.scss new file mode 100644 index 000000000..1cc39e3e9 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/navigation/style.scss @@ -0,0 +1,10 @@ +@import 'variables'; + +.glitch.local-settings__navigation { + background: $primary-text-color; + color: $ui-base-color; + width: 200px; + font-size: 15px; + line-height: 20px; + overflow-y: auto; +} diff --git a/app/javascript/glitch/components/settings/index.js b/app/javascript/glitch/components/local_settings/page/index.js index ab2e0fb87..8635b604f 100644 --- a/app/javascript/glitch/components/settings/index.js +++ b/app/javascript/glitch/components/local_settings/page/index.js @@ -2,14 +2,16 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; +import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; // Our imports -import SettingsItem from './item'; +import LocalSettingsPageItem from './item'; // Stylesheet imports import './style'; +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + const messages = defineMessages({ layout_auto: { id: 'layout.auto', defaultMessage: 'Auto' }, layout_desktop: { id: 'layout.desktop', defaultMessage: 'Desktop' }, @@ -17,27 +19,21 @@ const messages = defineMessages({ }); @injectIntl -export default class Settings extends React.PureComponent { +export default class LocalSettingsPage extends React.PureComponent { static propTypes = { - settings: ImmutablePropTypes.map.isRequired, - toggleSetting: PropTypes.func.isRequired, - changeSetting: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - }; - - state = { - currentIndex: 0, + index : PropTypes.number, + intl : PropTypes.object.isRequired, + onChange : PropTypes.func.isRequired, + settings : ImmutablePropTypes.map.isRequired, }; - General = () => { - const { intl } = this.props; - return ( - <div> + pages = [ + ({ intl, onChange, settings }) => ( + <div className='glitch local-settings__page general'> <h1><FormattedMessage id='settings.general' defaultMessage='General' /></h1> - <SettingsItem - settings={this.props.settings} + <LocalSettingsPageItem + settings={settings} item={['layout']} id='mastodon-settings--layout' options={[ @@ -45,180 +41,135 @@ export default class Settings extends React.PureComponent { { value: 'multiple', message: intl.formatMessage(messages.layout_desktop) }, { value: 'single', message: intl.formatMessage(messages.layout_mobile) }, ]} - onChange={this.props.changeSetting} + onChange={onChange} > <FormattedMessage id='settings.layout' defaultMessage='Layout:' /> - </SettingsItem> - - <SettingsItem - settings={this.props.settings} + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} item={['stretch']} id='mastodon-settings--stretch' - onChange={this.props.toggleSetting} + onChange={onChange} > <FormattedMessage id='settings.wide_view' defaultMessage='Wide view (Desktop mode only)' /> - </SettingsItem> - + </LocalSettingsPageItem> </div> - ); - } - - CollapsedStatuses = () => { - return ( - <div> + ), + ({ onChange, settings }) => ( + <div className='glitch local-settings__page collapsed'> <h1><FormattedMessage id='settings.collapsed_statuses' defaultMessage='Collapsed toots' /></h1> - <SettingsItem - settings={this.props.settings} + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'enabled']} id='mastodon-settings--collapsed-enabled' - onChange={this.props.toggleSetting} + onChange={onChange} > <FormattedMessage id='settings.enable_collapsed' defaultMessage='Enable collapsed toots' /> - </SettingsItem> + </LocalSettingsPageItem> <section> <h2><FormattedMessage id='settings.auto_collapse' defaultMessage='Automatic collapsing' /></h2> - <SettingsItem - settings={this.props.settings} + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'auto', 'all']} id='mastodon-settings--collapsed-auto-all' - onChange={this.props.toggleSetting} + onChange={onChange} dependsOn={[['collapsed', 'enabled']]} > <FormattedMessage id='settings.auto_collapse_all' defaultMessage='Everything' /> - </SettingsItem> - <SettingsItem - settings={this.props.settings} + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'auto', 'notifications']} id='mastodon-settings--collapsed-auto-notifications' - onChange={this.props.toggleSetting} + onChange={onChange} dependsOn={[['collapsed', 'enabled']]} dependsOnNot={[['collapsed', 'auto', 'all']]} > <FormattedMessage id='settings.auto_collapse_notifications' defaultMessage='Notifications' /> - </SettingsItem> - <SettingsItem - settings={this.props.settings} + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'auto', 'lengthy']} id='mastodon-settings--collapsed-auto-lengthy' - onChange={this.props.toggleSetting} + onChange={onChange} dependsOn={[['collapsed', 'enabled']]} dependsOnNot={[['collapsed', 'auto', 'all']]} > <FormattedMessage id='settings.auto_collapse_lengthy' defaultMessage='Lengthy toots' /> - </SettingsItem> - <SettingsItem - settings={this.props.settings} + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'auto', 'replies']} id='mastodon-settings--collapsed-auto-replies' - onChange={this.props.toggleSetting} + onChange={onChange} dependsOn={[['collapsed', 'enabled']]} dependsOnNot={[['collapsed', 'auto', 'all']]} > <FormattedMessage id='settings.auto_collapse_replies' defaultMessage='Replies' /> - </SettingsItem> - <SettingsItem - settings={this.props.settings} + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'auto', 'media']} id='mastodon-settings--collapsed-auto-media' - onChange={this.props.toggleSetting} + onChange={onChange} dependsOn={[['collapsed', 'enabled']]} dependsOnNot={[['collapsed', 'auto', 'all']]} > <FormattedMessage id='settings.auto_collapse_media' defaultMessage='Toots with media' /> - </SettingsItem> + </LocalSettingsPageItem> </section> <section> <h2><FormattedMessage id='settings.image_backgrounds' defaultMessage='Image backgrounds' /></h2> - <SettingsItem - settings={this.props.settings} + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'backgrounds', 'user_backgrounds']} id='mastodon-settings--collapsed-user-backgrouns' - onChange={this.props.toggleSetting} + onChange={onChange} dependsOn={[['collapsed', 'enabled']]} > <FormattedMessage id='settings.image_backgrounds_users' defaultMessage='Give collapsed toots an image background' /> - </SettingsItem> - <SettingsItem - settings={this.props.settings} + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} item={['collapsed', 'backgrounds', 'preview_images']} id='mastodon-settings--collapsed-preview-images' - onChange={this.props.toggleSetting} + onChange={onChange} dependsOn={[['collapsed', 'enabled']]} > <FormattedMessage id='settings.image_backgrounds_media' defaultMessage='Preview collapsed toot media' /> - </SettingsItem> + </LocalSettingsPageItem> </section> </div> - ); - } - - Media = () => { - return ( - <div> + ), + ({ onChange, settings }) => ( + <div className='glitch local-settings__page media'> <h1><FormattedMessage id='settings.media' defaultMessage='Media' /></h1> - <SettingsItem - settings={this.props.settings} + <LocalSettingsPageItem + settings={settings} item={['media', 'letterbox']} id='mastodon-settings--media-letterbox' - onChange={this.props.toggleSetting} + onChange={onChange} > <FormattedMessage id='settings.media_letterbox' defaultMessage='Letterbox media' /> - </SettingsItem> - <SettingsItem - settings={this.props.settings} + </LocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} item={['media', 'fullwidth']} id='mastodon-settings--media-fullwidth' - onChange={this.props.toggleSetting} + onChange={onChange} > <FormattedMessage id='settings.media_fullwidth' defaultMessage='Full-width media previews' /> - </SettingsItem> + </LocalSettingsPageItem> </div> - ); - } - - navigateTo = (e) => - this.setState({ currentIndex: +e.currentTarget.getAttribute('data-mastodon-navigation_index') }); + ), + ]; render () { + const { pages } = this; + const { index, intl, onChange, settings } = this.props; + const CurrentPage = pages[index] || pages[0]; - const { General, CollapsedStatuses, Media, navigateTo } = this; - const { onClose } = this.props; - const { currentIndex } = this.state; - - return ( - <div className='modal-root__modal settings-modal'> - - <nav className='settings-modal__navigation'> - <a onClick={navigateTo} role='button' data-mastodon-navigation_index='0' tabIndex='0' className={`settings-modal__navigation-item${currentIndex === 0 ? ' active' : ''}`}> - <FormattedMessage id='settings.general' defaultMessage='General' /> - </a> - <a onClick={navigateTo} role='button' data-mastodon-navigation_index='1' tabIndex='0' className={`settings-modal__navigation-item${currentIndex === 1 ? ' active' : ''}`}> - <FormattedMessage id='settings.collapsed_statuses' defaultMessage='Collapsed toots' /> - </a> - <a onClick={navigateTo} role='button' data-mastodon-navigation_index='2' tabIndex='0' className={`settings-modal__navigation-item${currentIndex === 2 ? ' active' : ''}`}> - <FormattedMessage id='settings.media' defaultMessage='Media' /> - </a> - <a href='/settings/preferences' className='settings-modal__navigation-item'> - <i className='fa fa-fw fa-cog' /> <FormattedMessage id='settings.preferences' defaultMessage='User preferences' /> - </a> - <a onClick={onClose} role='button' tabIndex='0' className='settings-modal__navigation-close'> - <FormattedMessage id='settings.close' defaultMessage='Close' /> - </a> - - </nav> - - <div className='settings-modal__content'> - { - [ - <General />, - <CollapsedStatuses />, - <Media />, - ][currentIndex] || <General /> - } - </div> - - </div> - ); + return <CurrentPage intl={intl} onChange={onChange} settings={settings} />; } } diff --git a/app/javascript/glitch/components/settings/item.js b/app/javascript/glitch/components/local_settings/page/item/index.js index 4c67cc2ac..326c7eeb0 100644 --- a/app/javascript/glitch/components/settings/item.js +++ b/app/javascript/glitch/components/local_settings/page/item/index.js @@ -1,30 +1,38 @@ -// Package imports // +// Package imports import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -export default class SettingsItem extends React.PureComponent { +// Stylesheet imports +import './style'; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +export default class LocalSettingsPageItem extends React.PureComponent { static propTypes = { - settings: ImmutablePropTypes.map.isRequired, - item: PropTypes.array.isRequired, + 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.object.isRequired, + message: PropTypes.string.isRequired, })), - dependsOn: PropTypes.array, - dependsOnNot: PropTypes.array, - children: PropTypes.element.isRequired, - onChange: PropTypes.func.isRequired, + settings: ImmutablePropTypes.map.isRequired, }; - handleChange = (e) => { - const { item, onChange } = this.props; - onChange(item, e); + 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; @@ -42,38 +50,41 @@ export default class SettingsItem extends React.PureComponent { if (options && options.length > 0) { const currentValue = settings.getIn(item); const optionElems = options && options.length > 0 && options.map((opt) => ( - <option key={opt.value} selected={currentValue === opt.value} value={opt.value} > + <option + key={opt.value} + value={opt.value} + > {opt.message} </option> )); return ( - <label htmlFor={id}> + <label className='glitch local-settings__page__item' htmlFor={id}> <p>{children}</p> <p> <select id={id} disabled={!enabled} - onBlur={this.handleChange} + onBlur={handleChange} + onChange={handleChange} + value={currentValue} > {optionElems} </select> </p> </label> ); - } else { - return ( - <label htmlFor={id}> - <input - id={id} - type='checkbox' - checked={settings.getIn(item)} - onChange={this.handleChange} - disabled={!enabled} - /> - {children} - </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> + ); } } diff --git a/app/javascript/glitch/components/local_settings/page/item/style.scss b/app/javascript/glitch/components/local_settings/page/item/style.scss new file mode 100644 index 000000000..e614030c0 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/page/item/style.scss @@ -0,0 +1,7 @@ +@import 'variables'; + +.glitch.local-settings__page__item { + select { + margin-bottom: 5px; + } +} diff --git a/app/javascript/glitch/components/local_settings/page/style.scss b/app/javascript/glitch/components/local_settings/page/style.scss new file mode 100644 index 000000000..7269056c3 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/page/style.scss @@ -0,0 +1,9 @@ +@import 'variables'; + +.glitch.local-settings__page { + display: block; + flex: auto; + padding: 15px 20px 15px 20px; + width: 360px; + overflow-y: auto; +} diff --git a/app/javascript/glitch/components/local_settings/style.scss b/app/javascript/glitch/components/local_settings/style.scss new file mode 100644 index 000000000..6f7fcbaa4 --- /dev/null +++ b/app/javascript/glitch/components/local_settings/style.scss @@ -0,0 +1,34 @@ +@import 'variables'; + +.glitch.local-settings { + position: relative; + display: flex; + flex-direction: row; + background: $ui-secondary-color; + color: $ui-base-color; + border-radius: 8px; + height: 80vh; + width: 80vw; + max-width: 740px; + max-height: 450px; + overflow: hidden; + + label { + display: block; + } + + h1 { + font-size: 18px; + font-weight: 500; + line-height: 24px; + margin-bottom: 20px; + } + + h2 { + font-size: 15px; + font-weight: 500; + line-height: 20px; + margin-top: 20px; + margin-bottom: 10px; + } +} diff --git a/app/javascript/glitch/components/settings/style.scss b/app/javascript/glitch/components/settings/style.scss deleted file mode 100644 index 48cc37984..000000000 --- a/app/javascript/glitch/components/settings/style.scss +++ /dev/null @@ -1,84 +0,0 @@ -@import 'variables'; - -.settings-modal { - position: relative; - display: flex; - flex-direction: row; - background: $ui-secondary-color; - color: $ui-base-color; - border-radius: 8px; - height: 80vh; - width: 80vw; - max-width: 740px; - max-height: 450px; - overflow: hidden; - - label { - display: block; - } - - h1 { - font-size: 18px; - font-weight: 500; - line-height: 24px; - margin-bottom: 20px; - } - - h2 { - font-size: 15px; - font-weight: 500; - line-height: 20px; - margin-top: 20px; - margin-bottom: 10px; - } -} - -.settings-modal__navigation { - background: $primary-text-color; - color: $ui-base-color; - width: 200px; - font-size: 15px; - line-height: 20px; - overflow-y: auto; - - .settings-modal__navigation-item, .settings-modal__navigation-close { - display: block; - padding: 15px 20px; - cursor: pointer; - outline: none; - text-decoration: none; - } - - .settings-modal__navigation-item { - background: $primary-text-color; - color: inherit; - border-bottom: 1px $ui-primary-color solid; - transition: background .3s; - - &:hover { - background: $ui-secondary-color; - } - - &.active { - background: $ui-highlight-color; - color: $primary-text-color; - } - } - - .settings-modal__navigation-close { - background: $error-value-color; - color: $primary-text-color; - } -} - -.settings-modal__content { - display: block; - flex: auto; - padding: 15px 20px 15px 20px; - width: 360px; - overflow-y: auto; - - select { - margin-bottom: 5px; - } -} \ No newline at end of file |