diff options
Diffstat (limited to 'app/javascript/mastodon/features/status/index.js')
-rw-r--r-- | app/javascript/mastodon/features/status/index.js | 153 |
1 files changed, 130 insertions, 23 deletions
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index fc45d5f21..fff5f529c 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -28,6 +28,7 @@ import StatusContainer from '../../../glitch/components/status/container'; import { openModal } from '../../actions/modal'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { HotKeys } from 'react-hotkeys'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -153,8 +154,100 @@ export default class Status extends ImmutablePureComponent { this.props.dispatch(openModal('EMBED', { url: status.get('url') })); } + handleHotkeyMoveUp = () => { + this.handleMoveUp(this.props.status.get('id')); + } + + handleHotkeyMoveDown = () => { + this.handleMoveDown(this.props.status.get('id')); + } + + handleHotkeyReply = e => { + e.preventDefault(); + this.handleReplyClick(this.props.status); + } + + handleHotkeyFavourite = () => { + this.handleFavouriteClick(this.props.status); + } + + handleHotkeyBoost = () => { + this.handleReblogClick(this.props.status); + } + + handleHotkeyMention = e => { + e.preventDefault(); + this.handleMentionClick(this.props.status); + } + + handleHotkeyOpenProfile = () => { + this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`); + } + + handleMoveUp = id => { + const { status, ancestorsIds, descendantsIds } = this.props; + + if (id === status.get('id')) { + this._selectChild(ancestorsIds.size - 1); + } else { + let index = ancestorsIds.indexOf(id); + + if (index === -1) { + index = descendantsIds.indexOf(id); + this._selectChild(ancestorsIds.size + index); + } else { + this._selectChild(index - 1); + } + } + } + + handleMoveDown = id => { + const { status, ancestorsIds, descendantsIds } = this.props; + + if (id === status.get('id')) { + this._selectChild(ancestorsIds.size + 1); + } else { + let index = ancestorsIds.indexOf(id); + + if (index === -1) { + index = descendantsIds.indexOf(id); + this._selectChild(ancestorsIds.size + index + 2); + } else { + this._selectChild(index + 1); + } + } + } + + _selectChild (index) { + const element = this.node.querySelectorAll('.focusable')[index]; + + if (element) { + element.focus(); + } + } + renderChildren (list) { - return list.map(id => <StatusContainer key={id} id={id} />); + return list.map(id => ( + <StatusContainer + key={id} + id={id} + onMoveUp={this.handleMoveUp} + onMoveDown={this.handleMoveDown} + /> + )); + } + + setRef = c => { + this.node = c; + } + + componentDidUpdate () { + const { status, ancestorsIds } = this.props; + + if (status && ancestorsIds && ancestorsIds.size > 0) { + const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size]; + element.scrollIntoView(); + } } render () { @@ -178,35 +271,49 @@ export default class Status extends ImmutablePureComponent { descendants = <div>{this.renderChildren(descendantsIds)}</div>; } + const handlers = { + moveUp: this.handleHotkeyMoveUp, + moveDown: this.handleHotkeyMoveDown, + reply: this.handleHotkeyReply, + favourite: this.handleHotkeyFavourite, + boost: this.handleHotkeyBoost, + mention: this.handleHotkeyMention, + openProfile: this.handleHotkeyOpenProfile, + }; + return ( <Column> <ColumnBackButton /> <ScrollContainer scrollKey='thread'> - <div className='scrollable detailed-status__wrapper'> + <div className='scrollable detailed-status__wrapper' ref={this.setRef}> {ancestors} - <DetailedStatus - status={status} - settings={settings} - autoPlayGif={autoPlayGif} - me={me} - onOpenVideo={this.handleOpenVideo} - onOpenMedia={this.handleOpenMedia} - /> - - <ActionBar - status={status} - me={me} - onReply={this.handleReplyClick} - onFavourite={this.handleFavouriteClick} - onReblog={this.handleReblogClick} - onDelete={this.handleDeleteClick} - onMention={this.handleMentionClick} - onReport={this.handleReport} - onPin={this.handlePin} - onEmbed={this.handleEmbed} - /> + <HotKeys handlers={handlers}> + <div className='focusable' tabIndex='0'> + <DetailedStatus + status={status} + settings={settings} + autoPlayGif={autoPlayGif} + me={me} + onOpenVideo={this.handleOpenVideo} + onOpenMedia={this.handleOpenMedia} + /> + + <ActionBar + status={status} + me={me} + onReply={this.handleReplyClick} + onFavourite={this.handleFavouriteClick} + onReblog={this.handleReblogClick} + onDelete={this.handleDeleteClick} + onMention={this.handleMentionClick} + onReport={this.handleReport} + onPin={this.handlePin} + onEmbed={this.handleEmbed} + /> + </div> + </HotKeys> {descendants} </div> |