Add ability to middle-mouse click posts in web UI (#32988)
This commit is contained in:
		@@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
 | 
			
		||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
import { Link } from 'react-router-dom';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
			
		||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		||||
@@ -164,32 +166,18 @@ class Status extends ImmutablePureComponent {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleClick = e => {
 | 
			
		||||
    if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
    this.handleHotkeyOpen(e);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleMouseUp = e => {
 | 
			
		||||
    // Only handle clicks on the empty space above the content
 | 
			
		||||
 | 
			
		||||
    if (e.target !== e.currentTarget) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.handleHotkeyOpen();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handlePrependAccountClick = e => {
 | 
			
		||||
    this.handleAccountClick(e, false);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleAccountClick = (e, proper = true) => {
 | 
			
		||||
    if (e && (e.button !== 0 || e.ctrlKey || e.metaKey))  {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._openProfile(proper);
 | 
			
		||||
    this.handleClick(e);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleExpandedToggle = () => {
 | 
			
		||||
@@ -287,7 +275,7 @@ class Status extends ImmutablePureComponent {
 | 
			
		||||
    this.props.onMention(this._properStatus().get('account'));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleHotkeyOpen = () => {
 | 
			
		||||
  handleHotkeyOpen = (e) => {
 | 
			
		||||
    if (this.props.onClick) {
 | 
			
		||||
      this.props.onClick();
 | 
			
		||||
      return;
 | 
			
		||||
@@ -300,7 +288,13 @@ class Status extends ImmutablePureComponent {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
 | 
			
		||||
    const path = `/@${status.getIn(['account', 'acct'])}/${status.get('id')}`;
 | 
			
		||||
 | 
			
		||||
    if (e?.button === 0 && !(e?.ctrlKey || e?.metaKey)) {
 | 
			
		||||
      history.push(path);
 | 
			
		||||
    } else if (e?.button === 1 || (e?.button === 0 && (e?.ctrlKey || e?.metaKey))) {
 | 
			
		||||
      window.open(path, '_blank', 'noreferrer noopener');
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleHotkeyOpenProfile = () => {
 | 
			
		||||
@@ -412,7 +406,7 @@ class Status extends ImmutablePureComponent {
 | 
			
		||||
      prepend = (
 | 
			
		||||
        <div className='status__prepend'>
 | 
			
		||||
          <div className='status__prepend__icon'><Icon id='retweet' icon={RepeatIcon} /></div>
 | 
			
		||||
          <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handlePrependAccountClick} data-id={status.getIn(['account', 'id'])} data-hover-card-account={status.getIn(['account', 'id'])} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} />
 | 
			
		||||
          <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <Link data-id={status.getIn(['account', 'id'])} data-hover-card-account={status.getIn(['account', 'id'])} to={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></Link> }} />
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@@ -550,20 +544,19 @@ class Status extends ImmutablePureComponent {
 | 
			
		||||
          <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'>
 | 
			
		||||
            <div onMouseUp={this.handleMouseUp} className='status__info'>
 | 
			
		||||
              <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time'>
 | 
			
		||||
                <span className='status__visibility-icon'><VisibilityIcon visibility={status.get('visibility')} /></span>
 | 
			
		||||
                <RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
 | 
			
		||||
              </a>
 | 
			
		||||
              </Link>
 | 
			
		||||
 | 
			
		||||
              <a onClick={this.handleAccountClick} href={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} data-hover-card-account={status.getIn(['account', 'id'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
 | 
			
		||||
              <Link to={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} data-hover-card-account={status.getIn(['account', 'id'])} className='status__display-name'>
 | 
			
		||||
                <div className='status__avatar'>
 | 
			
		||||
                  {statusAvatar}
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <DisplayName account={status.get('account')} />
 | 
			
		||||
              </a>
 | 
			
		||||
              </Link>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            {matchedFilters && <FilterWarning title={matchedFilters.join(', ')} expanded={this.state.showDespiteFilter} onClick={this.handleFilterToggle} />}
 | 
			
		||||
 
 | 
			
		||||
@@ -204,8 +204,8 @@ class StatusContent extends PureComponent {
 | 
			
		||||
      element = element.parentNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (deltaX + deltaY < 5 && e.button === 0 && this.props.onClick) {
 | 
			
		||||
      this.props.onClick();
 | 
			
		||||
    if (deltaX + deltaY < 5 && (e.button === 0 || e.button === 1) && this.props.onClick) {
 | 
			
		||||
      this.props.onClick(e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.startXY = null;
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleMouseUp = useCallback<React.MouseEventHandler<HTMLDivElement>>(
 | 
			
		||||
    ({ clientX, clientY, target, button }) => {
 | 
			
		||||
    ({ clientX, clientY, target, button, ctrlKey, metaKey }) => {
 | 
			
		||||
      const [startX, startY] = clickCoordinatesRef.current ?? [0, 0];
 | 
			
		||||
      const [deltaX, deltaY] = [
 | 
			
		||||
        Math.abs(clientX - startX),
 | 
			
		||||
@@ -64,8 +64,14 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
 | 
			
		||||
        element = element.parentNode as HTMLDivElement | null;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (deltaX + deltaY < 5 && button === 0 && account) {
 | 
			
		||||
        history.push(`/@${account.acct}/${statusId}`);
 | 
			
		||||
      if (deltaX + deltaY < 5 && account) {
 | 
			
		||||
        const path = `/@${account.acct}/${statusId}`;
 | 
			
		||||
 | 
			
		||||
        if (button === 0 && !(ctrlKey || metaKey)) {
 | 
			
		||||
          history.push(path);
 | 
			
		||||
        } else if (button === 1 || (button === 0 && (ctrlKey || metaKey))) {
 | 
			
		||||
          window.open(path, '_blank', 'noreferrer noopener');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      clickCoordinatesRef.current = null;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user