Focus on selected status in status list, add moveUp/moveDown hotkeys to tombstone

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2022-06-04 15:20:19 +02:00
parent 666c2dd0ce
commit d4cc2ab29b
3 changed files with 38 additions and 16 deletions

View file

@ -1,4 +1,3 @@
import { List as ImmutableList } from 'immutable';
import React from 'react'; import React from 'react';
import { FormattedList, FormattedMessage } from 'react-intl'; import { FormattedList, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@ -7,7 +6,7 @@ import { openModal } from 'soapbox/actions/modals';
import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper';
import { useAppDispatch } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks';
import type { Status } from 'soapbox/types/entities'; import type { Account, Status } from 'soapbox/types/entities';
interface IStatusReplyMentions { interface IStatusReplyMentions {
status: Status, status: Status,
@ -19,17 +18,19 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status }) => {
const handleOpenMentionsModal: React.MouseEventHandler<HTMLSpanElement> = (e) => { const handleOpenMentionsModal: React.MouseEventHandler<HTMLSpanElement> = (e) => {
e.stopPropagation(); e.stopPropagation();
const account = status.account as Account;
dispatch(openModal('MENTIONS', { dispatch(openModal('MENTIONS', {
username: status.getIn(['account', 'acct']), username: account.acct,
statusId: status.get('id'), statusId: status.id,
})); }));
}; };
if (!status.get('in_reply_to_id')) { if (!status.in_reply_to_id) {
return null; return null;
} }
const to = status.get('mentions', ImmutableList()); const to = status.mentions;
// The post is a reply, but it has no mentions. // The post is a reply, but it has no mentions.
// Rare, but it can happen. // Rare, but it can happen.
@ -46,14 +47,14 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status }) => {
// The typical case with a reply-to and a list of mentions. // The typical case with a reply-to and a list of mentions.
const accounts = to.slice(0, 2).map(account => ( const accounts = to.slice(0, 2).map(account => (
<HoverRefWrapper accountId={account.get('id')} inline> <HoverRefWrapper key={account.id} accountId={account.id} inline>
<Link to={`/@${account.get('acct')}`} className='reply-mentions__account'>@{account.get('username')}</Link> <Link to={`/@${account.acct}`} className='reply-mentions__account'>@{account.username}</Link>
</HoverRefWrapper> </HoverRefWrapper>
)).toArray(); )).toArray();
if (to.size > 2) { if (to.size > 2) {
accounts.push( accounts.push(
<span className='hover:underline cursor-pointer' role='presentation' onClick={handleOpenMentionsModal}> <span key='more' className='hover:underline cursor-pointer' role='presentation' onClick={handleOpenMentionsModal}>
<FormattedMessage id='reply_mentions.more' defaultMessage='{count} more' values={{ count: to.size - 2 }} /> <FormattedMessage id='reply_mentions.more' defaultMessage='{count} more' values={{ count: to.size - 2 }} />
</span>, </span>,
); );

View file

@ -1,16 +1,30 @@
import React from 'react'; import React from 'react';
import { HotKeys } from 'react-hotkeys';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Text } from 'soapbox/components/ui'; import { Text } from 'soapbox/components/ui';
interface ITombstone {
id: string,
onMoveUp: (statusId: string) => void,
onMoveDown: (statusId: string) => void,
}
/** Represents a deleted item. */ /** Represents a deleted item. */
const Tombstone: React.FC = () => { const Tombstone: React.FC<ITombstone> = ({ id, onMoveUp, onMoveDown }) => {
const handlers = {
moveUp: () => onMoveUp(id),
moveDown: () => onMoveDown(id),
};
return ( return (
<div className='p-9 flex items-center justify-center sm:rounded-xl bg-gray-100 border border-solid border-gray-200 dark:bg-slate-900 dark:border-slate-700'> <HotKeys handlers={handlers}>
<div className='p-9 flex items-center justify-center sm:rounded-xl bg-gray-100 border border-solid border-gray-200 dark:bg-slate-900 dark:border-slate-700 focusable' tabIndex={0}>
<Text> <Text>
<FormattedMessage id='statuses.tombstone' defaultMessage='One or more posts is unavailable.' /> <FormattedMessage id='statuses.tombstone' defaultMessage='One or more posts is unavailable.' />
</Text> </Text>
</div> </div>
</HotKeys>
); );
}; };

View file

@ -561,7 +561,12 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
renderTombstone(id: string) { renderTombstone(id: string) {
return ( return (
<div className='py-4 pb-8'> <div className='py-4 pb-8'>
<Tombstone key={id} /> <Tombstone
key={id}
id={id}
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
/>
</div> </div>
); );
} }
@ -635,6 +640,8 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
index: this.props.ancestorsIds.size, index: this.props.ancestorsIds.size,
offset: -80, offset: -80,
}); });
setImmediate(() => this.status?.querySelector('a')?.focus());
} }
} }