Refactor spoiler button logic into separate SpoilerButton component ()

This commit is contained in:
Claire 2025-03-24 14:58:37 +01:00 committed by GitHub
parent 5390edc2aa
commit 958953a687
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 82 additions and 60 deletions
app/javascript/mastodon

View file

@ -12,6 +12,7 @@ import { debounce } from 'lodash';
import { AltTextBadge } from 'mastodon/components/alt_text_badge';
import { Blurhash } from 'mastodon/components/blurhash';
import { SpoilerButton } from 'mastodon/components/spoiler_button';
import { formatTime } from 'mastodon/features/video';
import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
@ -299,7 +300,7 @@ class MediaGallery extends PureComponent {
const { visible } = this.state;
const width = this.state.width || defaultWidth;
let children, spoilerButton;
let children;
const style = {};
@ -318,35 +319,11 @@ class MediaGallery extends PureComponent {
children = media.map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />);
}
if (uncached) {
spoilerButton = (
<button type='button' disabled className='spoiler-button__overlay'>
<span className='spoiler-button__overlay__label'>
<FormattedMessage id='status.uncached_media_warning' defaultMessage='Preview not available' />
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.open' defaultMessage='Click to open' /></span>
</span>
</button>
);
} else if (!visible) {
spoilerButton = (
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
<span className='spoiler-button__overlay__label'>
{sensitive ? <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /> : <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />}
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
</span>
</button>
);
}
return (
<div className={`media-gallery media-gallery--layout-${size}`} style={style} ref={this.handleRef}>
{children}
{(!visible || uncached) && (
<div className={classNames('spoiler-button', { 'spoiler-button--click-thru': uncached })}>
{spoilerButton}
</div>
)}
{(!visible || uncached) && <SpoilerButton uncached={uncached} sensitive={sensitive} onClick={this.handleOpen} />}
{(visible && !uncached) && (
<div className='media-gallery__actions'>

View file

@ -0,0 +1,73 @@
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
interface Props {
hidden?: boolean;
sensitive: boolean;
uncached?: boolean;
onClick: React.MouseEventHandler<HTMLButtonElement>;
}
export const SpoilerButton: React.FC<Props> = ({
hidden = false,
sensitive,
uncached = false,
onClick,
}) => {
let warning;
let action;
if (uncached) {
warning = (
<FormattedMessage
id='status.uncached_media_warning'
defaultMessage='Preview not available'
/>
);
action = (
<FormattedMessage id='status.media.open' defaultMessage='Click to open' />
);
} else if (sensitive) {
warning = (
<FormattedMessage
id='status.sensitive_warning'
defaultMessage='Sensitive content'
/>
);
action = (
<FormattedMessage id='status.media.show' defaultMessage='Click to show' />
);
} else {
warning = (
<FormattedMessage
id='status.media_hidden'
defaultMessage='Media hidden'
/>
);
action = (
<FormattedMessage id='status.media.show' defaultMessage='Click to show' />
);
}
return (
<div
className={classNames('spoiler-button', {
'spoiler-button--hidden': hidden,
'spoiler-button--click-thru': uncached,
})}
>
<button
type='button'
className='spoiler-button__overlay'
onClick={onClick}
disabled={uncached}
>
<span className='spoiler-button__overlay__label'>
{warning}
<span className='spoiler-button__overlay__action'>{action}</span>
</span>
</button>
</div>
);
};

View file

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';
import classNames from 'classnames';
@ -16,6 +16,7 @@ import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?reac
import VolumeOffIcon from '@/material-icons/400-24px/volume_off-fill.svg?react';
import VolumeUpIcon from '@/material-icons/400-24px/volume_up-fill.svg?react';
import { Icon } from 'mastodon/components/icon';
import { SpoilerButton } from 'mastodon/components/spoiler_button';
import { formatTime, getPointerPosition, fileNameFromURL } from 'mastodon/features/video';
import { Blurhash } from '../../components/blurhash';
@ -476,14 +477,6 @@ class Audio extends PureComponent {
const progress = Math.min((currentTime / duration) * 100, 100);
const muted = this.state.muted || volume === 0;
let warning;
if (sensitive) {
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
} else {
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
}
return (
<div className={classNames('audio-player', { editable, inactive: !revealed })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), aspectRatio: '16 / 9' }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} tabIndex={0} onKeyDown={this.handleKeyDown}>
@ -521,14 +514,7 @@ class Audio extends PureComponent {
lang={lang}
/>
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
<span className='spoiler-button__overlay__label'>
{warning}
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
</span>
</button>
</div>
<SpoilerButton hidden={revealed || editable} sensitive={sensitive} onClick={this.toggleReveal} />
{(revealed || editable) && <img
src={this.props.poster}

View file

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';
import classNames from 'classnames';
@ -19,6 +19,7 @@ import VolumeOffIcon from '@/material-icons/400-24px/volume_off-fill.svg?react';
import VolumeUpIcon from '@/material-icons/400-24px/volume_up-fill.svg?react';
import { Blurhash } from 'mastodon/components/blurhash';
import { Icon } from 'mastodon/components/icon';
import { SpoilerButton } from 'mastodon/components/spoiler_button';
import { playerSettings } from 'mastodon/settings';
import { displayMedia, useBlurhash } from '../../initial_state';
@ -549,14 +550,6 @@ class Video extends PureComponent {
preload = 'none';
}
let warning;
if (sensitive) {
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
} else {
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
}
// The outer wrapper is necessary to avoid reflowing the layout when going into full screen
return (
<div style={{ aspectRatio }}>
@ -599,14 +592,7 @@ class Video extends PureComponent {
style={{ width: '100%' }}
/>}
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
<span className='spoiler-button__overlay__label'>
{warning}
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
</span>
</button>
</div>
<SpoilerButton hidden={revealed || editable} sensitive={sensitive} onClick={this.toggleReveal} />
<div className={classNames('video-player__controls', { active: paused || hovered })}>
<div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>