Merge commit 'ce1f35d7e213327549b960bb64f63c67a141ea40' into glitch-soc/merge-upstream
Conflicts: - `db/schema.rb`: Upstream regenerated the schema file using Rails 7, the conflicts are caused by our extra columns. Applied upstream's changes, but keeping our extra columns.
This commit is contained in:
@@ -470,6 +470,7 @@ class Audio extends PureComponent {
|
||||
const progress = Math.min((currentTime / duration) * 100, 100);
|
||||
|
||||
let warning;
|
||||
|
||||
if (sensitive) {
|
||||
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
|
||||
} else {
|
||||
@@ -515,7 +516,10 @@ class Audio extends PureComponent {
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
||||
@@ -86,7 +86,6 @@ class Bookmarks extends ImmutablePureComponent {
|
||||
onClick={this.handleHeaderClick}
|
||||
pinned={pinned}
|
||||
multiColumn={multiColumn}
|
||||
showBackButton
|
||||
/>
|
||||
|
||||
<StatusList
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
import { accountsCountRenderer } from 'mastodon/components/hashtag';
|
||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
|
||||
@@ -13,10 +16,14 @@ export default class Story extends PureComponent {
|
||||
static propTypes = {
|
||||
url: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
lang: PropTypes.string,
|
||||
publisher: PropTypes.string,
|
||||
publishedAt: PropTypes.string,
|
||||
author: PropTypes.string,
|
||||
sharedTimes: PropTypes.number,
|
||||
thumbnail: PropTypes.string,
|
||||
blurhash: PropTypes.string,
|
||||
expanded: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -26,16 +33,16 @@ export default class Story extends PureComponent {
|
||||
handleImageLoad = () => this.setState({ thumbnailLoaded: true });
|
||||
|
||||
render () {
|
||||
const { url, title, publisher, sharedTimes, thumbnail, blurhash } = this.props;
|
||||
const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, blurhash } = this.props;
|
||||
|
||||
const { thumbnailLoaded } = this.state;
|
||||
|
||||
return (
|
||||
<a className='story' href={url} target='blank' rel='noopener'>
|
||||
<a className={classNames('story', { expanded })} href={url} target='blank' rel='noopener'>
|
||||
<div className='story__details'>
|
||||
<div className='story__details__publisher'>{publisher ? publisher : <Skeleton width={50} />}</div>
|
||||
<div className='story__details__title'>{title ? title : <Skeleton />}</div>
|
||||
<div className='story__details__shared'>{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div>
|
||||
<div className='story__details__publisher'>{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}</div>
|
||||
<div className='story__details__title' lang={lang}>{title ? title : <Skeleton />}</div>
|
||||
<div className='story__details__shared'>{author && <><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{author}</strong> }} /> · </>}{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div>
|
||||
</div>
|
||||
|
||||
<div className='story__thumbnail'>
|
||||
|
||||
@@ -55,12 +55,16 @@ class Links extends PureComponent {
|
||||
<div className='explore__links'>
|
||||
{banner}
|
||||
|
||||
{isLoading ? (<LoadingIndicator />) : links.map(link => (
|
||||
{isLoading ? (<LoadingIndicator />) : links.map((link, i) => (
|
||||
<Story
|
||||
key={link.get('id')}
|
||||
expanded={i === 0}
|
||||
lang={link.get('language')}
|
||||
url={link.get('url')}
|
||||
title={link.get('title')}
|
||||
publisher={link.get('provider_name')}
|
||||
publishedAt={link.get('published_at')}
|
||||
author={link.get('author_name')}
|
||||
sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
|
||||
thumbnail={link.get('image')}
|
||||
blurhash={link.get('blurhash')}
|
||||
|
||||
@@ -86,7 +86,6 @@ class Favourites extends ImmutablePureComponent {
|
||||
onClick={this.handleHeaderClick}
|
||||
pinned={pinned}
|
||||
multiColumn={multiColumn}
|
||||
showBackButton
|
||||
/>
|
||||
|
||||
<StatusList
|
||||
|
||||
@@ -65,7 +65,7 @@ class Lists extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} showBackButton />
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} />
|
||||
|
||||
<NewListForm />
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
import { useBlurhash } from 'mastodon/initial_state';
|
||||
|
||||
const IDNA_PREFIX = 'xn--';
|
||||
@@ -57,14 +58,9 @@ export default class Card extends PureComponent {
|
||||
static propTypes = {
|
||||
card: ImmutablePropTypes.map,
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
compact: PropTypes.bool,
|
||||
sensitive: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
compact: false,
|
||||
};
|
||||
|
||||
state = {
|
||||
previewLoaded: false,
|
||||
embedded: false,
|
||||
@@ -148,7 +144,7 @@ export default class Card extends PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { card, compact } = this.props;
|
||||
const { card } = this.props;
|
||||
const { embedded, revealed } = this.state;
|
||||
|
||||
if (card === null) {
|
||||
@@ -156,29 +152,27 @@ export default class Card extends PureComponent {
|
||||
}
|
||||
|
||||
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
|
||||
const horizontal = (!compact && card.get('width') > card.get('height')) || card.get('type') !== 'link' || embedded;
|
||||
const interactive = card.get('type') !== 'link';
|
||||
const className = classnames('status-card', { horizontal, compact, interactive });
|
||||
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
||||
const language = card.get('language') || '';
|
||||
|
||||
const description = (
|
||||
<div className='status-card__content' lang={language}>
|
||||
{title}
|
||||
{!(horizontal || compact) && <p className='status-card__description' title={card.get('description')}>{card.get('description')}</p>}
|
||||
<span className='status-card__host'>{provider}</span>
|
||||
<div className='status-card__content'>
|
||||
<span className='status-card__host'>
|
||||
<span lang={language}>{provider}</span>
|
||||
{card.get('published_at') && <> · <RelativeTimestamp timestamp={card.get('published_at')} /></>}
|
||||
</span>
|
||||
<strong className='status-card__title' title={card.get('title')} lang={language}>{card.get('title')}</strong>
|
||||
{card.get('author_name').length > 0 && <span className='status-card__author'><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{card.get('author_name')}</strong> }} /></span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
const thumbnailStyle = {
|
||||
visibility: revealed? null : 'hidden',
|
||||
visibility: revealed ? null : 'hidden',
|
||||
aspectRatio: `${card.get('width')} / ${card.get('height')}`
|
||||
};
|
||||
|
||||
if (horizontal) {
|
||||
thumbnailStyle.aspectRatio = (compact && !embedded) ? '16 / 9' : `${card.get('width')} / ${card.get('height')}`;
|
||||
}
|
||||
let embed;
|
||||
|
||||
let embed = '';
|
||||
let canvas = (
|
||||
<Blurhash
|
||||
className={classnames('status-card__image-preview', {
|
||||
@@ -188,12 +182,18 @@ export default class Card extends PureComponent {
|
||||
dummy={!useBlurhash}
|
||||
/>
|
||||
);
|
||||
|
||||
let thumbnail = <img src={card.get('image')} alt='' style={thumbnailStyle} onLoad={this.handleImageLoad} className='status-card__image-image' />;
|
||||
|
||||
let spoilerButton = (
|
||||
<button type='button' onClick={this.handleReveal} className='spoiler-button__overlay'>
|
||||
<span className='spoiler-button__overlay__label'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
|
||||
<span className='spoiler-button__overlay__label'>
|
||||
<FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />
|
||||
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
spoilerButton = (
|
||||
<div className={classnames('spoiler-button', { 'spoiler-button--minified': revealed })}>
|
||||
{spoilerButton}
|
||||
@@ -219,19 +219,20 @@ export default class Card extends PureComponent {
|
||||
<div className='status-card__actions'>
|
||||
<div>
|
||||
<button type='button' onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button>
|
||||
{horizontal && <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>}
|
||||
<a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!revealed && spoilerButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} ref={this.setRef} onClick={revealed ? null : this.handleReveal} role={revealed ? 'button' : null}>
|
||||
<div className='status-card' ref={this.setRef} onClick={revealed ? null : this.handleReveal} role={revealed ? 'button' : null}>
|
||||
{embed}
|
||||
{!compact && description}
|
||||
<a href={card.get('url')} target='_blank' rel='noopener noreferrer'>{description}</a>
|
||||
</div>
|
||||
);
|
||||
} else if (card.get('image')) {
|
||||
@@ -243,14 +244,14 @@ export default class Card extends PureComponent {
|
||||
);
|
||||
} else {
|
||||
embed = (
|
||||
<div className='status-card__image'>
|
||||
<div className='status-card__image' style={{ aspectRatio: '1.9 / 1' }}>
|
||||
<Icon id='file-text' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<a href={card.get('url')} className={className} target='_blank' rel='noopener noreferrer' ref={this.setRef}>
|
||||
<a href={card.get('url')} className='status-card' target='_blank' rel='noopener noreferrer' ref={this.setRef}>
|
||||
{embed}
|
||||
{description}
|
||||
</a>
|
||||
|
||||
@@ -113,8 +113,30 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||
onTranslate(status);
|
||||
};
|
||||
|
||||
_properStatus () {
|
||||
const { status } = this.props;
|
||||
|
||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
return status.get('reblog');
|
||||
} else {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
getAttachmentAspectRatio () {
|
||||
const attachments = this._properStatus().get('media_attachments');
|
||||
|
||||
if (attachments.getIn([0, 'type']) === 'video') {
|
||||
return `${attachments.getIn([0, 'meta', 'original', 'width'])} / ${attachments.getIn([0, 'meta', 'original', 'height'])}`;
|
||||
} else if (attachments.getIn([0, 'type']) === 'audio') {
|
||||
return '16 / 9';
|
||||
} else {
|
||||
return (attachments.size === 1 && attachments.getIn([0, 'meta', 'small', 'aspect'])) ? attachments.getIn([0, 'meta', 'small', 'aspect']) : '3 / 2'
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
||||
const status = this._properStatus();
|
||||
const outerStyle = { boxSizing: 'border-box' };
|
||||
const { intl, compact, pictureInPicture } = this.props;
|
||||
|
||||
@@ -136,7 +158,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||
const language = status.getIn(['translation', 'language']) || status.get('language');
|
||||
|
||||
if (pictureInPicture.get('inUse')) {
|
||||
media = <PictureInPicturePlaceholder />;
|
||||
media = <PictureInPicturePlaceholder aspectRatio={this.getAttachmentAspectRatio()} />;
|
||||
} else if (status.get('media_attachments').size > 0) {
|
||||
if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
||||
const attachment = status.getIn(['media_attachments', 0]);
|
||||
@@ -167,13 +189,13 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||
<Video
|
||||
preview={attachment.get('preview_url')}
|
||||
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
||||
aspectRatio={`${attachment.getIn(['meta', 'original', 'width'])} / ${attachment.getIn(['meta', 'original', 'height'])}`}
|
||||
blurhash={attachment.get('blurhash')}
|
||||
src={attachment.get('url')}
|
||||
alt={description}
|
||||
lang={language}
|
||||
width={300}
|
||||
height={150}
|
||||
inline
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
sensitive={status.get('sensitive')}
|
||||
visible={this.props.showMedia}
|
||||
|
||||
@@ -172,6 +172,7 @@ class MediaModal extends ImmutablePureComponent {
|
||||
width={image.get('width')}
|
||||
height={image.get('height')}
|
||||
frameRate={image.getIn(['meta', 'original', 'frame_rate'])}
|
||||
aspectRatio={`${image.getIn(['meta', 'original', 'width'])} / ${image.getIn(['meta', 'original', 'height'])}`}
|
||||
currentTime={currentTime || 0}
|
||||
autoPlay={autoPlay || false}
|
||||
volume={volume || 1}
|
||||
|
||||
@@ -49,6 +49,7 @@ class VideoModal extends ImmutablePureComponent {
|
||||
<Video
|
||||
preview={media.get('preview_url')}
|
||||
frameRate={media.getIn(['meta', 'original', 'frame_rate'])}
|
||||
aspectRatio={`${media.getIn(['meta', 'original', 'width'])} / ${media.getIn(['meta', 'original', 'height'])}`}
|
||||
blurhash={media.get('blurhash')}
|
||||
src={media.get('url')}
|
||||
currentTime={options.startTime}
|
||||
|
||||
@@ -105,6 +105,7 @@ class Video extends PureComponent {
|
||||
static propTypes = {
|
||||
preview: PropTypes.string,
|
||||
frameRate: PropTypes.string,
|
||||
aspectRatio: PropTypes.string,
|
||||
src: PropTypes.string.isRequired,
|
||||
alt: PropTypes.string,
|
||||
lang: PropTypes.string,
|
||||
@@ -113,7 +114,6 @@ class Video extends PureComponent {
|
||||
onOpenVideo: PropTypes.func,
|
||||
onCloseVideo: PropTypes.func,
|
||||
detailed: PropTypes.bool,
|
||||
inline: PropTypes.bool,
|
||||
editable: PropTypes.bool,
|
||||
alwaysVisible: PropTypes.bool,
|
||||
visible: PropTypes.bool,
|
||||
@@ -500,14 +500,9 @@ class Video extends PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
|
||||
const { preview, src, aspectRatio, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
|
||||
const { currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
||||
const progress = Math.min((currentTime / duration) * 100, 100);
|
||||
const playerStyle = {};
|
||||
|
||||
if (inline) {
|
||||
playerStyle.aspectRatio = '16 / 9';
|
||||
}
|
||||
|
||||
let preload;
|
||||
|
||||
@@ -527,95 +522,101 @@ class Video extends PureComponent {
|
||||
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
|
||||
role='menuitem'
|
||||
className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, editable })}
|
||||
style={playerStyle}
|
||||
ref={this.setPlayerRef}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
onClick={this.handleClickRoot}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
tabIndex={0}
|
||||
>
|
||||
<Blurhash
|
||||
hash={blurhash}
|
||||
className={classNames('media-gallery__preview', {
|
||||
'media-gallery__preview--hidden': revealed,
|
||||
})}
|
||||
dummy={!useBlurhash}
|
||||
/>
|
||||
|
||||
{(revealed || editable) && <video
|
||||
ref={this.setVideoRef}
|
||||
src={src}
|
||||
poster={preview}
|
||||
preload={preload}
|
||||
role='button'
|
||||
<div style={{ aspectRatio }}>
|
||||
<div
|
||||
role='menuitem'
|
||||
className={classNames('video-player', { inactive: !revealed, detailed, fullscreen, editable })}
|
||||
style={{ aspectRatio }}
|
||||
ref={this.setPlayerRef}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
onClick={this.handleClickRoot}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
tabIndex={0}
|
||||
aria-label={alt}
|
||||
title={alt}
|
||||
lang={lang}
|
||||
volume={volume}
|
||||
onClick={this.togglePlay}
|
||||
onKeyDown={this.handleVideoKeyDown}
|
||||
onPlay={this.handlePlay}
|
||||
onPause={this.handlePause}
|
||||
onLoadedData={this.handleLoadedData}
|
||||
onProgress={this.handleProgress}
|
||||
onVolumeChange={this.handleVolumeChange}
|
||||
style={{ ...playerStyle, width: '100%' }}
|
||||
/>}
|
||||
>
|
||||
<Blurhash
|
||||
hash={blurhash}
|
||||
className={classNames('media-gallery__preview', {
|
||||
'media-gallery__preview--hidden': revealed,
|
||||
})}
|
||||
dummy={!useBlurhash}
|
||||
/>
|
||||
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
{(revealed || editable) && <video
|
||||
ref={this.setVideoRef}
|
||||
src={src}
|
||||
poster={preview}
|
||||
preload={preload}
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
aria-label={alt}
|
||||
title={alt}
|
||||
lang={lang}
|
||||
volume={volume}
|
||||
onClick={this.togglePlay}
|
||||
onKeyDown={this.handleVideoKeyDown}
|
||||
onPlay={this.handlePlay}
|
||||
onPause={this.handlePause}
|
||||
onLoadedData={this.handleLoadedData}
|
||||
onProgress={this.handleProgress}
|
||||
onVolumeChange={this.handleVolumeChange}
|
||||
style={{ width: '100%' }}
|
||||
/>}
|
||||
|
||||
<div className={classNames('video-player__controls', { active: paused || hovered })}>
|
||||
<div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
|
||||
<div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
|
||||
<div className='video-player__seek__progress' style={{ width: `${progress}%` }} />
|
||||
|
||||
<span
|
||||
className={classNames('video-player__seek__handle', { active: dragging })}
|
||||
tabIndex={0}
|
||||
style={{ left: `${progress}%` }}
|
||||
onKeyDown={this.handleVideoKeyDown}
|
||||
/>
|
||||
<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>
|
||||
|
||||
<div className='video-player__buttons-bar'>
|
||||
<div className='video-player__buttons left'>
|
||||
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={autoFocus}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
|
||||
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
|
||||
<div className={classNames('video-player__controls', { active: paused || hovered })}>
|
||||
<div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
|
||||
<div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
|
||||
<div className='video-player__seek__progress' style={{ width: `${progress}%` }} />
|
||||
|
||||
<div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
||||
<div className='video-player__volume__current' style={{ width: `${volume * 100}%` }} />
|
||||
|
||||
<span
|
||||
className={classNames('video-player__volume__handle')}
|
||||
tabIndex={0}
|
||||
style={{ left: `${volume * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{(detailed || fullscreen) && (
|
||||
<span className='video-player__time'>
|
||||
<span className='video-player__time-current'>{formatTime(Math.floor(currentTime))}</span>
|
||||
<span className='video-player__time-sep'>/</span>
|
||||
<span className='video-player__time-total'>{formatTime(Math.floor(duration))}</span>
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={classNames('video-player__seek__handle', { active: dragging })}
|
||||
tabIndex={0}
|
||||
style={{ left: `${progress}%` }}
|
||||
onKeyDown={this.handleVideoKeyDown}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='video-player__buttons right'>
|
||||
{(!onCloseVideo && !editable && !fullscreen && !this.props.alwaysVisible) && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
|
||||
{(!fullscreen && onOpenVideo) && <button type='button' title={intl.formatMessage(messages.expand)} aria-label={intl.formatMessage(messages.expand)} className='player-button' onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>}
|
||||
{onCloseVideo && <button type='button' title={intl.formatMessage(messages.close)} aria-label={intl.formatMessage(messages.close)} className='player-button' onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>}
|
||||
<button type='button' title={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} className='player-button' onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button>
|
||||
<div className='video-player__buttons-bar'>
|
||||
<div className='video-player__buttons left'>
|
||||
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={autoFocus}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
|
||||
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
|
||||
|
||||
<div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
||||
<div className='video-player__volume__current' style={{ width: `${volume * 100}%` }} />
|
||||
|
||||
<span
|
||||
className={classNames('video-player__volume__handle')}
|
||||
tabIndex={0}
|
||||
style={{ left: `${volume * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{(detailed || fullscreen) && (
|
||||
<span className='video-player__time'>
|
||||
<span className='video-player__time-current'>{formatTime(Math.floor(currentTime))}</span>
|
||||
<span className='video-player__time-sep'>/</span>
|
||||
<span className='video-player__time-total'>{formatTime(Math.floor(duration))}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='video-player__buttons right'>
|
||||
{(!onCloseVideo && !editable && !fullscreen && !this.props.alwaysVisible) && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
|
||||
{(!fullscreen && onOpenVideo) && <button type='button' title={intl.formatMessage(messages.expand)} aria-label={intl.formatMessage(messages.expand)} className='player-button' onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>}
|
||||
{onCloseVideo && <button type='button' title={intl.formatMessage(messages.close)} aria-label={intl.formatMessage(messages.close)} className='player-button' onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>}
|
||||
<button type='button' title={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} className='player-button' onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user