~cytrogen/masto-fe

290e4aba3167b282c9ee1e966c840a490b7c818b — Eugen Rochko 2 years ago 0c81eec
Add lines to threads in web UI (#24549)

M app/javascript/mastodon/components/status.jsx => app/javascript/mastodon/components/status.jsx +13 -5
@@ 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'>

M app/javascript/mastodon/containers/status_container.jsx => app/javascript/mastodon/containers/status_container.jsx +1 -0
@@ 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),
  });


M app/javascript/mastodon/features/status/index.jsx => app/javascript/mastodon/features/status/index.jsx +8 -3
@@ 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) {

M app/javascript/styles/mastodon/components.scss => app/javascript/styles/mastodon/components.scss +54 -0
@@ 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 {