Change status content markup to match upstream (#2923)
* Remove option to have media outside of CWs Upstream adopted the media-in-CW design glitch-soc originally had. * Move poll to StatusContent * Refactor status media icons * Rename `forceFilter` to `showDespiteFilter` for consistency with upstream * Change media and status content markup to match upstream's * Add mention placeholders back
This commit is contained in:
		
							parent
							
								
									5e65586161
								
							
						
					
					
						commit
						b7afca0f05
					
				| 
						 | 
					@ -1,17 +1,25 @@
 | 
				
			||||||
 | 
					import type { IconName } from './media_icon';
 | 
				
			||||||
 | 
					import { MediaIcon } from './media_icon';
 | 
				
			||||||
import { StatusBanner, BannerVariant } from './status_banner';
 | 
					import { StatusBanner, BannerVariant } from './status_banner';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ContentWarning: React.FC<{
 | 
					export const ContentWarning: React.FC<{
 | 
				
			||||||
  text: string;
 | 
					  text: string;
 | 
				
			||||||
  expanded?: boolean;
 | 
					  expanded?: boolean;
 | 
				
			||||||
  onClick?: () => void;
 | 
					  onClick?: () => void;
 | 
				
			||||||
  icons?: React.ReactNode[];
 | 
					  icons?: IconName[];
 | 
				
			||||||
}> = ({ text, expanded, onClick, icons }) => (
 | 
					}> = ({ text, expanded, onClick, icons }) => (
 | 
				
			||||||
  <StatusBanner
 | 
					  <StatusBanner
 | 
				
			||||||
    expanded={expanded}
 | 
					    expanded={expanded}
 | 
				
			||||||
    onClick={onClick}
 | 
					    onClick={onClick}
 | 
				
			||||||
    variant={BannerVariant.Warning}
 | 
					    variant={BannerVariant.Warning}
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    {icons}
 | 
					    {icons?.map((icon) => (
 | 
				
			||||||
 | 
					      <MediaIcon
 | 
				
			||||||
 | 
					        className='status__content__spoiler-icon'
 | 
				
			||||||
 | 
					        icon={icon}
 | 
				
			||||||
 | 
					        key={`icon-${icon}`}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    ))}
 | 
				
			||||||
    <p dangerouslySetInnerHTML={{ __html: text }} />
 | 
					    <p dangerouslySetInnerHTML={{ __html: text }} />
 | 
				
			||||||
  </StatusBanner>
 | 
					  </StatusBanner>
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					import { defineMessages, useIntl } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ImageIcon from '@/material-icons/400-24px/image.svg?react';
 | 
				
			||||||
 | 
					import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
 | 
				
			||||||
 | 
					import LinkIcon from '@/material-icons/400-24px/link.svg?react';
 | 
				
			||||||
 | 
					import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
 | 
				
			||||||
 | 
					import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
 | 
				
			||||||
 | 
					import { Icon } from 'flavours/glitch/components/icon';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const messages = defineMessages({
 | 
				
			||||||
 | 
					  link: {
 | 
				
			||||||
 | 
					    id: 'status.has_preview_card',
 | 
				
			||||||
 | 
					    defaultMessage: 'Features an attached preview card',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  'picture-o': {
 | 
				
			||||||
 | 
					    id: 'status.has_pictures',
 | 
				
			||||||
 | 
					    defaultMessage: 'Features attached pictures',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  tasks: { id: 'status.is_poll', defaultMessage: 'This toot is a poll' },
 | 
				
			||||||
 | 
					  'video-camera': {
 | 
				
			||||||
 | 
					    id: 'status.has_video',
 | 
				
			||||||
 | 
					    defaultMessage: 'Features attached videos',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  music: {
 | 
				
			||||||
 | 
					    id: 'status.has_audio',
 | 
				
			||||||
 | 
					    defaultMessage: 'Features attached audio files',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const iconComponents = {
 | 
				
			||||||
 | 
					  link: LinkIcon,
 | 
				
			||||||
 | 
					  'picture-o': ImageIcon,
 | 
				
			||||||
 | 
					  tasks: InsertChartIcon,
 | 
				
			||||||
 | 
					  'video-camera': MovieIcon,
 | 
				
			||||||
 | 
					  music: MusicNoteIcon,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type IconName = keyof typeof iconComponents;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MediaIcon: React.FC<{
 | 
				
			||||||
 | 
					  className?: string;
 | 
				
			||||||
 | 
					  icon: IconName;
 | 
				
			||||||
 | 
					}> = ({ className, icon }) => {
 | 
				
			||||||
 | 
					  const intl = useIntl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Icon
 | 
				
			||||||
 | 
					      className={className}
 | 
				
			||||||
 | 
					      id={icon}
 | 
				
			||||||
 | 
					      icon={iconComponents[icon]}
 | 
				
			||||||
 | 
					      title={intl.formatMessage(messages[icon])}
 | 
				
			||||||
 | 
					      aria-hidden='true'
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Permalink } from 'flavours/glitch/components/permalink';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MentionsPlaceholder = ({ status }) => {
 | 
				
			||||||
 | 
					  if (status.get('spoiler_text').length === 0 || !status.get('mentions')) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className='status__content'>
 | 
				
			||||||
 | 
					      {status.get('mentions').map(item => (
 | 
				
			||||||
 | 
					        <Permalink
 | 
				
			||||||
 | 
					          to={`/@${item.get('acct')}`}
 | 
				
			||||||
 | 
					          href={item.get('url')}
 | 
				
			||||||
 | 
					          key={item.get('id')}
 | 
				
			||||||
 | 
					          className='mention'
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          @<span>{item.get('username')}</span>
 | 
				
			||||||
 | 
					        </Permalink>
 | 
				
			||||||
 | 
					      )).reduce((aggregate, item) => [...aggregate, item, ' '], [])}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MentionsPlaceholder.propTypes = {
 | 
				
			||||||
 | 
					  status: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
| 
						 | 
					@ -9,8 +9,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { HotKeys } from 'react-hotkeys';
 | 
					import { HotKeys } from 'react-hotkeys';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { ContentWarning } from 'flavours/glitch/components/content_warning';
 | 
				
			||||||
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
 | 
					import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
 | 
				
			||||||
import PollContainer from 'flavours/glitch/containers/poll_container';
 | 
					 | 
				
			||||||
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
 | 
					import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
 | 
				
			||||||
import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning';
 | 
					import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning';
 | 
				
			||||||
import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
 | 
					import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,7 @@ import { Avatar } from './avatar';
 | 
				
			||||||
import { AvatarOverlay } from './avatar_overlay';
 | 
					import { AvatarOverlay } from './avatar_overlay';
 | 
				
			||||||
import { DisplayName } from './display_name';
 | 
					import { DisplayName } from './display_name';
 | 
				
			||||||
import { getHashtagBarForStatus } from './hashtag_bar';
 | 
					import { getHashtagBarForStatus } from './hashtag_bar';
 | 
				
			||||||
 | 
					import { MentionsPlaceholder } from './mentions_placeholder';
 | 
				
			||||||
import { Permalink } from './permalink';
 | 
					import { Permalink } from './permalink';
 | 
				
			||||||
import StatusActionBar from './status_action_bar';
 | 
					import StatusActionBar from './status_action_bar';
 | 
				
			||||||
import StatusContent from './status_content';
 | 
					import StatusContent from './status_content';
 | 
				
			||||||
| 
						 | 
					@ -134,7 +135,7 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
    showMedia: defaultMediaVisibility(this.props.status, this.props.settings) && !(this.context?.hideMediaByDefault),
 | 
					    showMedia: defaultMediaVisibility(this.props.status, this.props.settings) && !(this.context?.hideMediaByDefault),
 | 
				
			||||||
    revealBehindCW: undefined,
 | 
					    revealBehindCW: undefined,
 | 
				
			||||||
    showCard: false,
 | 
					    showCard: false,
 | 
				
			||||||
    forceFilter: undefined,
 | 
					    showDespiteFilter: undefined,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Avoid checking props that are functions (and whose equality will always
 | 
					  // Avoid checking props that are functions (and whose equality will always
 | 
				
			||||||
| 
						 | 
					@ -158,7 +159,7 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
  updateOnStates = [
 | 
					  updateOnStates = [
 | 
				
			||||||
    'isExpanded',
 | 
					    'isExpanded',
 | 
				
			||||||
    'showMedia',
 | 
					    'showMedia',
 | 
				
			||||||
    'forceFilter',
 | 
					    'showDespiteFilter',
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static getDerivedStateFromProps(nextProps, prevState) {
 | 
					  static getDerivedStateFromProps(nextProps, prevState) {
 | 
				
			||||||
| 
						 | 
					@ -242,7 +243,7 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
    if (this.props.status?.get('id') !== prevProps.status?.get('id')) {
 | 
					    if (this.props.status?.get('id') !== prevProps.status?.get('id')) {
 | 
				
			||||||
      this.setState({
 | 
					      this.setState({
 | 
				
			||||||
        showMedia: defaultMediaVisibility(this.props.status, this.props.settings) && !(this.context?.hideMediaByDefault),
 | 
					        showMedia: defaultMediaVisibility(this.props.status, this.props.settings) && !(this.context?.hideMediaByDefault),
 | 
				
			||||||
        forceFilter: undefined,
 | 
					        showDespiteFilter: undefined,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -399,12 +400,12 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleUnfilterClick = e => {
 | 
					  handleUnfilterClick = e => {
 | 
				
			||||||
    this.setState({ forceFilter: false });
 | 
					    this.setState({ showDespiteFilter: false });
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleFilterClick = () => {
 | 
					  handleFilterClick = () => {
 | 
				
			||||||
    this.setState({ forceFilter: true });
 | 
					    this.setState({ showDespiteFilter: true });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleRef = c => {
 | 
					  handleRef = c => {
 | 
				
			||||||
| 
						 | 
					@ -448,27 +449,16 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
    } = this.props;
 | 
					    } = this.props;
 | 
				
			||||||
    let attachments = null;
 | 
					    let attachments = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //  Depending on user settings, some media are considered as parts of the
 | 
					    let media = [];
 | 
				
			||||||
    //  contents (affected by CW) while other will be displayed outside of the
 | 
					    let mediaIcons = [];
 | 
				
			||||||
    //  CW.
 | 
					 | 
				
			||||||
    let contentMedia = [];
 | 
					 | 
				
			||||||
    let contentMediaIcons = [];
 | 
					 | 
				
			||||||
    let extraMedia = [];
 | 
					 | 
				
			||||||
    let extraMediaIcons = [];
 | 
					 | 
				
			||||||
    let media = contentMedia;
 | 
					 | 
				
			||||||
    let mediaIcons = contentMediaIcons;
 | 
					 | 
				
			||||||
    let statusAvatar;
 | 
					    let statusAvatar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (settings.getIn(['content_warnings', 'media_outside'])) {
 | 
					 | 
				
			||||||
      media = extraMedia;
 | 
					 | 
				
			||||||
      mediaIcons = extraMediaIcons;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (status === null) {
 | 
					    if (status === null) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const isExpanded = settings.getIn(['content_warnings', 'shared_state']) ? !status.get('hidden') : this.state.isExpanded;
 | 
					    const isExpanded = settings.getIn(['content_warnings', 'shared_state']) ? !status.get('hidden') : this.state.isExpanded;
 | 
				
			||||||
 | 
					    const expanded = isExpanded || status.get('spoiler_text').length === 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handlers = {
 | 
					    const handlers = {
 | 
				
			||||||
      reply: this.handleHotkeyReply,
 | 
					      reply: this.handleHotkeyReply,
 | 
				
			||||||
| 
						 | 
					@ -498,13 +488,13 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
          <div ref={this.handleRef} className='status focusable' tabIndex={unfocusable ? null : 0}>
 | 
					          <div ref={this.handleRef} className='status focusable' tabIndex={unfocusable ? null : 0}>
 | 
				
			||||||
            <span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span>
 | 
					            <span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span>
 | 
				
			||||||
            {status.get('spoiler_text').length > 0 && (<span>{status.get('spoiler_text')}</span>)}
 | 
					            {status.get('spoiler_text').length > 0 && (<span>{status.get('spoiler_text')}</span>)}
 | 
				
			||||||
            {isExpanded && <span>{status.get('content')}</span>}
 | 
					            {expanded && <span>{status.get('content')}</span>}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </HotKeys>
 | 
					        </HotKeys>
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.state.forceFilter === undefined ? matchedFilters : this.state.forceFilter) {
 | 
					    if (this.state.showDespiteFilter === undefined ? matchedFilters : this.state.showDespiteFilter) {
 | 
				
			||||||
      const minHandlers = this.props.muted ? {} : {
 | 
					      const minHandlers = this.props.muted ? {} : {
 | 
				
			||||||
        moveUp: this.handleHotkeyMoveUp,
 | 
					        moveUp: this.handleHotkeyMoveUp,
 | 
				
			||||||
        moveDown: this.handleHotkeyMoveDown,
 | 
					        moveDown: this.handleHotkeyMoveDown,
 | 
				
			||||||
| 
						 | 
					@ -552,7 +542,7 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
                sensitive={status.get('sensitive')}
 | 
					                sensitive={status.get('sensitive')}
 | 
				
			||||||
                letterbox={settings.getIn(['media', 'letterbox'])}
 | 
					                letterbox={settings.getIn(['media', 'letterbox'])}
 | 
				
			||||||
                fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
 | 
					                fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
 | 
				
			||||||
                hidden={!isExpanded}
 | 
					                hidden={!expanded}
 | 
				
			||||||
                onOpenMedia={this.handleOpenMedia}
 | 
					                onOpenMedia={this.handleOpenMedia}
 | 
				
			||||||
                cacheWidth={this.props.cacheMediaWidth}
 | 
					                cacheWidth={this.props.cacheMediaWidth}
 | 
				
			||||||
                defaultWidth={this.props.cachedMediaWidth}
 | 
					                defaultWidth={this.props.cachedMediaWidth}
 | 
				
			||||||
| 
						 | 
					@ -609,7 +599,7 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
              sensitive={status.get('sensitive')}
 | 
					              sensitive={status.get('sensitive')}
 | 
				
			||||||
              letterbox={settings.getIn(['media', 'letterbox'])}
 | 
					              letterbox={settings.getIn(['media', 'letterbox'])}
 | 
				
			||||||
              fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
 | 
					              fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
 | 
				
			||||||
              preventPlayback={!isExpanded}
 | 
					              preventPlayback={!expanded}
 | 
				
			||||||
              onOpenVideo={this.handleOpenVideo}
 | 
					              onOpenVideo={this.handleOpenVideo}
 | 
				
			||||||
              deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
 | 
					              deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
 | 
				
			||||||
              visible={this.state.showMedia}
 | 
					              visible={this.state.showMedia}
 | 
				
			||||||
| 
						 | 
					@ -631,9 +621,7 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (status.get('poll')) {
 | 
					    if (status.get('poll')) {
 | 
				
			||||||
      const language = status.getIn(['translation', 'language']) || status.get('language');
 | 
					      mediaIcons.push('tasks');
 | 
				
			||||||
      contentMedia.push(<PollContainer pollId={status.get('poll')} status={status} lang={language} />);
 | 
					 | 
				
			||||||
      contentMediaIcons.push('tasks');
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //  Here we prepare extra data-* attributes for CSS selectors.
 | 
					    //  Here we prepare extra data-* attributes for CSS selectors.
 | 
				
			||||||
| 
						 | 
					@ -672,7 +660,6 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
 | 
					    const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
 | 
				
			||||||
    contentMedia.push(hashtagBar);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <HotKeys handlers={handlers} tabIndex={unfocusable ? null : -1}>
 | 
					      <HotKeys handlers={handlers} tabIndex={unfocusable ? null : -1}>
 | 
				
			||||||
| 
						 | 
					@ -704,26 +691,35 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
                </Permalink>
 | 
					                </Permalink>
 | 
				
			||||||
                <StatusIcons
 | 
					                <StatusIcons
 | 
				
			||||||
                  status={status}
 | 
					                  status={status}
 | 
				
			||||||
                  mediaIcons={contentMediaIcons.concat(extraMediaIcons)}
 | 
					                  mediaIcons={mediaIcons}
 | 
				
			||||||
                  settings={settings.get('status_icons')}
 | 
					                  settings={settings.get('status_icons')}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
              </header>
 | 
					              </header>
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
            <StatusContent
 | 
					
 | 
				
			||||||
              status={status}
 | 
					            {status.get('spoiler_text').length > 0 && <ContentWarning text={status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml')} expanded={expanded} onClick={this.handleExpandedToggle} icons={mediaIcons} />}
 | 
				
			||||||
              onClick={this.handleClick}
 | 
					
 | 
				
			||||||
              onTranslate={this.handleTranslate}
 | 
					            {expanded && (
 | 
				
			||||||
              collapsible
 | 
					              <>
 | 
				
			||||||
              media={contentMedia}
 | 
					                <StatusContent
 | 
				
			||||||
              extraMedia={extraMedia}
 | 
					                  status={status}
 | 
				
			||||||
              mediaIcons={contentMediaIcons}
 | 
					                  onClick={this.handleClick}
 | 
				
			||||||
              expanded={isExpanded}
 | 
					                  onTranslate={this.handleTranslate}
 | 
				
			||||||
              onExpandedToggle={this.handleExpandedToggle}
 | 
					                  collapsible
 | 
				
			||||||
              onCollapsedToggle={this.handleCollapsedToggle}
 | 
					                  media={media}
 | 
				
			||||||
              tagLinks={settings.get('tag_misleading_links')}
 | 
					                  onCollapsedToggle={this.handleCollapsedToggle}
 | 
				
			||||||
              rewriteMentions={settings.get('rewrite_mentions')}
 | 
					                  tagLinks={settings.get('tag_misleading_links')}
 | 
				
			||||||
              {...statusContentProps}
 | 
					                  rewriteMentions={settings.get('rewrite_mentions')}
 | 
				
			||||||
            />
 | 
					                  {...statusContentProps}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                {media}
 | 
				
			||||||
 | 
					                {hashtagBar}
 | 
				
			||||||
 | 
					              </>
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {/* This is a glitch-soc addition to have a placeholder */}
 | 
				
			||||||
 | 
					            {!expanded && <MentionsPlaceholder status={status} />}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <StatusActionBar
 | 
					            <StatusActionBar
 | 
				
			||||||
              status={status}
 | 
					              status={status}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,19 +10,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
 | 
					import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
 | 
				
			||||||
import ImageIcon from '@/material-icons/400-24px/image.svg?react';
 | 
					 | 
				
			||||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
 | 
					 | 
				
			||||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
 | 
					 | 
				
			||||||
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
 | 
					 | 
				
			||||||
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
 | 
					 | 
				
			||||||
import { ContentWarning } from 'flavours/glitch/components/content_warning';
 | 
					 | 
				
			||||||
import { Icon } from 'flavours/glitch/components/icon';
 | 
					import { Icon } from 'flavours/glitch/components/icon';
 | 
				
			||||||
 | 
					import PollContainer from 'flavours/glitch/containers/poll_container';
 | 
				
			||||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
 | 
					import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
 | 
				
			||||||
import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
 | 
					import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
 | 
				
			||||||
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
 | 
					import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Permalink } from './permalink';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
 | 
					const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const textMatchesTarget = (text, origin, host) => {
 | 
					const textMatchesTarget = (text, origin, host) => {
 | 
				
			||||||
| 
						 | 
					@ -135,16 +128,10 @@ class StatusContent extends PureComponent {
 | 
				
			||||||
    identity: identityContextPropShape,
 | 
					    identity: identityContextPropShape,
 | 
				
			||||||
    status: ImmutablePropTypes.map.isRequired,
 | 
					    status: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
    statusContent: PropTypes.string,
 | 
					    statusContent: PropTypes.string,
 | 
				
			||||||
    expanded: PropTypes.bool,
 | 
					 | 
				
			||||||
    onExpandedToggle: PropTypes.func,
 | 
					 | 
				
			||||||
    onTranslate: PropTypes.func,
 | 
					    onTranslate: PropTypes.func,
 | 
				
			||||||
    media: PropTypes.node,
 | 
					 | 
				
			||||||
    extraMedia: PropTypes.node,
 | 
					 | 
				
			||||||
    mediaIcons: PropTypes.arrayOf(PropTypes.string),
 | 
					 | 
				
			||||||
    onClick: PropTypes.func,
 | 
					    onClick: PropTypes.func,
 | 
				
			||||||
    collapsible: PropTypes.bool,
 | 
					    collapsible: PropTypes.bool,
 | 
				
			||||||
    onCollapsedToggle: PropTypes.func,
 | 
					    onCollapsedToggle: PropTypes.func,
 | 
				
			||||||
    onUpdate: PropTypes.func,
 | 
					 | 
				
			||||||
    tagLinks: PropTypes.bool,
 | 
					    tagLinks: PropTypes.bool,
 | 
				
			||||||
    rewriteMentions: PropTypes.string,
 | 
					    rewriteMentions: PropTypes.string,
 | 
				
			||||||
    languages: ImmutablePropTypes.map,
 | 
					    languages: ImmutablePropTypes.map,
 | 
				
			||||||
| 
						 | 
					@ -160,12 +147,8 @@ class StatusContent extends PureComponent {
 | 
				
			||||||
    rewriteMentions: 'no',
 | 
					    rewriteMentions: 'no',
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  state = {
 | 
					 | 
				
			||||||
    hidden: true,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _updateStatusLinks () {
 | 
					  _updateStatusLinks () {
 | 
				
			||||||
    const node = this.contentsNode;
 | 
					    const node = this.node;
 | 
				
			||||||
    const { tagLinks, rewriteMentions } = this.props;
 | 
					    const { tagLinks, rewriteMentions } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!node) {
 | 
					    if (!node) {
 | 
				
			||||||
| 
						 | 
					@ -280,7 +263,6 @@ class StatusContent extends PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					  componentDidUpdate () {
 | 
				
			||||||
    this._updateStatusLinks();
 | 
					    this._updateStatusLinks();
 | 
				
			||||||
    if (this.props.onUpdate) this.props.onUpdate();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onMentionClick = (mention, e) => {
 | 
					  onMentionClick = (mention, e) => {
 | 
				
			||||||
| 
						 | 
					@ -326,49 +308,27 @@ class StatusContent extends PureComponent {
 | 
				
			||||||
    this.startXY = null;
 | 
					    this.startXY = null;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleSpoilerClick = (e) => {
 | 
					 | 
				
			||||||
    e.preventDefault();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (this.props.onExpandedToggle) {
 | 
					 | 
				
			||||||
      this.props.onExpandedToggle();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      this.setState({ hidden: !this.state.hidden });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleTranslate = () => {
 | 
					  handleTranslate = () => {
 | 
				
			||||||
    this.props.onTranslate();
 | 
					    this.props.onTranslate();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setContentsRef = (c) => {
 | 
					  setRef = (c) => {
 | 
				
			||||||
    this.contentsNode = c;
 | 
					    this.node = c;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const {
 | 
					    const { status, intl, statusContent } = this.props;
 | 
				
			||||||
      status,
 | 
					 | 
				
			||||||
      media,
 | 
					 | 
				
			||||||
      extraMedia,
 | 
					 | 
				
			||||||
      mediaIcons,
 | 
					 | 
				
			||||||
      tagLinks,
 | 
					 | 
				
			||||||
      rewriteMentions,
 | 
					 | 
				
			||||||
      intl,
 | 
					 | 
				
			||||||
      statusContent,
 | 
					 | 
				
			||||||
    } = this.props;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const renderReadMore = this.props.onClick && status.get('collapsed');
 | 
					    const renderReadMore = this.props.onClick && status.get('collapsed');
 | 
				
			||||||
    const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
 | 
					 | 
				
			||||||
    const contentLocale = intl.locale.replace(/[_-].*/, '');
 | 
					    const contentLocale = intl.locale.replace(/[_-].*/, '');
 | 
				
			||||||
    const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
 | 
					    const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
 | 
				
			||||||
    const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
 | 
					    const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const content = { __html: statusContent ?? getStatusContent(status) };
 | 
					    const content = { __html: statusContent ?? getStatusContent(status) };
 | 
				
			||||||
    const spoilerHtml = status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml');
 | 
					 | 
				
			||||||
    const language = status.getIn(['translation', 'language']) || status.get('language');
 | 
					    const language = status.getIn(['translation', 'language']) || status.get('language');
 | 
				
			||||||
    const classNames = classnames('status__content', {
 | 
					    const classNames = classnames('status__content', {
 | 
				
			||||||
      'status__content--with-action': this.props.onClick && this.props.history,
 | 
					      'status__content--with-action': this.props.onClick && this.props.history,
 | 
				
			||||||
      'status__content--collapsed': renderReadMore,
 | 
					      'status__content--collapsed': renderReadMore,
 | 
				
			||||||
      'status__content--with-spoiler': status.get('spoiler_text').length > 0,
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const readMoreButton = renderReadMore && (
 | 
					    const readMoreButton = renderReadMore && (
 | 
				
			||||||
| 
						 | 
					@ -381,113 +341,30 @@ class StatusContent extends PureComponent {
 | 
				
			||||||
      <TranslateButton onClick={this.handleTranslate} translation={status.get('translation')} />
 | 
					      <TranslateButton onClick={this.handleTranslate} translation={status.get('translation')} />
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (status.get('spoiler_text').length > 0) {
 | 
					    const poll = !!status.get('poll') && (
 | 
				
			||||||
      let mentionsPlaceholder = '';
 | 
					      <PollContainer pollId={status.get('poll')} status={status} lang={language} />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
      const mentionLinks = status.get('mentions').map(item => (
 | 
					 | 
				
			||||||
        <Permalink
 | 
					 | 
				
			||||||
          to={`/@${item.get('acct')}`}
 | 
					 | 
				
			||||||
          href={item.get('url')}
 | 
					 | 
				
			||||||
          key={item.get('id')}
 | 
					 | 
				
			||||||
          className='mention'
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          @<span>{item.get('username')}</span>
 | 
					 | 
				
			||||||
        </Permalink>
 | 
					 | 
				
			||||||
      )).reduce((aggregate, item) => [...aggregate, item, ' '], []);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      let spoilerIcons = [];
 | 
					 | 
				
			||||||
      if (mediaIcons) {
 | 
					 | 
				
			||||||
        const mediaComponents = {
 | 
					 | 
				
			||||||
          'link': LinkIcon,
 | 
					 | 
				
			||||||
          'picture-o': ImageIcon,
 | 
					 | 
				
			||||||
          'tasks': InsertChartIcon,
 | 
					 | 
				
			||||||
          'video-camera': MovieIcon,
 | 
					 | 
				
			||||||
          'music': MusicNoteIcon,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        spoilerIcons = mediaIcons.map((mediaIcon) => (
 | 
					 | 
				
			||||||
          <Icon
 | 
					 | 
				
			||||||
            fixedWidth
 | 
					 | 
				
			||||||
            className='status__content__spoiler-icon'
 | 
					 | 
				
			||||||
            id={mediaIcon}
 | 
					 | 
				
			||||||
            icon={mediaComponents[mediaIcon]}
 | 
					 | 
				
			||||||
            aria-hidden='true'
 | 
					 | 
				
			||||||
            key={`icon-${mediaIcon}`}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (hidden) {
 | 
					 | 
				
			||||||
        mentionsPlaceholder = <div>{mentionLinks}</div>;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.props.onClick) {
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <div className={classNames} tabIndex={0} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
 | 
					        <>
 | 
				
			||||||
          <ContentWarning text={spoilerHtml} expanded={!hidden} onClick={this.handleSpoilerClick} icons={spoilerIcons} />
 | 
					          <div className={classNames} ref={this.setRef} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} tabIndex={0} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
 | 
					            <div className='status__content__text status__content__text--visible translate' lang={language} dangerouslySetInnerHTML={content} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          {mentionsPlaceholder}
 | 
					            {poll}
 | 
				
			||||||
 | 
					            {translateButton}
 | 
				
			||||||
          <div className={`status__content__spoiler ${!hidden ? 'status__content__spoiler--visible' : ''}`}>
 | 
					 | 
				
			||||||
            <div
 | 
					 | 
				
			||||||
              ref={this.setContentsRef}
 | 
					 | 
				
			||||||
              key={`contents-${tagLinks}`}
 | 
					 | 
				
			||||||
              tabIndex={!hidden ? 0 : null}
 | 
					 | 
				
			||||||
              dangerouslySetInnerHTML={content}
 | 
					 | 
				
			||||||
              className='status__content__text translate'
 | 
					 | 
				
			||||||
              onMouseEnter={this.handleMouseEnter}
 | 
					 | 
				
			||||||
              onMouseLeave={this.handleMouseLeave}
 | 
					 | 
				
			||||||
              lang={language}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            {!hidden && translateButton}
 | 
					 | 
				
			||||||
            {media}
 | 
					 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          {extraMedia}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    } else if (this.props.onClick) {
 | 
					 | 
				
			||||||
      return (
 | 
					 | 
				
			||||||
        <div
 | 
					 | 
				
			||||||
          className={classNames}
 | 
					 | 
				
			||||||
          onMouseDown={this.handleMouseDown}
 | 
					 | 
				
			||||||
          onMouseUp={this.handleMouseUp}
 | 
					 | 
				
			||||||
          tabIndex={0}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <div
 | 
					 | 
				
			||||||
            ref={this.setContentsRef}
 | 
					 | 
				
			||||||
            key={`contents-${tagLinks}-${rewriteMentions}`}
 | 
					 | 
				
			||||||
            dangerouslySetInnerHTML={content}
 | 
					 | 
				
			||||||
            className='status__content__text translate'
 | 
					 | 
				
			||||||
            tabIndex={0}
 | 
					 | 
				
			||||||
            onMouseEnter={this.handleMouseEnter}
 | 
					 | 
				
			||||||
            onMouseLeave={this.handleMouseLeave}
 | 
					 | 
				
			||||||
            lang={language}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
          {translateButton}
 | 
					 | 
				
			||||||
          {readMoreButton}
 | 
					          {readMoreButton}
 | 
				
			||||||
          {media}
 | 
					        </>
 | 
				
			||||||
          {extraMedia}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <div
 | 
					        <div className={classNames} ref={this.setRef} tabIndex={0} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
          className='status__content'
 | 
					          <div className='status__content__text status__content__text--visible translate' lang={language} dangerouslySetInnerHTML={content} />
 | 
				
			||||||
          tabIndex={0}
 | 
					
 | 
				
			||||||
        >
 | 
					          {poll}
 | 
				
			||||||
          <div
 | 
					 | 
				
			||||||
            ref={this.setContentsRef}
 | 
					 | 
				
			||||||
            key={`contents-${tagLinks}`}
 | 
					 | 
				
			||||||
            className='status__content__text translate'
 | 
					 | 
				
			||||||
            dangerouslySetInnerHTML={content}
 | 
					 | 
				
			||||||
            tabIndex={0}
 | 
					 | 
				
			||||||
            onMouseEnter={this.handleMouseEnter}
 | 
					 | 
				
			||||||
            onMouseLeave={this.handleMouseLeave}
 | 
					 | 
				
			||||||
            lang={language}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
          {translateButton}
 | 
					          {translateButton}
 | 
				
			||||||
          {media}
 | 
					 | 
				
			||||||
          {extraMedia}
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,23 +8,14 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ForumIcon from '@/material-icons/400-24px/forum.svg?react';
 | 
					import ForumIcon from '@/material-icons/400-24px/forum.svg?react';
 | 
				
			||||||
import HomeIcon from '@/material-icons/400-24px/home.svg?react';
 | 
					import HomeIcon from '@/material-icons/400-24px/home.svg?react';
 | 
				
			||||||
import ImageIcon from '@/material-icons/400-24px/image.svg?react';
 | 
					 | 
				
			||||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
 | 
					 | 
				
			||||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
 | 
					 | 
				
			||||||
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
 | 
					 | 
				
			||||||
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
 | 
					 | 
				
			||||||
import { Icon } from 'flavours/glitch/components/icon';
 | 
					import { Icon } from 'flavours/glitch/components/icon';
 | 
				
			||||||
 | 
					import { MediaIcon } from 'flavours/glitch/components/media_icon';
 | 
				
			||||||
import { languages } from 'flavours/glitch/initial_state';
 | 
					import { languages } from 'flavours/glitch/initial_state';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { VisibilityIcon } from './visibility_icon';
 | 
					import { VisibilityIcon } from './visibility_icon';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const messages = defineMessages({
 | 
					const messages = defineMessages({
 | 
				
			||||||
  inReplyTo: { id: 'status.in_reply_to', defaultMessage: 'This toot is a reply' },
 | 
					  inReplyTo: { id: 'status.in_reply_to', defaultMessage: 'This toot is a reply' },
 | 
				
			||||||
  previewCard: { id: 'status.has_preview_card', defaultMessage: 'Features an attached preview card' },
 | 
					 | 
				
			||||||
  pictures: { id: 'status.has_pictures', defaultMessage: 'Features attached pictures' },
 | 
					 | 
				
			||||||
  poll: { id: 'status.is_poll', defaultMessage: 'This toot is a poll' },
 | 
					 | 
				
			||||||
  video: { id: 'status.has_video', defaultMessage: 'Features attached videos' },
 | 
					 | 
				
			||||||
  audio: { id: 'status.has_audio', defaultMessage: 'Features attached audio files' },
 | 
					 | 
				
			||||||
  localOnly: { id: 'status.local_only', defaultMessage: 'Only visible from your instance' },
 | 
					  localOnly: { id: 'status.local_only', defaultMessage: 'Only visible from your instance' },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,47 +45,6 @@ class StatusIcons extends PureComponent {
 | 
				
			||||||
    settings: ImmutablePropTypes.map.isRequired,
 | 
					    settings: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  renderIcon (mediaIcon) {
 | 
					 | 
				
			||||||
    const { intl } = this.props;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let title, iconComponent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    switch (mediaIcon) {
 | 
					 | 
				
			||||||
    case 'link':
 | 
					 | 
				
			||||||
      title = messages.previewCard;
 | 
					 | 
				
			||||||
      iconComponent = LinkIcon;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'picture-o':
 | 
					 | 
				
			||||||
      title = messages.pictures;
 | 
					 | 
				
			||||||
      iconComponent = ImageIcon;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'tasks':
 | 
					 | 
				
			||||||
      title = messages.poll;
 | 
					 | 
				
			||||||
      iconComponent = InsertChartIcon;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'video-camera':
 | 
					 | 
				
			||||||
      title = messages.video;
 | 
					 | 
				
			||||||
      iconComponent = MovieIcon;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'music':
 | 
					 | 
				
			||||||
      title = messages.audio;
 | 
					 | 
				
			||||||
      iconComponent = MusicNoteIcon;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
      <Icon
 | 
					 | 
				
			||||||
        fixedWidth
 | 
					 | 
				
			||||||
        className='status__media-icon'
 | 
					 | 
				
			||||||
        key={`media-icon--${mediaIcon}`}
 | 
					 | 
				
			||||||
        id={mediaIcon}
 | 
					 | 
				
			||||||
        icon={iconComponent}
 | 
					 | 
				
			||||||
        aria-hidden='true'
 | 
					 | 
				
			||||||
        title={title && intl.formatMessage(title)}
 | 
					 | 
				
			||||||
      />
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const {
 | 
					    const {
 | 
				
			||||||
      status,
 | 
					      status,
 | 
				
			||||||
| 
						 | 
					@ -122,7 +72,7 @@ class StatusIcons extends PureComponent {
 | 
				
			||||||
            aria-hidden='true'
 | 
					            aria-hidden='true'
 | 
				
			||||||
            title={intl.formatMessage(messages.localOnly)}
 | 
					            title={intl.formatMessage(messages.localOnly)}
 | 
				
			||||||
          />}
 | 
					          />}
 | 
				
			||||||
        {settings.get('media') && !!mediaIcons && mediaIcons.map(icon => this.renderIcon(icon))}
 | 
					        {settings.get('media') && !!mediaIcons && mediaIcons.map(icon => (<MediaIcon key={`media-icon--${icon}`} className='status__media-icon' icon={icon} />))}
 | 
				
			||||||
        {settings.get('visibility') && <VisibilityIcon visibility={status.get('visibility')} />}
 | 
					        {settings.get('visibility') && <VisibilityIcon visibility={status.get('visibility')} />}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -274,15 +274,6 @@ class LocalSettingsPage extends PureComponent {
 | 
				
			||||||
          <FormattedMessage id='settings.content_warnings_shared_state' defaultMessage='Show/hide content of all copies at once' />
 | 
					          <FormattedMessage id='settings.content_warnings_shared_state' defaultMessage='Show/hide content of all copies at once' />
 | 
				
			||||||
          <span className='hint'><FormattedMessage id='settings.content_warnings_shared_state_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW' /></span>
 | 
					          <span className='hint'><FormattedMessage id='settings.content_warnings_shared_state_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW' /></span>
 | 
				
			||||||
        </LocalSettingsPageItem>
 | 
					        </LocalSettingsPageItem>
 | 
				
			||||||
        <LocalSettingsPageItem
 | 
					 | 
				
			||||||
          settings={settings}
 | 
					 | 
				
			||||||
          item={['content_warnings', 'media_outside']}
 | 
					 | 
				
			||||||
          id='mastodon-settings--content_warnings-media_outside'
 | 
					 | 
				
			||||||
          onChange={onChange}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <FormattedMessage id='settings.content_warnings_media_outside' defaultMessage='Display media attachments outside content warnings' />
 | 
					 | 
				
			||||||
          <span className='hint'><FormattedMessage id='settings.content_warnings_media_outside_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments' /></span>
 | 
					 | 
				
			||||||
        </LocalSettingsPageItem>
 | 
					 | 
				
			||||||
        <section>
 | 
					        <section>
 | 
				
			||||||
          <h2><FormattedMessage id='settings.content_warnings_unfold_opts' defaultMessage='Auto-unfolding options' /></h2>
 | 
					          <h2><FormattedMessage id='settings.content_warnings_unfold_opts' defaultMessage='Auto-unfolding options' /></h2>
 | 
				
			||||||
          <DeprecatedLocalSettingsPageItem
 | 
					          <DeprecatedLocalSettingsPageItem
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@
 | 
				
			||||||
                  @typescript-eslint/no-unsafe-assignment */
 | 
					                  @typescript-eslint/no-unsafe-assignment */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import type { CSSProperties } from 'react';
 | 
					import type { CSSProperties } from 'react';
 | 
				
			||||||
import { useState, useRef, useCallback } from 'react';
 | 
					import React, { useState, useRef, useCallback } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedDate, FormattedMessage } from 'react-intl';
 | 
					import { FormattedDate, FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,14 +13,15 @@ import { Link } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AnimatedNumber } from 'flavours/glitch/components/animated_number';
 | 
					import { AnimatedNumber } from 'flavours/glitch/components/animated_number';
 | 
				
			||||||
import AttachmentList from 'flavours/glitch/components/attachment_list';
 | 
					import AttachmentList from 'flavours/glitch/components/attachment_list';
 | 
				
			||||||
 | 
					import { ContentWarning } from 'flavours/glitch/components/content_warning';
 | 
				
			||||||
import EditedTimestamp from 'flavours/glitch/components/edited_timestamp';
 | 
					import EditedTimestamp from 'flavours/glitch/components/edited_timestamp';
 | 
				
			||||||
import type { StatusLike } from 'flavours/glitch/components/hashtag_bar';
 | 
					import type { StatusLike } from 'flavours/glitch/components/hashtag_bar';
 | 
				
			||||||
import { getHashtagBarForStatus } from 'flavours/glitch/components/hashtag_bar';
 | 
					import { getHashtagBarForStatus } from 'flavours/glitch/components/hashtag_bar';
 | 
				
			||||||
import { IconLogo } from 'flavours/glitch/components/logo';
 | 
					import { IconLogo } from 'flavours/glitch/components/logo';
 | 
				
			||||||
 | 
					import { MentionsPlaceholder } from 'flavours/glitch/components/mentions_placeholder';
 | 
				
			||||||
import { Permalink } from 'flavours/glitch/components/permalink';
 | 
					import { Permalink } from 'flavours/glitch/components/permalink';
 | 
				
			||||||
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
 | 
					import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
 | 
				
			||||||
import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon';
 | 
					import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon';
 | 
				
			||||||
import PollContainer from 'flavours/glitch/containers/poll_container';
 | 
					 | 
				
			||||||
import { useAppSelector } from 'flavours/glitch/store';
 | 
					import { useAppSelector } from 'flavours/glitch/store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Avatar } from '../../../components/avatar';
 | 
					import { Avatar } from '../../../components/avatar';
 | 
				
			||||||
| 
						 | 
					@ -82,13 +83,6 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
    (state) =>
 | 
					    (state) =>
 | 
				
			||||||
      state.local_settings.get('tag_misleading_links', false) as boolean,
 | 
					      state.local_settings.get('tag_misleading_links', false) as boolean,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  const mediaOutsideCW = useAppSelector(
 | 
					 | 
				
			||||||
    (state) =>
 | 
					 | 
				
			||||||
      state.local_settings.getIn(
 | 
					 | 
				
			||||||
        ['content_warnings', 'media_outside'],
 | 
					 | 
				
			||||||
        false,
 | 
					 | 
				
			||||||
      ) as boolean,
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  const letterboxMedia = useAppSelector(
 | 
					  const letterboxMedia = useAppSelector(
 | 
				
			||||||
    (state) =>
 | 
					    (state) =>
 | 
				
			||||||
      state.local_settings.getIn(['media', 'letterbox'], false) as boolean,
 | 
					      state.local_settings.getIn(['media', 'letterbox'], false) as boolean,
 | 
				
			||||||
| 
						 | 
					@ -108,6 +102,10 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
    [onOpenVideo, status],
 | 
					    [onOpenVideo, status],
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleExpandedToggle = useCallback(() => {
 | 
				
			||||||
 | 
					    if (onToggleHidden) onToggleHidden(status);
 | 
				
			||||||
 | 
					  }, [onToggleHidden, status]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const _measureHeight = useCallback(
 | 
					  const _measureHeight = useCallback(
 | 
				
			||||||
    (heightJustChanged?: boolean) => {
 | 
					    (heightJustChanged?: boolean) => {
 | 
				
			||||||
      if (measureHeight && nodeRef.current) {
 | 
					      if (measureHeight && nodeRef.current) {
 | 
				
			||||||
| 
						 | 
					@ -132,10 +130,6 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
    [_measureHeight],
 | 
					    [_measureHeight],
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleChildUpdate = useCallback(() => {
 | 
					 | 
				
			||||||
    _measureHeight();
 | 
					 | 
				
			||||||
  }, [_measureHeight]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleTranslate = useCallback(() => {
 | 
					  const handleTranslate = useCallback(() => {
 | 
				
			||||||
    if (onTranslate) onTranslate(status);
 | 
					    if (onTranslate) onTranslate(status);
 | 
				
			||||||
  }, [onTranslate, status]);
 | 
					  }, [onTranslate, status]);
 | 
				
			||||||
| 
						 | 
					@ -144,23 +138,11 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let media;
 | 
				
			||||||
  let applicationLink;
 | 
					  let applicationLink;
 | 
				
			||||||
  let reblogLink;
 | 
					  let reblogLink;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //  Depending on user settings, some media are considered as parts of the
 | 
					  const mediaIcons: string[] = [];
 | 
				
			||||||
  //  contents (affected by CW) while other will be displayed outside of the
 | 
					 | 
				
			||||||
  //  CW.
 | 
					 | 
				
			||||||
  const contentMedia: React.ReactNode[] = [];
 | 
					 | 
				
			||||||
  const contentMediaIcons: string[] = [];
 | 
					 | 
				
			||||||
  const extraMedia: React.ReactNode[] = [];
 | 
					 | 
				
			||||||
  const extraMediaIcons: string[] = [];
 | 
					 | 
				
			||||||
  let media = contentMedia;
 | 
					 | 
				
			||||||
  let mediaIcons: string[] = contentMediaIcons;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (mediaOutsideCW) {
 | 
					 | 
				
			||||||
    media = extraMedia;
 | 
					 | 
				
			||||||
    mediaIcons = extraMediaIcons;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const outerStyle = { boxSizing: 'border-box' } as CSSProperties;
 | 
					  const outerStyle = { boxSizing: 'border-box' } as CSSProperties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -172,7 +154,7 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
    status.getIn(['translation', 'language']) || status.get('language');
 | 
					    status.getIn(['translation', 'language']) || status.get('language');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (pictureInPicture.get('inUse')) {
 | 
					  if (pictureInPicture.get('inUse')) {
 | 
				
			||||||
    media.push(<PictureInPicturePlaceholder />);
 | 
					    media = <PictureInPicturePlaceholder />;
 | 
				
			||||||
    mediaIcons.push('video-camera');
 | 
					    mediaIcons.push('video-camera');
 | 
				
			||||||
  } else if (status.get('media_attachments').size > 0) {
 | 
					  } else if (status.get('media_attachments').size > 0) {
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
| 
						 | 
					@ -182,14 +164,14 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
          (item: Immutable.Map<string, any>) => item.get('type') === 'unknown',
 | 
					          (item: Immutable.Map<string, any>) => item.get('type') === 'unknown',
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
      media.push(<AttachmentList media={status.get('media_attachments')} />);
 | 
					      media = <AttachmentList media={status.get('media_attachments')} />;
 | 
				
			||||||
    } else if (
 | 
					    } else if (
 | 
				
			||||||
      ['image', 'gifv', 'unknown'].includes(
 | 
					      ['image', 'gifv', 'unknown'].includes(
 | 
				
			||||||
        status.getIn(['media_attachments', 0, 'type']) as string,
 | 
					        status.getIn(['media_attachments', 0, 'type']) as string,
 | 
				
			||||||
      ) ||
 | 
					      ) ||
 | 
				
			||||||
      status.get('media_attachments').size > 1
 | 
					      status.get('media_attachments').size > 1
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
      media.push(
 | 
					      media = (
 | 
				
			||||||
        <MediaGallery
 | 
					        <MediaGallery
 | 
				
			||||||
          standalone
 | 
					          standalone
 | 
				
			||||||
          sensitive={status.get('sensitive')}
 | 
					          sensitive={status.get('sensitive')}
 | 
				
			||||||
| 
						 | 
					@ -202,7 +184,7 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
          onOpenMedia={onOpenMedia}
 | 
					          onOpenMedia={onOpenMedia}
 | 
				
			||||||
          visible={showMedia}
 | 
					          visible={showMedia}
 | 
				
			||||||
          onToggleVisibility={onToggleMediaVisibility}
 | 
					          onToggleVisibility={onToggleMediaVisibility}
 | 
				
			||||||
        />,
 | 
					        />
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      mediaIcons.push('picture-o');
 | 
					      mediaIcons.push('picture-o');
 | 
				
			||||||
    } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
 | 
					    } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
 | 
				
			||||||
| 
						 | 
					@ -211,7 +193,7 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
        attachment.getIn(['translation', 'description']) ||
 | 
					        attachment.getIn(['translation', 'description']) ||
 | 
				
			||||||
        attachment.get('description');
 | 
					        attachment.get('description');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      media.push(
 | 
					      media = (
 | 
				
			||||||
        <Audio
 | 
					        <Audio
 | 
				
			||||||
          src={attachment.get('url')}
 | 
					          src={attachment.get('url')}
 | 
				
			||||||
          alt={description}
 | 
					          alt={description}
 | 
				
			||||||
| 
						 | 
					@ -229,7 +211,7 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
          blurhash={attachment.get('blurhash')}
 | 
					          blurhash={attachment.get('blurhash')}
 | 
				
			||||||
          height={150}
 | 
					          height={150}
 | 
				
			||||||
          onToggleVisibility={onToggleMediaVisibility}
 | 
					          onToggleVisibility={onToggleMediaVisibility}
 | 
				
			||||||
        />,
 | 
					        />
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      mediaIcons.push('music');
 | 
					      mediaIcons.push('music');
 | 
				
			||||||
    } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
 | 
					    } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
 | 
				
			||||||
| 
						 | 
					@ -238,7 +220,7 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
        attachment.getIn(['translation', 'description']) ||
 | 
					        attachment.getIn(['translation', 'description']) ||
 | 
				
			||||||
        attachment.get('description');
 | 
					        attachment.get('description');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      media.push(
 | 
					      media = (
 | 
				
			||||||
        <Video
 | 
					        <Video
 | 
				
			||||||
          preview={attachment.get('preview_url')}
 | 
					          preview={attachment.get('preview_url')}
 | 
				
			||||||
          frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
 | 
					          frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
 | 
				
			||||||
| 
						 | 
					@ -257,31 +239,23 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
          letterbox={letterboxMedia}
 | 
					          letterbox={letterboxMedia}
 | 
				
			||||||
          fullwidth={fullwidthMedia}
 | 
					          fullwidth={fullwidthMedia}
 | 
				
			||||||
          preventPlayback={!expanded}
 | 
					          preventPlayback={!expanded}
 | 
				
			||||||
        />,
 | 
					        />
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      mediaIcons.push('video-camera');
 | 
					      mediaIcons.push('video-camera');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else if (status.get('spoiler_text').length === 0) {
 | 
					  } else if (status.get('spoiler_text').length === 0) {
 | 
				
			||||||
    media.push(
 | 
					    media = (
 | 
				
			||||||
      <Card
 | 
					      <Card
 | 
				
			||||||
        sensitive={status.get('sensitive')}
 | 
					        sensitive={status.get('sensitive')}
 | 
				
			||||||
        onOpenMedia={onOpenMedia}
 | 
					        onOpenMedia={onOpenMedia}
 | 
				
			||||||
        card={status.get('card', null)}
 | 
					        card={status.get('card', null)}
 | 
				
			||||||
      />,
 | 
					      />
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    mediaIcons.push('link');
 | 
					    mediaIcons.push('link');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (status.get('poll')) {
 | 
					  if (status.get('poll')) {
 | 
				
			||||||
    contentMedia.push(
 | 
					    mediaIcons.push('tasks');
 | 
				
			||||||
      <PollContainer
 | 
					 | 
				
			||||||
        pollId={status.get('poll')}
 | 
					 | 
				
			||||||
        // @ts-expect-error -- Poll/PollContainer is not typed yet
 | 
					 | 
				
			||||||
        status={status}
 | 
					 | 
				
			||||||
        lang={status.get('language')}
 | 
					 | 
				
			||||||
      />,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    contentMediaIcons.push('tasks');
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (status.get('application')) {
 | 
					  if (status.get('application')) {
 | 
				
			||||||
| 
						 | 
					@ -345,7 +319,8 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
  const { statusContentProps, hashtagBar } = getHashtagBarForStatus(
 | 
					  const { statusContentProps, hashtagBar } = getHashtagBarForStatus(
 | 
				
			||||||
    status as StatusLike,
 | 
					    status as StatusLike,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  contentMedia.push(hashtagBar);
 | 
					
 | 
				
			||||||
 | 
					  expanded ||= status.get('spoiler_text').length === 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div style={outerStyle}>
 | 
					    <div style={outerStyle}>
 | 
				
			||||||
| 
						 | 
					@ -379,20 +354,34 @@ export const DetailedStatus: React.FC<{
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        </Permalink>
 | 
					        </Permalink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <StatusContent
 | 
					        {status.get('spoiler_text').length > 0 && (
 | 
				
			||||||
          status={status}
 | 
					          <ContentWarning
 | 
				
			||||||
          media={contentMedia}
 | 
					            text={
 | 
				
			||||||
          extraMedia={extraMedia}
 | 
					              status.getIn(['translation', 'spoilerHtml']) ||
 | 
				
			||||||
          mediaIcons={contentMediaIcons}
 | 
					              status.get('spoilerHtml')
 | 
				
			||||||
          expanded={expanded}
 | 
					            }
 | 
				
			||||||
          collapsed={false}
 | 
					            expanded={expanded}
 | 
				
			||||||
          onExpandedToggle={onToggleHidden}
 | 
					            onClick={handleExpandedToggle}
 | 
				
			||||||
          onTranslate={handleTranslate}
 | 
					          />
 | 
				
			||||||
          onUpdate={handleChildUpdate}
 | 
					        )}
 | 
				
			||||||
          tagLinks={tagMisleadingLinks}
 | 
					
 | 
				
			||||||
          rewriteMentions={rewriteMentions}
 | 
					        {expanded && (
 | 
				
			||||||
          {...(statusContentProps as any)}
 | 
					          <>
 | 
				
			||||||
        />
 | 
					            <StatusContent
 | 
				
			||||||
 | 
					              status={status}
 | 
				
			||||||
 | 
					              onTranslate={handleTranslate}
 | 
				
			||||||
 | 
					              tagLinks={tagMisleadingLinks}
 | 
				
			||||||
 | 
					              rewriteMentions={rewriteMentions}
 | 
				
			||||||
 | 
					              {...(statusContentProps as any)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {media}
 | 
				
			||||||
 | 
					            {hashtagBar}
 | 
				
			||||||
 | 
					          </>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {/* This is a glitch-soc addition to have a placeholder */}
 | 
				
			||||||
 | 
					        {!expanded && <MentionsPlaceholder status={status} />}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='detailed-status__meta'>
 | 
					        <div className='detailed-status__meta'>
 | 
				
			||||||
          <div className='detailed-status__meta__line'>
 | 
					          <div className='detailed-status__meta__line'>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,8 +68,6 @@
 | 
				
			||||||
  "settings.content_warnings": "Content Warnings",
 | 
					  "settings.content_warnings": "Content Warnings",
 | 
				
			||||||
  "settings.content_warnings.regexp": "Regular expression",
 | 
					  "settings.content_warnings.regexp": "Regular expression",
 | 
				
			||||||
  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
 | 
					  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
 | 
				
			||||||
  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
 | 
					 | 
				
			||||||
  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
 | 
					 | 
				
			||||||
  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
 | 
					  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
 | 
				
			||||||
  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
 | 
					  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
 | 
				
			||||||
  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
 | 
					  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,6 @@ const initialState = ImmutableMap({
 | 
				
			||||||
  rewrite_mentions: 'no',
 | 
					  rewrite_mentions: 'no',
 | 
				
			||||||
  content_warnings : ImmutableMap({
 | 
					  content_warnings : ImmutableMap({
 | 
				
			||||||
    filter       : null,
 | 
					    filter       : null,
 | 
				
			||||||
    media_outside: false,
 | 
					 | 
				
			||||||
    shared_state : false,
 | 
					    shared_state : false,
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
  media     : ImmutableMap({
 | 
					  media     : ImmutableMap({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1133,11 +1133,6 @@ body > [data-popper-placement] {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.status__content {
 | 
					 | 
				
			||||||
  // glitch: necessary for fullwidth media options
 | 
					 | 
				
			||||||
  overflow: visible;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.reply-indicator {
 | 
					.reply-indicator {
 | 
				
			||||||
  display: grid;
 | 
					  display: grid;
 | 
				
			||||||
  grid-template-columns: 46px minmax(0, 1fr);
 | 
					  grid-template-columns: 46px minmax(0, 1fr);
 | 
				
			||||||
| 
						 | 
					@ -1351,7 +1346,6 @@ body > [data-popper-placement] {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.status__content.status__content--collapsed .status__content__text {
 | 
					.status__content.status__content--collapsed .status__content__text {
 | 
				
			||||||
  max-height: 20px * 15; // 15 lines is roughly above 500 characters
 | 
					  max-height: 20px * 15; // 15 lines is roughly above 500 characters
 | 
				
			||||||
  overflow: hidden;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.status__content__read-more-button,
 | 
					.status__content__read-more-button,
 | 
				
			||||||
| 
						 | 
					@ -1499,11 +1493,25 @@ body > [data-popper-placement] {
 | 
				
			||||||
    border-bottom: 0;
 | 
					    border-bottom: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .status__content,
 | 
					    .status__content,
 | 
				
			||||||
    .status__action-bar {
 | 
					    .status__action-bar,
 | 
				
			||||||
 | 
					    .media-gallery,
 | 
				
			||||||
 | 
					    .video-player,
 | 
				
			||||||
 | 
					    .audio-player,
 | 
				
			||||||
 | 
					    .attachment-list,
 | 
				
			||||||
 | 
					    .picture-in-picture-placeholder,
 | 
				
			||||||
 | 
					    .more-from-author,
 | 
				
			||||||
 | 
					    .status-card,
 | 
				
			||||||
 | 
					    .hashtag-bar,
 | 
				
			||||||
 | 
					    .content-warning,
 | 
				
			||||||
 | 
					    .filter-warning {
 | 
				
			||||||
      margin-inline-start: $thread-margin;
 | 
					      margin-inline-start: $thread-margin;
 | 
				
			||||||
      width: calc(100% - $thread-margin);
 | 
					      width: calc(100% - $thread-margin);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .more-from-author {
 | 
				
			||||||
 | 
					      width: calc(100% - $thread-margin + 2px);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .status__content__read-more-button {
 | 
					    .status__content__read-more-button {
 | 
				
			||||||
      margin-inline-start: $thread-margin;
 | 
					      margin-inline-start: $thread-margin;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -1659,14 +1667,6 @@ body > [data-popper-placement] {
 | 
				
			||||||
  .media-gallery__item-thumbnail {
 | 
					  .media-gallery__item-thumbnail {
 | 
				
			||||||
    cursor: default;
 | 
					    cursor: default;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  .content-warning {
 | 
					 | 
				
			||||||
    margin-bottom: 16px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &:last-child {
 | 
					 | 
				
			||||||
      margin-bottom: 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.status__prepend {
 | 
					.status__prepend {
 | 
				
			||||||
| 
						 | 
					@ -10934,7 +10934,17 @@ noscript {
 | 
				
			||||||
  $icon-margin: 48px; // 40px avatar + 8px gap
 | 
					  $icon-margin: 48px; // 40px avatar + 8px gap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .status__content,
 | 
					  .status__content,
 | 
				
			||||||
  .status__action-bar {
 | 
					  .status__action-bar,
 | 
				
			||||||
 | 
					  .media-gallery,
 | 
				
			||||||
 | 
					  .video-player,
 | 
				
			||||||
 | 
					  .audio-player,
 | 
				
			||||||
 | 
					  .attachment-list,
 | 
				
			||||||
 | 
					  .picture-in-picture-placeholder,
 | 
				
			||||||
 | 
					  .more-from-author,
 | 
				
			||||||
 | 
					  .status-card,
 | 
				
			||||||
 | 
					  .hashtag-bar,
 | 
				
			||||||
 | 
					  .content-warning,
 | 
				
			||||||
 | 
					  .filter-warning {
 | 
				
			||||||
    margin-inline-start: $icon-margin;
 | 
					    margin-inline-start: $icon-margin;
 | 
				
			||||||
    width: calc(100% - $icon-margin);
 | 
					    width: calc(100% - $icon-margin);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue