Add lines to threads in web UI (#24549)

main
Eugen Rochko 2023-04-24 08:07:03 +02:00 committed by GitHub
parent 0c81eec239
commit 290e4aba31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 8 deletions

View File

@ -68,6 +68,9 @@ class Status extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map,
account: ImmutablePropTypes.map,
previousId: PropTypes.string,
nextInReplyToId: PropTypes.string,
rootId: PropTypes.string,
onClick: PropTypes.func,
onReply: PropTypes.func,
onFavourite: PropTypes.func,
@ -309,10 +312,7 @@ class Status extends ImmutablePureComponent {
};
render () {
let media = null;
let statusAvatar, prepend, rebloggedByText;
const { intl, hidden, featured, unread, showThread, scrollKey, pictureInPicture } = this.props;
const { intl, hidden, featured, unread, showThread, scrollKey, pictureInPicture, previousId, nextInReplyToId, rootId } = this.props;
let { status, account, ...other } = this.props;
@ -334,6 +334,8 @@ class Status extends ImmutablePureComponent {
openMedia: this.handleHotkeyOpenMedia,
};
let media, statusAvatar, prepend, rebloggedByText;
if (hidden) {
return (
<HotKeys handlers={handlers}>
@ -345,7 +347,11 @@ class Status extends ImmutablePureComponent {
);
}
const connectUp = previousId && previousId === status.get('in_reply_to_id');
const connectToRoot = rootId && rootId === status.get('in_reply_to_id');
const connectReply = nextInReplyToId && nextInReplyToId === status.get('id');
const matchedFilters = status.get('matched_filters');
if (this.state.forceFilter === undefined ? matchedFilters : this.state.forceFilter) {
const minHandlers = this.props.muted ? {} : {
moveUp: this.handleHotkeyMoveUp,
@ -519,7 +525,9 @@ class Status extends ImmutablePureComponent {
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
{prepend}
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted })} data-id={status.get('id')}>
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), 'status--in-thread': !!rootId, 'status--first-in-thread': previousId && (!connectUp || connectToRoot), muted: this.props.muted })} data-id={status.get('id')}>
{(connectReply || connectUp || connectToRoot) && <div className={classNames('status__line', { 'status__line--full': connectReply, 'status__line--first': !status.get('in_reply_to_id') && !connectToRoot })} />}
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div onClick={this.handleClick} className='status__info'>
<a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>

View File

@ -67,6 +67,7 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => ({
status: getStatus(state, props),
nextInReplyToId: props.nextId ? state.getIn(['statuses', props.nextId, 'in_reply_to_id']) : null,
pictureInPicture: getPictureInPicture(state, props),
});

View File

@ -529,14 +529,19 @@ class Status extends ImmutablePureComponent {
}
}
renderChildren (list) {
return list.map(id => (
renderChildren (list, ancestors) {
const { params: { statusId } } = this.props;
return list.map((id, i) => (
<StatusContainer
key={id}
id={id}
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
contextType='thread'
previousId={i > 0 && list.get(i - 1)}
nextId={list.get(i + 1) || (ancestors && statusId)}
rootId={statusId}
/>
));
}
@ -590,7 +595,7 @@ class Status extends ImmutablePureComponent {
}
if (ancestorsIds && ancestorsIds.size > 0) {
ancestors = <>{this.renderChildren(ancestorsIds)}</>;
ancestors = <>{this.renderChildren(ancestorsIds, true)}</>;
}
if (descendantsIds && descendantsIds.size > 0) {

View File

@ -1145,6 +1145,58 @@ body > [data-popper-placement] {
}
}
}
&--in-thread {
border-bottom: 0;
.status__content,
.status__action-bar,
.media-gallery,
.video,
.audio,
.attachment-list {
margin-left: 46px + 10px;
width: calc(100% - (46px + 10px));
}
}
&--first-in-thread {
border-top: 1px solid lighten($ui-base-color, 8%);
}
&__line {
height: 16px - 4px;
border-inline-start: 2px solid lighten($ui-base-color, 8%);
width: 0;
position: absolute;
top: 0;
inset-inline-start: 16px + ((46px - 2px) / 2);
&--full {
top: 0;
height: 100%;
&::before {
content: '';
display: block;
position: absolute;
top: 16px - 4px;
height: 46px + 4px + 4px;
width: 2px;
background: $ui-base-color;
inset-inline-start: -2px;
}
}
&--first {
top: 16px + 46px + 4px;
height: calc(100% - (16px + 46px + 4px));
&::before {
display: none;
}
}
}
}
.status__relative-time {
@ -1293,6 +1345,7 @@ body > [data-popper-placement] {
.detailed-status {
background: lighten($ui-base-color, 4%);
padding: 16px;
border-top: 1px solid lighten($ui-base-color, 8%);
&--flex {
display: flex;
@ -1729,6 +1782,7 @@ a.account__display-name {
.status__avatar {
width: 46px;
height: 46px;
box-shadow: 0 0 0 2px $ui-base-color;
}
.muted {