diff options
Diffstat (limited to 'app/javascript/flavours/glitch/features')
12 files changed, 226 insertions, 84 deletions
diff --git a/app/javascript/flavours/glitch/features/drawer/results/index.js b/app/javascript/flavours/glitch/features/drawer/results/index.js index 23dc0e3cf..ac7a14ef4 100644 --- a/app/javascript/flavours/glitch/features/drawer/results/index.js +++ b/app/javascript/flavours/glitch/features/drawer/results/index.js @@ -12,6 +12,7 @@ import { Link } from 'react-router-dom'; // Components. import AccountContainer from 'flavours/glitch/containers/account_container'; import StatusContainer from 'flavours/glitch/containers/status_container'; +import Hashtag from 'flavours/glitch/components/hashtag'; // Utils. import Motion from 'flavours/glitch/util/optional_motion'; @@ -98,15 +99,7 @@ export default function DrawerResults ({ <section> <h5><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5> - {hashtags.map( - hashtag => ( - <Link - className='hashtag' - key={hashtag} - to={`/timelines/tag/${hashtag}`} - >#{hashtag}</Link> - ) - )} + {hashtags.map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} </section> ) : null} </div> diff --git a/app/javascript/flavours/glitch/features/getting_started_misc/index.js b/app/javascript/flavours/glitch/features/getting_started_misc/index.js index b67e6f97f..ee4452472 100644 --- a/app/javascript/flavours/glitch/features/getting_started_misc/index.js +++ b/app/javascript/flavours/glitch/features/getting_started_misc/index.js @@ -21,6 +21,7 @@ const messages = defineMessages({ pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' }, info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }, keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Keyboard shortcuts' }, + featured_users: { id: 'navigation_bar.featured_users', defaultMessage: 'Featured users' }, }); @connect() @@ -33,27 +34,33 @@ export default class gettingStartedMisc extends ImmutablePureComponent { }; openOnboardingModal = (e) => { - e.preventDefault(); this.props.dispatch(openModal('ONBOARDING')); } + openFeaturedAccountsModal = (e) => { + this.props.dispatch(openModal('PINNED_ACCOUNTS_EDITOR')); + } + render () { const { intl } = this.props; + let i = 1; + return ( <Column icon='ellipsis-h' heading={intl.formatMessage(messages.heading)}> <ColumnBackButtonSlim /> <div className='scrollable'> <ColumnSubheading text={intl.formatMessage(messages.subheading)} /> - <ColumnLink key='19' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' /> - <ColumnLink key='20' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' /> - <ColumnLink key='21' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' /> - <ColumnLink key='22' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' /> - <ColumnLink icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' /> - <ColumnLink key='23' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' /> - <ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' /> - <ColumnLink icon='hand-o-right' text={intl.formatMessage(messages.show_me_around)} onClick={this.openOnboardingModal} /> + <ColumnLink key='{i++}' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' /> + <ColumnLink key='{i++}' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' /> + <ColumnLink key='{i++}' icon='users' text={intl.formatMessage(messages.featured_users)} onClick={this.openFeaturedAccountsModal} /> + <ColumnLink key='{i++}' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' /> + <ColumnLink key='{i++}' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' /> + <ColumnLink key='{i++}' icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' /> + <ColumnLink key='{i++}' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' /> + <ColumnLink key='{i++}' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' /> + <ColumnLink key='{i++}' icon='hand-o-right' text={intl.formatMessage(messages.show_me_around)} onClick={this.openOnboardingModal} /> </div> </Column> ); diff --git a/app/javascript/flavours/glitch/features/list_editor/components/account.js b/app/javascript/flavours/glitch/features/list_editor/components/account.js index f48df759d..71a8b7673 100644 --- a/app/javascript/flavours/glitch/features/list_editor/components/account.js +++ b/app/javascript/flavours/glitch/features/list_editor/components/account.js @@ -1,38 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { makeGetAccount } from 'flavours/glitch/selectors'; import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Avatar from 'flavours/glitch/components/avatar'; import DisplayName from 'flavours/glitch/components/display_name'; import IconButton from 'flavours/glitch/components/icon_button'; -import { defineMessages, injectIntl } from 'react-intl'; -import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists'; +import { defineMessages } from 'react-intl'; const messages = defineMessages({ remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' }, add: { id: 'lists.account.add', defaultMessage: 'Add to list' }, }); -const makeMapStateToProps = () => { - const getAccount = makeGetAccount(); - - const mapStateToProps = (state, { accountId, added }) => ({ - account: getAccount(state, accountId), - added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added, - }); - - return mapStateToProps; -}; - -const mapDispatchToProps = (dispatch, { accountId }) => ({ - onRemove: () => dispatch(removeFromListEditor(accountId)), - onAdd: () => dispatch(addToListEditor(accountId)), -}); - -@connect(makeMapStateToProps, mapDispatchToProps) -@injectIntl export default class Account extends ImmutablePureComponent { static propTypes = { diff --git a/app/javascript/flavours/glitch/features/list_editor/components/search.js b/app/javascript/flavours/glitch/features/list_editor/components/search.js index 45c4d0f2e..280632652 100644 --- a/app/javascript/flavours/glitch/features/list_editor/components/search.js +++ b/app/javascript/flavours/glitch/features/list_editor/components/search.js @@ -1,26 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { defineMessages, injectIntl } from 'react-intl'; -import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; +import { defineMessages } from 'react-intl'; import classNames from 'classnames'; const messages = defineMessages({ search: { id: 'lists.search', defaultMessage: 'Search among people you follow' }, }); -const mapStateToProps = state => ({ - value: state.getIn(['listEditor', 'suggestions', 'value']), -}); - -const mapDispatchToProps = dispatch => ({ - onSubmit: value => dispatch(fetchListSuggestions(value)), - onClear: () => dispatch(clearListSuggestions()), - onChange: value => dispatch(changeListSuggestions(value)), -}); - -@connect(mapStateToProps, mapDispatchToProps) -@injectIntl export default class Search extends React.PureComponent { static propTypes = { diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js new file mode 100644 index 000000000..782eb42f3 --- /dev/null +++ b/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { makeGetAccount } from 'flavours/glitch/selectors'; +import { injectIntl } from 'react-intl'; +import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists'; +import Account from '../components/account'; + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, { accountId, added }) => ({ + account: getAccount(state, accountId), + added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added, + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { accountId }) => ({ + onRemove: () => dispatch(removeFromListEditor(accountId)), + onAdd: () => dispatch(addToListEditor(accountId)), +}); + +export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account)); diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js new file mode 100644 index 000000000..5af20efbd --- /dev/null +++ b/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { injectIntl } from 'react-intl'; +import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; +import Search from '../components/search'; + +const mapStateToProps = state => ({ + value: state.getIn(['listEditor', 'suggestions', 'value']), +}); + +const mapDispatchToProps = dispatch => ({ + onSubmit: value => dispatch(fetchListSuggestions(value)), + onClear: () => dispatch(clearListSuggestions()), + onChange: value => dispatch(changeListSuggestions(value)), +}); + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search)); diff --git a/app/javascript/flavours/glitch/features/list_editor/index.js b/app/javascript/flavours/glitch/features/list_editor/index.js index e6df4755a..b3be3070a 100644 --- a/app/javascript/flavours/glitch/features/list_editor/index.js +++ b/app/javascript/flavours/glitch/features/list_editor/index.js @@ -5,8 +5,8 @@ import { connect } from 'react-redux'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { injectIntl } from 'react-intl'; import { setupListEditor, clearListSuggestions, resetListEditor } from 'flavours/glitch/actions/lists'; -import Account from './components/account'; -import Search from './components/search'; +import AccountContainer from './containers/account_container'; +import SearchContainer from './containers/search_container'; import Motion from 'flavours/glitch/util/optional_motion'; import spring from 'react-motion/lib/spring'; @@ -56,11 +56,11 @@ export default class ListEditor extends ImmutablePureComponent { <div className='modal-root__modal list-editor'> <h4>{title}</h4> - <Search /> + <SearchContainer /> <div className='drawer__pager'> <div className='drawer__inner list-editor__accounts'> - {accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)} + {accountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} added />)} </div> {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />} @@ -68,7 +68,7 @@ export default class ListEditor extends ImmutablePureComponent { <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> {({ x }) => (<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> - {searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)} + {searchAccountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} />)} </div>) } </Motion> 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 d3b7b00b5..f88e23c47 100644 --- a/app/javascript/flavours/glitch/features/local_settings/page/index.js +++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js @@ -35,34 +35,45 @@ export default class LocalSettingsPage extends React.PureComponent { <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) }, - ]} + item={['show_reply_count']} + id='mastodon-settings--reply-count' 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)' /> + <FormattedMessage id='settings.show_reply_counter' defaultMessage='Display an estimate of the reply count' /> </LocalSettingsPageItem> <section> + <h2><FormattedMessage id='settings.layout_opts' defaultMessage='Layout options' /></h2> + <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> + <section> <h2><FormattedMessage id='settings.compose_box_opts' defaultMessage='Compose box options' /></h2> <LocalSettingsPageItem settings={settings} diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js new file mode 100644 index 000000000..149d05c32 --- /dev/null +++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { makeGetAccount } from 'flavours/glitch/selectors'; +import { injectIntl } from 'react-intl'; +import { pinAccount, unpinAccount } from 'flavours/glitch/actions/accounts'; +import Account from 'flavours/glitch/features/list_editor/components/account'; + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, { accountId, added }) => ({ + account: getAccount(state, accountId), + added: typeof added === 'undefined' ? state.getIn(['pinnedAccountsEditor', 'accounts', 'items']).includes(accountId) : added, + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { accountId }) => ({ + onRemove: () => dispatch(unpinAccount(accountId)), + onAdd: () => dispatch(pinAccount(accountId)), +}); + +export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account)); diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js new file mode 100644 index 000000000..5a1efce0a --- /dev/null +++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { injectIntl } from 'react-intl'; +import { + fetchPinnedAccountsSuggestions, + clearPinnedAccountsSuggestions, + changePinnedAccountsSuggestions +} from '../../../actions/accounts'; +import Search from 'flavours/glitch/features/list_editor/components/search'; + +const mapStateToProps = state => ({ + value: state.getIn(['pinnedAccountsEditor', 'suggestions', 'value']), +}); + +const mapDispatchToProps = dispatch => ({ + onSubmit: value => dispatch(fetchPinnedAccountsSuggestions(value)), + onClear: () => dispatch(clearPinnedAccountsSuggestions()), + onChange: value => dispatch(changePinnedAccountsSuggestions(value)), +}); + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search)); diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js new file mode 100644 index 000000000..7484e458e --- /dev/null +++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js @@ -0,0 +1,78 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { injectIntl, FormattedMessage } from 'react-intl'; +import { fetchPinnedAccounts, clearPinnedAccountsSuggestions, resetPinnedAccountsEditor } from 'flavours/glitch/actions/accounts'; +import AccountContainer from './containers/account_container'; +import SearchContainer from './containers/search_container'; +import Motion from 'flavours/glitch/util/optional_motion'; +import spring from 'react-motion/lib/spring'; + +const mapStateToProps = state => ({ + accountIds: state.getIn(['pinnedAccountsEditor', 'accounts', 'items']), + searchAccountIds: state.getIn(['pinnedAccountsEditor', 'suggestions', 'items']), +}); + +const mapDispatchToProps = dispatch => ({ + onInitialize: () => dispatch(fetchPinnedAccounts()), + onClear: () => dispatch(clearPinnedAccountsSuggestions()), + onReset: () => dispatch(resetPinnedAccountsEditor()), +}); + +@connect(mapStateToProps, mapDispatchToProps) +@injectIntl +export default class PinnedAccountsEditor extends ImmutablePureComponent { + + static propTypes = { + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + onInitialize: PropTypes.func.isRequired, + onClear: PropTypes.func.isRequired, + onReset: PropTypes.func.isRequired, + title: PropTypes.string.isRequired, + accountIds: ImmutablePropTypes.list.isRequired, + searchAccountIds: ImmutablePropTypes.list.isRequired, + }; + + componentDidMount () { + const { onInitialize } = this.props; + onInitialize(); + } + + componentWillUnmount () { + const { onReset } = this.props; + onReset(); + } + + render () { + const { accountIds, searchAccountIds, onClear } = this.props; + const showSearch = searchAccountIds.size > 0; + + return ( + <div className='modal-root__modal list-editor'> + <h4><FormattedMessage id='endorsed_accounts_editor.endorsed_accounts' defaultMessage='Featured accounts' /></h4> + + <SearchContainer /> + + <div className='drawer__pager'> + <div className='drawer__inner list-editor__accounts'> + {accountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} added />)} + </div> + + {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />} + + <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> + {({ x }) => + (<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> + {searchAccountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} />)} + </div>) + } + </Motion> + </div> + </div> + ); + } + +} 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 23a7603d8..c9f54804a 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -19,6 +19,7 @@ import { SettingsModal, EmbedModal, ListEditor, + PinnedAccountsEditor, } from 'flavours/glitch/util/async-components'; const MODAL_COMPONENTS = { @@ -36,6 +37,7 @@ const MODAL_COMPONENTS = { 'EMBED': EmbedModal, 'LIST_EDITOR': ListEditor, 'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }), + 'PINNED_ACCOUNTS_EDITOR': PinnedAccountsEditor, }; export default class ModalRoot extends React.PureComponent { |