Merge remote-tracking branch 'origin/main' into fix-classnames

This commit is contained in:
danidfra 2024-11-03 18:50:32 -03:00
commit ce0301adc7
5 changed files with 60 additions and 57 deletions

View File

@ -17,11 +17,11 @@ const AttachmentThumbs = (props: IAttachmentThumbs) => {
const { media, onClick, sensitive } = props;
const dispatch = useAppDispatch();
const fallback = <div className='media-gallery--compact' />;
const fallback = <div className='!h-[50px] bg-transparent' />;
const onOpenMedia = (media: ImmutableList<Attachment>, index: number) => dispatch(openModal('MEDIA', { media, index }));
return (
<div className='attachment-thumbs'>
<div className='relative'>
<Suspense fallback={fallback}>
<MediaGallery
media={media}
@ -34,7 +34,11 @@ const AttachmentThumbs = (props: IAttachmentThumbs) => {
</Suspense>
{onClick && (
<div className='attachment-thumbs__clickable-region' onClick={onClick} />
<button
className='absolute inset-0 size-full cursor-pointer'
onClick={onClick}
style={{ background: 'none', border: 'none', padding: 0 }}
/>
)}
</div>
);

View File

@ -2,7 +2,6 @@ import clsx from 'clsx';
import React, { useState, useRef, useLayoutEffect } from 'react';
import Blurhash from 'soapbox/components/blurhash';
import Icon from 'soapbox/components/icon';
import StillImage from 'soapbox/components/still-image';
import { MIMETYPE_ICONS } from 'soapbox/components/upload';
import { useSettings, useSoapboxConfig } from 'soapbox/hooks';
@ -12,6 +11,8 @@ import { truncateFilename } from 'soapbox/utils/media';
import { isIOS } from '../is-mobile';
import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../utils/media-aspect-ratio';
import SvgIcon from './ui/icon/svg-icon';
import type { Property } from 'csstype';
import type { List as ImmutableList } from 'immutable';
@ -60,6 +61,7 @@ interface IItem {
dimensions: Dimensions;
last?: boolean;
total: number;
compact?: boolean;
}
const Item: React.FC<IItem> = ({
@ -71,6 +73,7 @@ const Item: React.FC<IItem> = ({
dimensions,
last,
total,
compact,
}) => {
const { autoPlayGif } = useSettings();
const { mediaPreview } = useSoapboxConfig();
@ -111,16 +114,21 @@ const Item: React.FC<IItem> = ({
e.stopPropagation();
};
const handleVideoHover: React.MouseEventHandler<HTMLVideoElement> = ({ currentTarget: video }) => {
const handleVideoHover = (event: React.SyntheticEvent<HTMLVideoElement>) => {
const video = event.currentTarget;
video.playbackRate = 3.0;
video.play();
};
const handleVideoLeave: React.MouseEventHandler<HTMLVideoElement> = ({ currentTarget: video }) => {
const handleVideoLeave = (event: React.SyntheticEvent<HTMLVideoElement>) => {
const video = event.currentTarget;
video.pause();
video.currentTime = 0;
};
const handleFocus: React.FocusEventHandler<HTMLVideoElement> = handleVideoHover;
const handleBlur: React.FocusEventHandler<HTMLVideoElement> = handleVideoLeave;
let width: Dimensions['w'] = 100;
let height: Dimensions['h'] = '100%';
let top: Dimensions['t'] = 'auto';
@ -144,43 +152,29 @@ const Item: React.FC<IItem> = ({
let thumbnail: React.ReactNode = '';
const ext = attachment.url.split('.').pop()?.toLowerCase();
/*if (attachment.type === 'unknown' && ['gb', 'gbc'].includes(ext!)) {
return (
<div
className={clsx('media-gallery__item', {
standalone,
'rounded-md': total > 1,
})}
key={attachment.id}
style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}
>
<Suspense fallback={<div className='media-gallery__item-thumbnail' />}>
<Gameboy className='media-gallery__item-thumbnail cursor-default' src={attachment.url} />
</Suspense>
</div>
);
} else */if (attachment.type === 'unknown') {
if (attachment.type === 'unknown') {
const filename = truncateFilename(attachment.url, MAX_FILENAME_LENGTH);
const attachmentIcon = (
<Icon
className='size-16 text-gray-800 dark:text-gray-200'
<SvgIcon
className={clsx('size-16 text-gray-800 dark:text-gray-200', { 'size-8': compact })}
src={MIMETYPE_ICONS[attachment.getIn(['pleroma', 'mime_type']) as string] || require('@tabler/icons/outline/paperclip.svg')}
/>
);
return (
<div
className={clsx('media-gallery__item', {
className={clsx('relative float-left box-border block overflow-hidden rounded-sm border-0', {
standalone,
'rounded-md': total > 1,
'!size-[50px] !inset-auto !float-left !mr-[50px]': compact,
})}
key={attachment.id}
style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}
>
<a className='media-gallery__item-thumbnail' href={attachment.url} target='_blank' style={{ cursor: 'pointer' }}>
<Blurhash hash={attachment.blurhash} className='media-gallery__preview' />
<span className='media-gallery__item__icons'>{attachmentIcon}</span>
<span className='media-gallery__filename__label'>{filename}</span>
<a className='relative z-[1] block size-full cursor-zoom-in leading-none text-gray-400 no-underline' href={attachment.url} target='_blank' style={{ cursor: 'pointer' }}>
<Blurhash hash={attachment.blurhash} className='absolute left-0 top-0 z-0 size-full rounded-lg bg-gray-200 object-cover dark:bg-gray-900' />
<span className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'>{attachmentIcon}</span>
<span className='pointer-events-none absolute bottom-1.5 left-1.5 z-[1] block bg-black/50 px-1.5 py-0.5 text-[11px] font-semibold leading-[18px] text-white opacity-90 transition-opacity duration-100 ease-linear'>{filename}</span>
</a>
</div>
);
@ -189,7 +183,7 @@ const Item: React.FC<IItem> = ({
thumbnail = (
<a
className='media-gallery__item-thumbnail'
className='relative z-[1] block size-full cursor-zoom-in leading-none text-gray-400 no-underline'
href={attachment.url}
onClick={handleClick}
target='_blank'
@ -213,9 +207,9 @@ const Item: React.FC<IItem> = ({
}
thumbnail = (
<div className={clsx('media-gallery__gifv', { autoplay: autoPlayGif })}>
<div className='group relative size-full overflow-hidden'>
<video
className='media-gallery__item-gifv-thumbnail'
className='relative top-0 z-10 size-full transform-none cursor-zoom-in rounded-md object-cover'
aria-label={attachment.description}
title={attachment.description}
role='application'
@ -228,61 +222,65 @@ const Item: React.FC<IItem> = ({
{...conditionalAttributes}
/>
<span className='media-gallery__gifv__label'>GIF</span> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
<span className={clsx('pointer-events-none absolute bottom-1.5 left-1.5 z-[1] block bg-black/50 px-1.5 py-0.5 text-[11px] font-semibold leading-[18px] text-white opacity-90 transition-opacity duration-100 ease-linear group-hover:opacity-100', { 'hidden': autoPlayGif })}>GIF</span> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
</div>
);
} else if (attachment.type === 'audio') {
thumbnail = (
<a
className={clsx('media-gallery__item-thumbnail')}
className={clsx('relative z-[1] block size-full cursor-zoom-in leading-none text-gray-400 no-underline')}
href={attachment.url}
onClick={handleClick}
target='_blank'
title={attachment.description}
>
<span className='media-gallery__item__icons'><Icon src={require('@tabler/icons/outline/volume.svg')} /></span>
<span className='media-gallery__file-extension__label uppercase'>{ext}</span>
<span className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'><SvgIcon className='size-24' src={require('@tabler/icons/outline/volume.svg')} /></span>
<span className={clsx('pointer-events-none absolute bottom-1.5 left-1.5 z-[1] block bg-black/50 px-1.5 py-0.5 text-[11px] font-semibold uppercase leading-[18px] text-white opacity-90 transition-opacity duration-100 ease-linear', { 'hidden': compact })}>{ext}</span>
</a>
);
} else if (attachment.type === 'video') {
thumbnail = (
<a
className={clsx('media-gallery__item-thumbnail')}
className={clsx('relative z-[1] block size-full cursor-zoom-in leading-none text-gray-400 no-underline')}
href={attachment.url}
onClick={handleClick}
target='_blank'
title={attachment.description}
>
<video
className='size-full object-cover'
muted
loop
onMouseOver={handleVideoHover}
onMouseOut={handleVideoLeave}
onFocus={handleFocus}
onBlur={handleBlur}
>
<source src={attachment.url} />
</video>
<span className='media-gallery__file-extension__label uppercase'>{ext}</span>
<span className={clsx('pointer-events-none absolute bottom-1.5 left-1.5 z-[1] block bg-black/50 px-1.5 py-0.5 text-[11px] font-semibold uppercase leading-[18px] text-white opacity-90 transition-opacity duration-100 ease-linear', { 'hidden': compact })}>{ext}</span>
</a>
);
}
return (
<div
className={clsx('media-gallery__item', `media-gallery__item--${attachment.type}`, {
className={clsx('relative float-left box-border block overflow-hidden rounded-sm border-0', {
standalone,
'rounded-md': total > 1,
'!size-[50px] !inset-auto !float-left !mr-[50px]': compact,
})}
key={attachment.id}
style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}
>
{last && total > ATTACHMENT_LIMIT && (
<div className='media-gallery__item-overflow'> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
<div className={clsx('pointer-events-none absolute inset-0 z-[2] flex size-full items-center justify-center bg-white/75 text-center text-[50px] font-bold text-gray-800', { '!text-5': compact })}> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
+{total - ATTACHMENT_LIMIT + 1}
</div>
)}
<Blurhash
hash={attachment.blurhash}
className='media-gallery__preview'
className='absolute left-0 top-0 z-0 size-full rounded-lg bg-gray-200 object-cover dark:bg-gray-900'
/>
{visible && thumbnail}
</div>
@ -561,6 +559,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
dimensions={sizeData.itemsDimensions[i]}
last={i === ATTACHMENT_LIMIT - 1}
total={media.size}
compact={compact}
/>
));
@ -578,7 +577,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
return (
<div
className={clsx(className, 'media-gallery', { 'media-gallery--compact': compact })}
className={clsx(className, 'relative isolate box-border h-auto w-full overflow-hidden rounded-lg', { '!h-[50px] bg-transparent': compact })}
style={sizeData.style}
ref={node}
>

View File

@ -166,7 +166,7 @@ const Upload: React.FC<IUpload> = ({
onDragEnter={onDragEnter}
onDragEnd={onDragEnd}
>
<Blurhash hash={media.blurhash} className='media-gallery__preview' />
<Blurhash hash={media.blurhash} className='absolute left-0 top-0 z-0 size-full rounded-lg bg-gray-200 object-cover dark:bg-gray-900' />
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
{({ scale }) => (
<div

View File

@ -2,8 +2,8 @@ import clsx from 'clsx';
import React, { useState } from 'react';
import Blurhash from 'soapbox/components/blurhash';
import Icon from 'soapbox/components/icon';
import StillImage from 'soapbox/components/still-image';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
import { useSettings } from 'soapbox/hooks';
import { isIOS } from 'soapbox/is-mobile';
@ -80,9 +80,9 @@ const MediaItem: React.FC<IMediaItem> = ({ attachment, onOpenMedia }) => {
conditionalAttributes.autoPlay = true;
}
thumbnail = (
<div className={clsx('media-gallery__gifv', { autoplay: autoPlayGif })}>
<div className='group relative size-full overflow-hidden'>
<video
className='media-gallery__item-gifv-thumbnail'
className='relative top-0 z-10 size-full transform-none cursor-zoom-in rounded-md object-cover'
aria-label={attachment.description}
title={attachment.description}
role='application'
@ -94,7 +94,7 @@ const MediaItem: React.FC<IMediaItem> = ({ attachment, onOpenMedia }) => {
{...conditionalAttributes}
/>
<span className='media-gallery__gifv__label'>GIF</span> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
<span className={clsx('pointer-events-none absolute bottom-1.5 left-1.5 z-[1] block bg-black/50 px-1.5 py-0.5 text-[11px] font-semibold leading-[18px] text-white opacity-90 transition-opacity duration-100 ease-linear group-hover:opacity-100', { 'hidden': autoPlayGif })}>GIF</span> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
</div>
);
} else if (attachment.type === 'audio') {
@ -102,28 +102,28 @@ const MediaItem: React.FC<IMediaItem> = ({ attachment, onOpenMedia }) => {
const fileExtensionLastIndex = remoteURL.lastIndexOf('.');
const fileExtension = remoteURL.slice(fileExtensionLastIndex + 1).toUpperCase();
thumbnail = (
<div className='media-gallery__item-thumbnail'>
<span className='media-gallery__item__icons'><Icon src={require('@tabler/icons/outline/volume.svg')} /></span>
<span className='media-gallery__file-extension__label'>{fileExtension}</span>
<div className='relative z-[1] block size-full cursor-zoom-in leading-none text-gray-400 no-underline'>
<span className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'><SvgIcon className='size-24' src={require('@tabler/icons/outline/volume.svg')} /></span>
<span className='pointer-events-none absolute bottom-1.5 left-1.5 z-[1] block bg-black/50 px-1.5 py-0.5 text-[11px] font-semibold leading-[18px] text-white opacity-90 transition-opacity duration-100 ease-linear'>{fileExtension}</span>
</div>
);
}
if (!visible) {
icon = (
<span className='media-gallery__item__icons'>
<Icon src={require('@tabler/icons/outline/eye-off.svg')} />
<span className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'>
<SvgIcon className='size-24' src={require('@tabler/icons/outline/eye-off.svg')} />
</span>
);
}
return (
<div className='col-span-1'>
<a className='media-gallery__item-thumbnail aspect-1' href={status.url} target='_blank' onClick={handleClick} title={title}>
<a className='relative z-[1] block aspect-1 size-full cursor-zoom-in leading-none text-gray-400 no-underline' href={status.url} target='_blank' onClick={handleClick} title={title}>
<Blurhash
hash={attachment.blurhash}
className={clsx('media-gallery__preview', {
'media-gallery__preview--hidden': visible,
className={clsx('absolute left-0 top-0 z-0 size-full rounded-lg bg-gray-200 object-cover dark:bg-gray-900', {
'hidden': visible,
})}
/>
{visible && thumbnail}

View File

@ -78,13 +78,13 @@ const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, de
const float = dimensions.float as any || 'left';
const position = dimensions.pos as any || 'relative';
return <div key={i} className='media-gallery__item animate-pulse bg-primary-200' style={{ position, float, left, top, right, bottom, height, width }} />;
return <div key={i} className='relative float-left box-border block animate-pulse overflow-hidden rounded-sm border-0 bg-primary-200' style={{ position, float, left, top, right, bottom, height, width }} />;
};
const sizeData = getSizeData(media.size);
return (
<div className='media-gallery media-gallery--placeholder' style={sizeData.get('style')} ref={handleRef}>
<div className='relative isolate box-border h-auto w-full overflow-hidden rounded-lg' style={sizeData.get('style')} ref={handleRef}>
{media.take(4).map((_, i) => renderItem(sizeData.get('itemsDimensions')[i], i))}
</div>
);