[Glitch] Change design of media tab on profiles in web UI
Port 89df27a06c to glitch-soc
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
			
			
This commit is contained in:
		
							parent
							
								
									157ecf255b
								
							
						
					
					
						commit
						9b5f073cb3
					
				| 
						 | 
				
			
			@ -11,6 +11,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
			
		|||
import { debounce } from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
 | 
			
		||||
import { formatTime } from 'flavours/glitch/features/video';
 | 
			
		||||
 | 
			
		||||
import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +59,7 @@ class Item extends PureComponent {
 | 
			
		|||
 | 
			
		||||
  hoverToPlay () {
 | 
			
		||||
    const { attachment } = this.props;
 | 
			
		||||
    return !this.getAutoPlay() && attachment.get('type') === 'gifv';
 | 
			
		||||
    return !this.getAutoPlay() && ['gifv', 'video'].includes(attachment.get('type'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleClick = (e) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -152,10 +153,15 @@ class Item extends PureComponent {
 | 
			
		|||
          />
 | 
			
		||||
        </a>
 | 
			
		||||
      );
 | 
			
		||||
    } else if (attachment.get('type') === 'gifv') {
 | 
			
		||||
    } else if (['gifv', 'video'].includes(attachment.get('type'))) {
 | 
			
		||||
      const autoPlay = this.getAutoPlay();
 | 
			
		||||
      const duration = attachment.getIn(['meta', 'original', 'duration']);
 | 
			
		||||
 | 
			
		||||
      badges.push(<span key='gif' className='media-gallery__gifv__label'>GIF</span>);
 | 
			
		||||
      if (attachment.get('type') === 'gifv') {
 | 
			
		||||
        badges.push(<span key='gif' className='media-gallery__gifv__label'>GIF</span>);
 | 
			
		||||
      } else {
 | 
			
		||||
        badges.push(<span key='video' className='media-gallery__gifv__label'>{formatTime(Math.floor(duration))}</span>);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      thumbnail = (
 | 
			
		||||
        <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
 | 
			
		||||
| 
						 | 
				
			
			@ -169,6 +175,7 @@ class Item extends PureComponent {
 | 
			
		|||
            onClick={this.handleClick}
 | 
			
		||||
            onMouseEnter={this.handleMouseEnter}
 | 
			
		||||
            onMouseLeave={this.handleMouseLeave}
 | 
			
		||||
            onLoadedData={this.handleImageLoad}
 | 
			
		||||
            autoPlay={autoPlay}
 | 
			
		||||
            playsInline
 | 
			
		||||
            loop
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -648,6 +648,27 @@ class Status extends ImmutablePureComponent {
 | 
			
		|||
            media={status.get('media_attachments')}
 | 
			
		||||
          />,
 | 
			
		||||
        );
 | 
			
		||||
      } else if (['image', 'gifv'].includes(status.getIn(['media_attachments', 0, 'type'])) || status.get('media_attachments').size > 1) {
 | 
			
		||||
        media.push(
 | 
			
		||||
          <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
 | 
			
		||||
            {Component => (
 | 
			
		||||
              <Component
 | 
			
		||||
                media={attachments}
 | 
			
		||||
                lang={language}
 | 
			
		||||
                sensitive={status.get('sensitive')}
 | 
			
		||||
                letterbox={settings.getIn(['media', 'letterbox'])}
 | 
			
		||||
                fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
 | 
			
		||||
                hidden={isCollapsed || !isExpanded}
 | 
			
		||||
                onOpenMedia={this.handleOpenMedia}
 | 
			
		||||
                cacheWidth={this.props.cacheMediaWidth}
 | 
			
		||||
                defaultWidth={this.props.cachedMediaWidth}
 | 
			
		||||
                visible={this.state.showMedia}
 | 
			
		||||
                onToggleVisibility={this.handleToggleMediaVisibility}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          </Bundle>,
 | 
			
		||||
        );
 | 
			
		||||
        mediaIcons.push('picture-o');
 | 
			
		||||
      } else if (attachments.getIn([0, 'type']) === 'audio') {
 | 
			
		||||
        const attachment = status.getIn(['media_attachments', 0]);
 | 
			
		||||
        const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
 | 
			
		||||
| 
						 | 
				
			
			@ -703,27 +724,6 @@ class Status extends ImmutablePureComponent {
 | 
			
		|||
          </Bundle>,
 | 
			
		||||
        );
 | 
			
		||||
        mediaIcons.push('video-camera');
 | 
			
		||||
      } else {  //  Media type is 'image' or 'gifv'
 | 
			
		||||
        media.push(
 | 
			
		||||
          <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
 | 
			
		||||
            {Component => (
 | 
			
		||||
              <Component
 | 
			
		||||
                media={attachments}
 | 
			
		||||
                lang={language}
 | 
			
		||||
                sensitive={status.get('sensitive')}
 | 
			
		||||
                letterbox={settings.getIn(['media', 'letterbox'])}
 | 
			
		||||
                fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
 | 
			
		||||
                hidden={isCollapsed || !isExpanded}
 | 
			
		||||
                onOpenMedia={this.handleOpenMedia}
 | 
			
		||||
                cacheWidth={this.props.cacheMediaWidth}
 | 
			
		||||
                defaultWidth={this.props.cachedMediaWidth}
 | 
			
		||||
                visible={this.state.showMedia}
 | 
			
		||||
                onToggleVisibility={this.handleToggleMediaVisibility}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          </Bundle>,
 | 
			
		||||
        );
 | 
			
		||||
        mediaIcons.push('picture-o');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!status.get('sensitive') && !(status.get('spoiler_text').length > 0) && settings.getIn(['collapsed', 'backgrounds', 'preview_images'])) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,158 +0,0 @@
 | 
			
		|||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
 | 
			
		||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
			
		||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		||||
 | 
			
		||||
import AudiotrackIcon from '@/material-icons/400-24px/music_note.svg?react';
 | 
			
		||||
import PlayArrowIcon from '@/material-icons/400-24px/play_arrow.svg?react';
 | 
			
		||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
 | 
			
		||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
 | 
			
		||||
import { Icon }  from 'flavours/glitch/components/icon';
 | 
			
		||||
import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/initial_state';
 | 
			
		||||
 | 
			
		||||
export default class MediaItem extends ImmutablePureComponent {
 | 
			
		||||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    attachment: ImmutablePropTypes.map.isRequired,
 | 
			
		||||
    displayWidth: PropTypes.number.isRequired,
 | 
			
		||||
    onOpenMedia: PropTypes.func.isRequired,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  state = {
 | 
			
		||||
    visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
 | 
			
		||||
    loaded: false,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleImageLoad = () => {
 | 
			
		||||
    this.setState({ loaded: true });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleMouseEnter = e => {
 | 
			
		||||
    if (this.hoverToPlay()) {
 | 
			
		||||
      e.target.play();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleMouseLeave = e => {
 | 
			
		||||
    if (this.hoverToPlay()) {
 | 
			
		||||
      e.target.pause();
 | 
			
		||||
      e.target.currentTime = 0;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  hoverToPlay () {
 | 
			
		||||
    return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleClick = e => {
 | 
			
		||||
    if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
 | 
			
		||||
      if (this.state.visible) {
 | 
			
		||||
        this.props.onOpenMedia(this.props.attachment);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.setState({ visible: true });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { attachment, displayWidth } = this.props;
 | 
			
		||||
    const { visible, loaded } = this.state;
 | 
			
		||||
 | 
			
		||||
    const width  = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
 | 
			
		||||
    const height = width;
 | 
			
		||||
    const status = attachment.get('status');
 | 
			
		||||
    const title  = status.get('spoiler_text') || attachment.get('description');
 | 
			
		||||
 | 
			
		||||
    let thumbnail, label, icon, content;
 | 
			
		||||
 | 
			
		||||
    if (!visible) {
 | 
			
		||||
      icon = (
 | 
			
		||||
        <span className='account-gallery__item__icons'>
 | 
			
		||||
          <Icon id='eye-slash' icon={VisibilityOffIcon} />
 | 
			
		||||
        </span>
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      if (['audio', 'video'].includes(attachment.get('type'))) {
 | 
			
		||||
        content = (
 | 
			
		||||
          <img
 | 
			
		||||
            src={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
 | 
			
		||||
            alt={attachment.get('description')}
 | 
			
		||||
            lang={status.get('language')}
 | 
			
		||||
            onLoad={this.handleImageLoad}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (attachment.get('type') === 'audio') {
 | 
			
		||||
          label = <Icon id='music' icon={AudiotrackIcon} />;
 | 
			
		||||
        } else {
 | 
			
		||||
          label = <Icon id='play' icon={PlayArrowIcon} />;
 | 
			
		||||
        }
 | 
			
		||||
      } else if (attachment.get('type') === 'image') {
 | 
			
		||||
        const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
 | 
			
		||||
        const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
 | 
			
		||||
        const x      = ((focusX /  2) + .5) * 100;
 | 
			
		||||
        const y      = ((focusY / -2) + .5) * 100;
 | 
			
		||||
 | 
			
		||||
        content = (
 | 
			
		||||
          <img
 | 
			
		||||
            src={attachment.get('preview_url')}
 | 
			
		||||
            alt={attachment.get('description')}
 | 
			
		||||
            lang={status.get('language')}
 | 
			
		||||
            style={{ objectPosition: `${x}% ${y}%` }}
 | 
			
		||||
            onLoad={this.handleImageLoad}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
      } else if (attachment.get('type') === 'gifv') {
 | 
			
		||||
        content = (
 | 
			
		||||
          <video
 | 
			
		||||
            className='media-gallery__item-gifv-thumbnail'
 | 
			
		||||
            aria-label={attachment.get('description')}
 | 
			
		||||
            title={attachment.get('description')}
 | 
			
		||||
            lang={status.get('language')}
 | 
			
		||||
            role='application'
 | 
			
		||||
            src={attachment.get('url')}
 | 
			
		||||
            onMouseEnter={this.handleMouseEnter}
 | 
			
		||||
            onMouseLeave={this.handleMouseLeave}
 | 
			
		||||
            autoPlay={autoPlayGif}
 | 
			
		||||
            playsInline
 | 
			
		||||
            loop
 | 
			
		||||
            muted
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        label = 'GIF';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      thumbnail = (
 | 
			
		||||
        <div className='media-gallery__gifv'>
 | 
			
		||||
          {content}
 | 
			
		||||
 | 
			
		||||
          {label && (
 | 
			
		||||
            <div className='media-gallery__item__badges'>
 | 
			
		||||
              <span className='media-gallery__gifv__label'>{label}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className='account-gallery__item' style={{ width, height }}>
 | 
			
		||||
        <a className='media-gallery__item-thumbnail' href={status.get('url')} onClick={this.handleClick} title={title} target='_blank' rel='noopener noreferrer'>
 | 
			
		||||
          <Blurhash
 | 
			
		||||
            hash={attachment.get('blurhash')}
 | 
			
		||||
            className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })}
 | 
			
		||||
            dummy={!useBlurhash}
 | 
			
		||||
          />
 | 
			
		||||
 | 
			
		||||
          {visible ? thumbnail : icon}
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,200 @@
 | 
			
		|||
import { useState, useCallback } from 'react';
 | 
			
		||||
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
 | 
			
		||||
import HeadphonesIcon from '@/material-icons/400-24px/headphones-fill.svg?react';
 | 
			
		||||
import MovieIcon from '@/material-icons/400-24px/movie-fill.svg?react';
 | 
			
		||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
 | 
			
		||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
 | 
			
		||||
import { Icon } from 'flavours/glitch/components/icon';
 | 
			
		||||
import { formatTime } from 'flavours/glitch/features/video';
 | 
			
		||||
import {
 | 
			
		||||
  autoPlayGif,
 | 
			
		||||
  displayMedia,
 | 
			
		||||
  useBlurhash,
 | 
			
		||||
} from 'flavours/glitch/initial_state';
 | 
			
		||||
import type { Status, MediaAttachment } from 'flavours/glitch/models/status';
 | 
			
		||||
 | 
			
		||||
export const MediaItem: React.FC<{
 | 
			
		||||
  attachment: MediaAttachment;
 | 
			
		||||
  onOpenMedia: (arg0: MediaAttachment) => void;
 | 
			
		||||
}> = ({ attachment, onOpenMedia }) => {
 | 
			
		||||
  const [visible, setVisible] = useState(
 | 
			
		||||
    (displayMedia !== 'hide_all' &&
 | 
			
		||||
      !attachment.getIn(['status', 'sensitive'])) ||
 | 
			
		||||
      displayMedia === 'show_all',
 | 
			
		||||
  );
 | 
			
		||||
  const [loaded, setLoaded] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const handleImageLoad = useCallback(() => {
 | 
			
		||||
    setLoaded(true);
 | 
			
		||||
  }, [setLoaded]);
 | 
			
		||||
 | 
			
		||||
  const handleMouseEnter = useCallback(
 | 
			
		||||
    (e: React.MouseEvent<HTMLVideoElement>) => {
 | 
			
		||||
      if (e.target instanceof HTMLVideoElement) {
 | 
			
		||||
        void e.target.play();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    [],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleMouseLeave = useCallback(
 | 
			
		||||
    (e: React.MouseEvent<HTMLVideoElement>) => {
 | 
			
		||||
      if (e.target instanceof HTMLVideoElement) {
 | 
			
		||||
        e.target.pause();
 | 
			
		||||
        e.target.currentTime = 0;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    [],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleClick = useCallback(
 | 
			
		||||
    (e: React.MouseEvent<HTMLAnchorElement>) => {
 | 
			
		||||
      if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
 | 
			
		||||
        if (visible) {
 | 
			
		||||
          onOpenMedia(attachment);
 | 
			
		||||
        } else {
 | 
			
		||||
          setVisible(true);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    [attachment, visible, onOpenMedia, setVisible],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const status = attachment.get('status') as Status;
 | 
			
		||||
  const description = (attachment.getIn(['translation', 'description']) ||
 | 
			
		||||
    attachment.get('description')) as string | undefined;
 | 
			
		||||
  const previewUrl = attachment.get('preview_url') as string;
 | 
			
		||||
  const fullUrl = attachment.get('url') as string;
 | 
			
		||||
  const avatarUrl = status.getIn(['account', 'avatar_static']) as string;
 | 
			
		||||
  const lang = status.get('language') as string;
 | 
			
		||||
  const blurhash = attachment.get('blurhash') as string;
 | 
			
		||||
  const statusUrl = status.get('url') as string;
 | 
			
		||||
  const type = attachment.get('type') as string;
 | 
			
		||||
 | 
			
		||||
  let thumbnail;
 | 
			
		||||
 | 
			
		||||
  const badges = [];
 | 
			
		||||
 | 
			
		||||
  if (description && description.length > 0) {
 | 
			
		||||
    badges.push(
 | 
			
		||||
      <span key='alt' className='media-gallery__alt__label'>
 | 
			
		||||
        ALT
 | 
			
		||||
      </span>,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!visible) {
 | 
			
		||||
    thumbnail = (
 | 
			
		||||
      <div className='media-gallery__item__overlay'>
 | 
			
		||||
        <Icon id='eye-slash' icon={VisibilityOffIcon} />
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  } else if (type === 'audio') {
 | 
			
		||||
    thumbnail = (
 | 
			
		||||
      <>
 | 
			
		||||
        <img
 | 
			
		||||
          src={previewUrl || avatarUrl}
 | 
			
		||||
          alt={description}
 | 
			
		||||
          title={description}
 | 
			
		||||
          lang={lang}
 | 
			
		||||
          onLoad={handleImageLoad}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <div className='media-gallery__item__overlay media-gallery__item__overlay--corner'>
 | 
			
		||||
          <Icon id='music' icon={HeadphonesIcon} />
 | 
			
		||||
        </div>
 | 
			
		||||
      </>
 | 
			
		||||
    );
 | 
			
		||||
  } else if (type === 'image') {
 | 
			
		||||
    const focusX = (attachment.getIn(['meta', 'focus', 'x']) || 0) as number;
 | 
			
		||||
    const focusY = (attachment.getIn(['meta', 'focus', 'y']) || 0) as number;
 | 
			
		||||
    const x = (focusX / 2 + 0.5) * 100;
 | 
			
		||||
    const y = (focusY / -2 + 0.5) * 100;
 | 
			
		||||
 | 
			
		||||
    thumbnail = (
 | 
			
		||||
      <img
 | 
			
		||||
        src={previewUrl}
 | 
			
		||||
        alt={description}
 | 
			
		||||
        title={description}
 | 
			
		||||
        lang={lang}
 | 
			
		||||
        style={{ objectPosition: `${x}% ${y}%` }}
 | 
			
		||||
        onLoad={handleImageLoad}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  } else if (['video', 'gifv'].includes(type)) {
 | 
			
		||||
    const duration = attachment.getIn([
 | 
			
		||||
      'meta',
 | 
			
		||||
      'original',
 | 
			
		||||
      'duration',
 | 
			
		||||
    ]) as number;
 | 
			
		||||
 | 
			
		||||
    thumbnail = (
 | 
			
		||||
      <div className='media-gallery__gifv'>
 | 
			
		||||
        <video
 | 
			
		||||
          className='media-gallery__item-gifv-thumbnail'
 | 
			
		||||
          aria-label={description}
 | 
			
		||||
          title={description}
 | 
			
		||||
          lang={lang}
 | 
			
		||||
          src={fullUrl}
 | 
			
		||||
          onMouseEnter={handleMouseEnter}
 | 
			
		||||
          onMouseLeave={handleMouseLeave}
 | 
			
		||||
          onLoadedData={handleImageLoad}
 | 
			
		||||
          autoPlay={autoPlayGif}
 | 
			
		||||
          playsInline
 | 
			
		||||
          loop
 | 
			
		||||
          muted
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        {type === 'video' && (
 | 
			
		||||
          <div className='media-gallery__item__overlay media-gallery__item__overlay--corner'>
 | 
			
		||||
            <Icon id='play' icon={MovieIcon} />
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (type === 'gifv') {
 | 
			
		||||
      badges.push(
 | 
			
		||||
        <span key='gif' className='media-gallery__gifv__label'>
 | 
			
		||||
          GIF
 | 
			
		||||
        </span>,
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      badges.push(
 | 
			
		||||
        <span key='video' className='media-gallery__gifv__label'>
 | 
			
		||||
          {formatTime(Math.floor(duration))}
 | 
			
		||||
        </span>,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className='media-gallery__item media-gallery__item--square'>
 | 
			
		||||
      <Blurhash
 | 
			
		||||
        hash={blurhash}
 | 
			
		||||
        className={classNames('media-gallery__preview', {
 | 
			
		||||
          'media-gallery__preview--hidden': visible && loaded,
 | 
			
		||||
        })}
 | 
			
		||||
        dummy={!useBlurhash}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <a
 | 
			
		||||
        className='media-gallery__item-thumbnail'
 | 
			
		||||
        href={statusUrl}
 | 
			
		||||
        onClick={handleClick}
 | 
			
		||||
        target='_blank'
 | 
			
		||||
        rel='noopener noreferrer'
 | 
			
		||||
      >
 | 
			
		||||
        {thumbnail}
 | 
			
		||||
      </a>
 | 
			
		||||
 | 
			
		||||
      {badges.length > 0 && (
 | 
			
		||||
        <div className='media-gallery__item__badges'>{badges}</div>
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ import { expandAccountMediaTimeline } from '../../actions/timelines';
 | 
			
		|||
import HeaderContainer from '../account_timeline/containers/header_container';
 | 
			
		||||
import Column from '../ui/components/column';
 | 
			
		||||
 | 
			
		||||
import MediaItem from './components/media_item';
 | 
			
		||||
import { MediaItem } from './components/media_item';
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = (state, { params: { acct, id } }) => {
 | 
			
		||||
  const accountId = id || state.getIn(['accounts_map', normalizeForLookup(acct)]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -195,6 +195,28 @@ export const DetailedStatus: React.FC<{
 | 
			
		|||
        )
 | 
			
		||||
    ) {
 | 
			
		||||
      media.push(<AttachmentList media={status.get('media_attachments')} />);
 | 
			
		||||
    } else if (
 | 
			
		||||
      ['image', 'gifv'].includes(
 | 
			
		||||
        status.getIn(['media_attachments', 0, 'type']) as string,
 | 
			
		||||
      ) ||
 | 
			
		||||
      status.get('media_attachments').size > 1
 | 
			
		||||
    ) {
 | 
			
		||||
      media.push(
 | 
			
		||||
        <MediaGallery
 | 
			
		||||
          standalone
 | 
			
		||||
          sensitive={status.get('sensitive')}
 | 
			
		||||
          media={status.get('media_attachments')}
 | 
			
		||||
          lang={language}
 | 
			
		||||
          height={300}
 | 
			
		||||
          letterbox={letterboxMedia}
 | 
			
		||||
          fullwidth={fullwidthMedia}
 | 
			
		||||
          hidden={!expanded}
 | 
			
		||||
          onOpenMedia={onOpenMedia}
 | 
			
		||||
          visible={showMedia}
 | 
			
		||||
          onToggleVisibility={onToggleMediaVisibility}
 | 
			
		||||
        />,
 | 
			
		||||
      );
 | 
			
		||||
      mediaIcons.push('picture-o');
 | 
			
		||||
    } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
 | 
			
		||||
      const attachment = status.getIn(['media_attachments', 0]);
 | 
			
		||||
      const description =
 | 
			
		||||
| 
						 | 
				
			
			@ -249,23 +271,6 @@ export const DetailedStatus: React.FC<{
 | 
			
		|||
        />,
 | 
			
		||||
      );
 | 
			
		||||
      mediaIcons.push('video-camera');
 | 
			
		||||
    } else {
 | 
			
		||||
      media.push(
 | 
			
		||||
        <MediaGallery
 | 
			
		||||
          standalone
 | 
			
		||||
          sensitive={status.get('sensitive')}
 | 
			
		||||
          media={status.get('media_attachments')}
 | 
			
		||||
          lang={language}
 | 
			
		||||
          height={300}
 | 
			
		||||
          letterbox={letterboxMedia}
 | 
			
		||||
          fullwidth={fullwidthMedia}
 | 
			
		||||
          hidden={!expanded}
 | 
			
		||||
          onOpenMedia={onOpenMedia}
 | 
			
		||||
          visible={showMedia}
 | 
			
		||||
          onToggleVisibility={onToggleMediaVisibility}
 | 
			
		||||
        />,
 | 
			
		||||
      );
 | 
			
		||||
      mediaIcons.push('picture-o');
 | 
			
		||||
    }
 | 
			
		||||
  } else if (status.get('spoiler_text').length === 0) {
 | 
			
		||||
    media.push(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,3 +10,5 @@ export type Status = Immutable.Map<string, unknown>;
 | 
			
		|||
type CardShape = Required<ApiPreviewCardJSON>;
 | 
			
		||||
 | 
			
		||||
export type Card = RecordOf<CardShape>;
 | 
			
		||||
 | 
			
		||||
export type MediaAttachment = Immutable.Map<string, unknown>;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -242,6 +242,7 @@
 | 
			
		|||
  flex: 0 0 auto;
 | 
			
		||||
 | 
			
		||||
  a {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    color: inherit;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -6287,6 +6288,7 @@ a.status-card {
 | 
			
		|||
      .icon {
 | 
			
		||||
        width: 24px;
 | 
			
		||||
        height: 24px;
 | 
			
		||||
        filter: var(--overlay-icon-shadow);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:hover,
 | 
			
		||||
| 
						 | 
				
			
			@ -6381,6 +6383,10 @@ a.status-card {
 | 
			
		|||
    .icon-button {
 | 
			
		||||
      color: $white;
 | 
			
		||||
 | 
			
		||||
      .icon {
 | 
			
		||||
        filter: var(--overlay-icon-shadow);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:hover,
 | 
			
		||||
      &:focus,
 | 
			
		||||
      &:active {
 | 
			
		||||
| 
						 | 
				
			
			@ -6439,6 +6445,7 @@ a.status-card {
 | 
			
		|||
.media-modal__page-dot {
 | 
			
		||||
  flex: 0 0 auto;
 | 
			
		||||
  background-color: $white;
 | 
			
		||||
  filter: var(--overlay-icon-shadow);
 | 
			
		||||
  opacity: 0.4;
 | 
			
		||||
  height: 6px;
 | 
			
		||||
  width: 6px;
 | 
			
		||||
| 
						 | 
				
			
			@ -7616,8 +7623,8 @@ img.modal-warning {
 | 
			
		|||
  width: 100%;
 | 
			
		||||
  min-height: 64px;
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: 50% 50%;
 | 
			
		||||
  grid-template-rows: 50% 50%;
 | 
			
		||||
  grid-template-columns: 1fr 1fr;
 | 
			
		||||
  grid-template-rows: 1fr 1fr;
 | 
			
		||||
  gap: 2px;
 | 
			
		||||
 | 
			
		||||
  @include fullwidth-gallery;
 | 
			
		||||
| 
						 | 
				
			
			@ -7630,6 +7637,9 @@ img.modal-warning {
 | 
			
		|||
  position: relative;
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  outline: 1px solid var(--media-outline-color);
 | 
			
		||||
  outline-offset: -1px;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
 | 
			
		||||
  &--tall {
 | 
			
		||||
    grid-row: span 2;
 | 
			
		||||
| 
						 | 
				
			
			@ -7646,15 +7656,44 @@ img.modal-warning {
 | 
			
		|||
  &.letterbox {
 | 
			
		||||
    background: $base-shadow-color;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &--square {
 | 
			
		||||
    aspect-ratio: 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__overlay {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    inset-inline-start: 0;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
    padding: 8px;
 | 
			
		||||
    z-index: 1;
 | 
			
		||||
 | 
			
		||||
    &--corner {
 | 
			
		||||
      align-items: flex-start;
 | 
			
		||||
      justify-content: flex-end;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .icon {
 | 
			
		||||
      color: $white;
 | 
			
		||||
      filter: var(--overlay-icon-shadow);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.media-gallery__item-thumbnail {
 | 
			
		||||
  cursor: zoom-in;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  display: block;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  color: $secondary-text-color;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
  z-index: -1;
 | 
			
		||||
 | 
			
		||||
  &,
 | 
			
		||||
  img {
 | 
			
		||||
| 
						 | 
				
			
			@ -7676,7 +7715,7 @@ img.modal-warning {
 | 
			
		|||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  inset-inline-start: 0;
 | 
			
		||||
  z-index: 0;
 | 
			
		||||
  z-index: -2;
 | 
			
		||||
  background: $base-overlay-background;
 | 
			
		||||
 | 
			
		||||
  &--hidden {
 | 
			
		||||
| 
						 | 
				
			
			@ -7689,10 +7728,11 @@ img.modal-warning {
 | 
			
		|||
  overflow: hidden;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  z-index: -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.media-gallery__item-gifv-thumbnail {
 | 
			
		||||
  cursor: zoom-in;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  object-fit: contain;
 | 
			
		||||
| 
						 | 
				
			
			@ -7704,13 +7744,6 @@ img.modal-warning {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.media-gallery__item-thumbnail-label {
 | 
			
		||||
  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
 | 
			
		||||
  clip: rect(1px, 1px, 1px, 1px);
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* End Media Gallery */
 | 
			
		||||
 | 
			
		||||
.detailed,
 | 
			
		||||
| 
						 | 
				
			
			@ -7733,6 +7766,8 @@ img.modal-warning {
 | 
			
		|||
  border-radius: 8px;
 | 
			
		||||
  padding-bottom: 44px;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  outline: 1px solid var(--media-outline-color);
 | 
			
		||||
  outline-offset: -1px;
 | 
			
		||||
 | 
			
		||||
  &.editable {
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -7789,6 +7824,7 @@ img.modal-warning {
 | 
			
		|||
  .video-player__controls {
 | 
			
		||||
    padding-top: 10px;
 | 
			
		||||
    background: transparent;
 | 
			
		||||
    z-index: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7802,16 +7838,15 @@ img.modal-warning {
 | 
			
		|||
  color: $white;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  outline: 1px solid var(--media-outline-color);
 | 
			
		||||
  outline-offset: -1px;
 | 
			
		||||
  z-index: 2;
 | 
			
		||||
 | 
			
		||||
  &.editable {
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
    height: 100% !important;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &:focus {
 | 
			
		||||
    outline: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .detailed-status & {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
| 
						 | 
				
			
			@ -7823,7 +7858,7 @@ img.modal-warning {
 | 
			
		|||
    display: block;
 | 
			
		||||
    max-width: 100vw;
 | 
			
		||||
    max-height: 80vh;
 | 
			
		||||
    z-index: 1;
 | 
			
		||||
    z-index: -2;
 | 
			
		||||
    position: relative;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7850,7 +7885,7 @@ img.modal-warning {
 | 
			
		|||
  &__controls {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    direction: ltr;
 | 
			
		||||
    z-index: 2;
 | 
			
		||||
    z-index: -1;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    inset-inline-start: 0;
 | 
			
		||||
    inset-inline-end: 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -8165,26 +8200,16 @@ img.modal-warning {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.account-gallery__container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  padding: 4px 2px;
 | 
			
		||||
}
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: 1fr 1fr 1fr;
 | 
			
		||||
  gap: 2px;
 | 
			
		||||
 | 
			
		||||
.account-gallery__item {
 | 
			
		||||
  border: 0;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  display: block;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  margin: 2px;
 | 
			
		||||
  .media-gallery__item {
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__icons {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    inset-inline-start: 50%;
 | 
			
		||||
    transform: translate(-50%, -50%);
 | 
			
		||||
    font-size: 24px;
 | 
			
		||||
  .load-more {
 | 
			
		||||
    grid-column: span 3;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,6 +117,8 @@ $dismiss-overlay-width: 4rem;
 | 
			
		|||
  --surface-variant-active-background-color: #{lighten($ui-base-color, 4%)};
 | 
			
		||||
  --on-surface-color: #{transparentize($ui-base-color, 0.5)};
 | 
			
		||||
  --avatar-border-radius: 8px;
 | 
			
		||||
  --media-outline-color: #{rgba(#fcf8ff, 0.15)};
 | 
			
		||||
  --overlay-icon-shadow: drop-shadow(0 0 8px #{rgba($base-shadow-color, 0.25)});
 | 
			
		||||
  --error-background-color: #{darken($error-red, 16%)};
 | 
			
		||||
  --error-active-background-color: #{darken($error-red, 12%)};
 | 
			
		||||
  --on-error-color: #fff;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue