diff options
Diffstat (limited to 'app/javascript/flavours/glitch/features')
10 files changed, 116 insertions, 148 deletions
diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.js b/app/javascript/flavours/glitch/features/compose/components/compose_form.js index c75906ce7..b03bc34b8 100644 --- a/app/javascript/flavours/glitch/features/compose/components/compose_form.js +++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.js @@ -246,9 +246,14 @@ class ComposeForm extends ImmutablePureComponent { selectionStart = selectionEnd = text.length; } if (textarea) { - textarea.setSelectionRange(selectionStart, selectionEnd); - textarea.focus(); - if (!singleColumn) textarea.scrollIntoView(); + // Because of the wicg-inert polyfill, the activeElement may not be + // immediately selectable, we have to wait for observers to run, as + // described in https://github.com/WICG/inert#performance-and-gotchas + Promise.resolve().then(() => { + textarea.setSelectionRange(selectionStart, selectionEnd); + textarea.focus(); + if (!singleColumn) textarea.scrollIntoView(); + }).catch(console.error); } // Refocuses the textarea after submitting. diff --git a/app/javascript/flavours/glitch/features/directory/components/account_card.js b/app/javascript/flavours/glitch/features/directory/components/account_card.js index 2a3fd1ecf..c9ef5850c 100644 --- a/app/javascript/flavours/glitch/features/directory/components/account_card.js +++ b/app/javascript/flavours/glitch/features/directory/components/account_card.js @@ -7,31 +7,28 @@ import { makeGetAccount } from 'flavours/glitch/selectors'; import Avatar from 'flavours/glitch/components/avatar'; import DisplayName from 'flavours/glitch/components/display_name'; import Permalink from 'flavours/glitch/components/permalink'; -import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp'; -import IconButton from 'flavours/glitch/components/icon_button'; +import Button from 'flavours/glitch/components/button'; import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import { autoPlayGif, me, unfollowModal } from 'flavours/glitch/util/initial_state'; import ShortNumber from 'flavours/glitch/components/short_number'; import { followAccount, unfollowAccount, - blockAccount, unblockAccount, unmuteAccount, } from 'flavours/glitch/actions/accounts'; import { openModal } from 'flavours/glitch/actions/modal'; -import { initMuteModal } from 'flavours/glitch/actions/mutes'; +import classNames from 'classnames'; const messages = defineMessages({ - follow: { id: 'account.follow', defaultMessage: 'Follow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, - requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' }, - unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, - unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, - unfollowConfirm: { - id: 'confirmations.unfollow.confirm', - defaultMessage: 'Unfollow', - }, + follow: { id: 'account.follow', defaultMessage: 'Follow' }, + cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Cancel follow request' }, + requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, + unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' }, + unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' }, + unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' }, + edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, }); const makeMapStateToProps = () => { @@ -75,18 +72,15 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onBlock(account) { if (account.getIn(['relationship', 'blocking'])) { dispatch(unblockAccount(account.get('id'))); - } else { - dispatch(blockAccount(account.get('id'))); } }, onMute(account) { if (account.getIn(['relationship', 'muting'])) { dispatch(unmuteAccount(account.get('id'))); - } else { - dispatch(initMuteModal(account)); } }, + }); export default @@ -138,130 +132,92 @@ class AccountCard extends ImmutablePureComponent { handleMute = () => { this.props.onMute(this.props.account); - }; + } + + handleEditProfile = () => { + window.open('/settings/profile', '_blank'); + } render() { const { account, intl } = this.props; - let buttons; - - if ( - account.get('id') !== me && - account.get('relationship', null) !== null - ) { - const following = account.getIn(['relationship', 'following']); - const requested = account.getIn(['relationship', 'requested']); - const blocking = account.getIn(['relationship', 'blocking']); - const muting = account.getIn(['relationship', 'muting']); - - if (requested) { - buttons = ( - <IconButton - disabled - icon='hourglass' - title={intl.formatMessage(messages.requested)} - /> - ); - } else if (blocking) { - buttons = ( - <IconButton - active - icon='unlock' - title={intl.formatMessage(messages.unblock, { - name: account.get('username'), - })} - onClick={this.handleBlock} - /> - ); - } else if (muting) { - buttons = ( - <IconButton - active - icon='volume-up' - title={intl.formatMessage(messages.unmute, { - name: account.get('username'), - })} - onClick={this.handleMute} - /> - ); - } else if (!account.get('moved') || following) { - buttons = ( - <IconButton - icon={following ? 'user-times' : 'user-plus'} - title={intl.formatMessage( - following ? messages.unfollow : messages.follow, - )} - onClick={this.handleFollow} - active={following} - /> - ); + let actionBtn; + + if (me !== account.get('id')) { + if (!account.get('relationship')) { // Wait until the relationship is loaded + actionBtn = ''; + } else if (account.getIn(['relationship', 'requested'])) { + actionBtn = <Button className={classNames('logo-button')} text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.handleFollow} />; + } else if (account.getIn(['relationship', 'muting'])) { + actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unmute)} onClick={this.handleMute} />; + } else if (!account.getIn(['relationship', 'blocking'])) { + actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.handleFollow} />; + } else if (account.getIn(['relationship', 'blocking'])) { + actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock)} onClick={this.handleBlock} />; } + } else { + actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.edit_profile)} onClick={this.handleEditProfile} />; } return ( - <div className='directory__card'> - <div className='directory__card__img'> - <img - src={ - autoPlayGif ? account.get('header') : account.get('header_static') - } - alt='' - /> - </div> + <div className='account-card'> + <Permalink href={account.get('url')} to={`/@${account.get('acct')}`} className='account-card__permalink'> + <div className='account-card__header'> + <img + src={ + autoPlayGif ? account.get('header') : account.get('header_static') + } + alt='' + /> + </div> - <div className='directory__card__bar'> - <Permalink - className='directory__card__bar__name' - href={account.get('url')} - to={`/@${account.get('acct')}`} - > - <Avatar account={account} size={48} /> + <div className='account-card__title'> + <div className='account-card__title__avatar'><Avatar account={account} size={56} /></div> <DisplayName account={account} /> - </Permalink> - - <div className='directory__card__bar__relationship account__relationship'> - {buttons} </div> - </div> + </Permalink> - <div className='directory__card__extra' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> + {account.get('note').length > 0 && ( <div - className='account__header__content translate' + className='account-card__bio translate' + onMouseEnter={this.handleMouseEnter} + onMouseLeave={this.handleMouseLeave} dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }} /> - </div> - - <div className='directory__card__extra'> - <div className='accounts-table__count'> - <ShortNumber value={account.get('statuses_count')} /> - <small> - <FormattedMessage id='account.posts' defaultMessage='Toots' /> - </small> + )} + + <div className='account-card__actions'> + <div className='account-card__counters'> + <div className='account-card__counters__item'> + <ShortNumber value={account.get('statuses_count')} /> + <small> + <FormattedMessage id='account.posts' defaultMessage='Toots' /> + </small> + </div> + + <div className='account-card__counters__item'> + {account.get('followers_count') < 0 ? '-' : <ShortNumber value={account.get('followers_count')} />}{' '} + <small> + <FormattedMessage + id='account.followers' + defaultMessage='Followers' + /> + </small> + </div> + + <div className='account-card__counters__item'> + <ShortNumber value={account.get('following_count')} />{' '} + <small> + <FormattedMessage + id='account.following' + defaultMessage='Following' + /> + </small> + </div> </div> - <div className='accounts-table__count'> - {account.get('followers_count') < 0 ? '-' : <ShortNumber value={account.get('followers_count')} />}{' '} - <small> - <FormattedMessage - id='account.followers' - defaultMessage='Followers' - /> - </small> - </div> - <div className='accounts-table__count'> - {account.get('last_status_at') === null ? ( - <FormattedMessage - id='account.never_active' - defaultMessage='Never' - /> - ) : ( - <RelativeTimestamp timestamp={account.get('last_status_at')} /> - )}{' '} - <small> - <FormattedMessage - id='account.last_status' - defaultMessage='Last active' - /> - </small> + + <div className='account-card__actions__button'> + {actionBtn} </div> </div> </div> diff --git a/app/javascript/flavours/glitch/features/directory/index.js b/app/javascript/flavours/glitch/features/directory/index.js index cde5926e0..87d9b3625 100644 --- a/app/javascript/flavours/glitch/features/directory/index.js +++ b/app/javascript/flavours/glitch/features/directory/index.js @@ -10,9 +10,9 @@ import { fetchDirectory, expandDirectory } from 'flavours/glitch/actions/directo import { List as ImmutableList } from 'immutable'; import AccountCard from './components/account_card'; import RadioButton from 'flavours/glitch/components/radio_button'; -import classNames from 'classnames'; import LoadMore from 'flavours/glitch/components/load_more'; import ScrollContainer from 'flavours/glitch/containers/scroll_container'; +import LoadingIndicator from 'flavours/glitch/components/loading_indicator'; const messages = defineMessages({ title: { id: 'column.directory', defaultMessage: 'Browse profiles' }, @@ -129,7 +129,7 @@ class Directory extends React.PureComponent { const pinned = !!columnId; const scrollableArea = ( - <div className='scrollable' style={{ background: 'transparent' }}> + <div className='scrollable'> <div className='filter-form'> <div className='filter-form__column' role='group'> <RadioButton name='order' value='active' label={intl.formatMessage(messages.recentlyActive)} checked={order === 'active'} onChange={this.handleChangeOrder} /> @@ -142,8 +142,10 @@ class Directory extends React.PureComponent { </div> </div> - <div className={classNames('directory__list', { loading: isLoading })}> - {accountIds.map(accountId => <AccountCard id={accountId} key={accountId} />)} + <div className='directory__list'> + {isLoading ? <LoadingIndicator /> : accountIds.map(accountId => ( + <AccountCard id={accountId} key={accountId} /> + ))} </div> <LoadMore onClick={this.handleLoadMore} visible={!isLoading} /> diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js index e01d277a1..0408105ae 100644 --- a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js +++ b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js @@ -62,7 +62,7 @@ class Footer extends ImmutablePureComponent { const { router } = this.context; if (onClose) { - onClose(); + onClose(true); } dispatch(replyCompose(status, router.history)); @@ -181,7 +181,7 @@ class Footer extends ImmutablePureComponent { {replyButton} <IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={status.get('reblogs_count')} /> <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} /> - {withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} />} + {withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} href={status.get('url')} />} </div> ); } diff --git a/app/javascript/flavours/glitch/features/report/category.js b/app/javascript/flavours/glitch/features/report/category.js index ddbc82563..cf63533d0 100644 --- a/app/javascript/flavours/glitch/features/report/category.js +++ b/app/javascript/flavours/glitch/features/report/category.js @@ -8,7 +8,7 @@ const messages = defineMessages({ dislike: { id: 'report.reasons.dislike', defaultMessage: 'I don\'t like it' }, dislike_description: { id: 'report.reasons.dislike_description', defaultMessage: 'It is not something you want to see' }, spam: { id: 'report.reasons.spam', defaultMessage: 'It\'s spam' }, - spam_description: { id: 'report.reasons.spam_description', defaultMessage: 'Malicious links, fake engagement, or repetetive replies' }, + spam_description: { id: 'report.reasons.spam_description', defaultMessage: 'Malicious links, fake engagement, or repetitive replies' }, violation: { id: 'report.reasons.violation', defaultMessage: 'It violates server rules' }, violation_description: { id: 'report.reasons.violation_description', defaultMessage: 'You are aware that it breaks specific rules' }, other: { id: 'report.reasons.other', defaultMessage: 'It\'s something else' }, diff --git a/app/javascript/flavours/glitch/features/report/comment.js b/app/javascript/flavours/glitch/features/report/comment.js index b2663bbf2..ec261afcb 100644 --- a/app/javascript/flavours/glitch/features/report/comment.js +++ b/app/javascript/flavours/glitch/features/report/comment.js @@ -74,7 +74,7 @@ class Comment extends React.PureComponent { <div className='flex-spacer' /> <div className='report-dialog-modal__actions'> - <Button onClick={this.handleClick}><FormattedMessage id='report.submit' defaultMessage='Submit report' /></Button> + <Button onClick={this.handleClick} disabled={isSubmitting}><FormattedMessage id='report.submit' defaultMessage='Submit report' /></Button> </div> </React.Fragment> ); diff --git a/app/javascript/flavours/glitch/features/report/statuses.js b/app/javascript/flavours/glitch/features/report/statuses.js index 69cfbb3e5..47d5ee863 100644 --- a/app/javascript/flavours/glitch/features/report/statuses.js +++ b/app/javascript/flavours/glitch/features/report/statuses.js @@ -6,9 +6,11 @@ import StatusCheckBox from 'flavours/glitch/features/report/containers/status_ch import { OrderedSet } from 'immutable'; import { FormattedMessage } from 'react-intl'; import Button from 'flavours/glitch/components/button'; +import LoadingIndicator from 'flavours/glitch/components/loading_indicator'; const mapStateToProps = (state, { accountId }) => ({ availableStatusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}:with_replies`, 'items'])), + isLoading: state.getIn(['timelines', `account:${accountId}:with_replies`, 'isLoading']), }); export default @connect(mapStateToProps) @@ -19,6 +21,7 @@ class Statuses extends React.PureComponent { accountId: PropTypes.string.isRequired, availableStatusIds: ImmutablePropTypes.set.isRequired, selectedStatusIds: ImmutablePropTypes.set.isRequired, + isLoading: PropTypes.bool, onToggle: PropTypes.func.isRequired, }; @@ -28,7 +31,7 @@ class Statuses extends React.PureComponent { }; render () { - const { availableStatusIds, selectedStatusIds, onToggle } = this.props; + const { availableStatusIds, selectedStatusIds, onToggle, isLoading } = this.props; return ( <React.Fragment> @@ -36,7 +39,7 @@ class Statuses extends React.PureComponent { <p className='report-dialog-modal__lead'><FormattedMessage id='report.statuses.subtitle' defaultMessage='Select all that apply' /></p> <div className='report-dialog-modal__statuses'> - {availableStatusIds.union(selectedStatusIds).map(statusId => ( + {isLoading ? <LoadingIndicator /> : availableStatusIds.union(selectedStatusIds).map(statusId => ( <StatusCheckBox id={statusId} key={statusId} 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 1e065c171..a975c4013 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -55,6 +55,7 @@ export default class ModalRoot extends React.PureComponent { type: PropTypes.string, props: PropTypes.object, onClose: PropTypes.func.isRequired, + ignoreFocus: PropTypes.bool, }; state = { @@ -85,7 +86,7 @@ export default class ModalRoot extends React.PureComponent { return <BundleModalError {...props} onClose={onClose} />; } - handleClose = () => { + handleClose = (ignoreFocus = false) => { const { onClose } = this.props; let message = null; try { @@ -95,7 +96,7 @@ export default class ModalRoot extends React.PureComponent { // isn't set. // This would be much smoother with react-intl 3+ and `forwardRef`. } - onClose(message); + onClose(message, ignoreFocus); } setModalRef = (c) => { @@ -103,12 +104,12 @@ export default class ModalRoot extends React.PureComponent { } render () { - const { type, props } = this.props; + const { type, props, ignoreFocus } = this.props; const { backgroundColor } = this.state; const visible = !!type; return ( - <Base backgroundColor={backgroundColor} onClose={this.handleClose} noEsc={props ? props.noEsc : false}> + <Base backgroundColor={backgroundColor} onClose={this.handleClose} noEsc={props ? props.noEsc : false} ignoreFocus={ignoreFocus}> {visible && ( <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}> {(SpecificComponent) => <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={this.setModalRef} />} diff --git a/app/javascript/flavours/glitch/features/ui/containers/modal_container.js b/app/javascript/flavours/glitch/features/ui/containers/modal_container.js index 039aabd8a..560c34f01 100644 --- a/app/javascript/flavours/glitch/features/ui/containers/modal_container.js +++ b/app/javascript/flavours/glitch/features/ui/containers/modal_container.js @@ -3,22 +3,23 @@ import { openModal, closeModal } from 'flavours/glitch/actions/modal'; import ModalRoot from '../components/modal_root'; const mapStateToProps = state => ({ - type: state.getIn(['modal', 0, 'modalType'], null), - props: state.getIn(['modal', 0, 'modalProps'], {}), + ignoreFocus: state.getIn(['modal', 'ignoreFocus']), + type: state.getIn(['modal', 'stack', 0, 'modalType'], null), + props: state.getIn(['modal', 'stack', 0, 'modalProps'], {}), }); const mapDispatchToProps = dispatch => ({ - onClose (confirmationMessage) { + onClose (confirmationMessage, ignoreFocus = false) { if (confirmationMessage) { dispatch( openModal('CONFIRM', { message: confirmationMessage.message, confirm: confirmationMessage.confirm, - onConfirm: () => dispatch(closeModal()), + onConfirm: () => dispatch(closeModal(undefined, { ignoreFocus })), }), ); } else { - dispatch(closeModal()); + dispatch(closeModal(undefined, { ignoreFocus })); } }, }); diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js index fcbf07ce2..53e3dfda3 100644 --- a/app/javascript/flavours/glitch/features/video/index.js +++ b/app/javascript/flavours/glitch/features/video/index.js @@ -123,7 +123,7 @@ class Video extends React.PureComponent { autoPlay: PropTypes.bool, volume: PropTypes.number, muted: PropTypes.bool, - componetIndex: PropTypes.number, + componentIndex: PropTypes.number, }; static defaultProps = { @@ -516,7 +516,7 @@ class Video extends React.PureComponent { startTime: this.video.currentTime, autoPlay: !this.state.paused, defaultVolume: this.state.volume, - componetIndex: this.props.componetIndex, + componentIndex: this.props.componentIndex, }); } |