[Glitch] Add audio player
Port 4190e31626
to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
This commit is contained in:
parent
04de74c992
commit
3aeaf9b897
10 changed files with 325 additions and 15 deletions
|
@ -10,7 +10,7 @@ import AttachmentList from './attachment_list';
|
||||||
import Card from '../features/status/components/card';
|
import Card from '../features/status/components/card';
|
||||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { MediaGallery, Video } from 'flavours/glitch/util/async-components';
|
import { MediaGallery, Video, Audio } from 'flavours/glitch/util/async-components';
|
||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
|
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -443,11 +443,15 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingMediaGallery () {
|
renderLoadingMediaGallery () {
|
||||||
return <div className='media_gallery' style={{ height: '110px' }} />;
|
return <div className='media-gallery' style={{ height: '110px' }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingVideoPlayer () {
|
renderLoadingVideoPlayer () {
|
||||||
return <div className='media-spoiler-video' style={{ height: '110px' }} />;
|
return <div className='video-player' style={{ height: '110px' }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLoadingAudioPlayer () {
|
||||||
|
return <div className='audio-player' style={{ height: '110px' }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
@ -561,7 +565,23 @@ class Status extends ImmutablePureComponent {
|
||||||
media={status.get('media_attachments')}
|
media={status.get('media_attachments')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (['video', 'audio'].includes(attachments.getIn([0, 'type']))) {
|
} else if (attachments.getIn([0, 'type']) === 'audio') {
|
||||||
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
|
media = (
|
||||||
|
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
||||||
|
{Component => (
|
||||||
|
<Component
|
||||||
|
src={attachment.get('url')}
|
||||||
|
alt={attachment.get('description')}
|
||||||
|
duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
|
||||||
|
height={110}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Bundle>
|
||||||
|
);
|
||||||
|
mediaIcon = 'music';
|
||||||
|
} else if (attachments.getIn([0, 'type']) === 'video') {
|
||||||
const attachment = status.getIn(['media_attachments', 0]);
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
media = (
|
media = (
|
||||||
|
@ -584,7 +604,7 @@ class Status extends ImmutablePureComponent {
|
||||||
/>)}
|
/>)}
|
||||||
</Bundle>
|
</Bundle>
|
||||||
);
|
);
|
||||||
mediaIcon = attachment.get('type') === 'video' ? 'video-camera' : 'music';
|
mediaIcon = 'video-camera';
|
||||||
} else { // Media type is 'image' or 'gifv'
|
} else { // Media type is 'image' or 'gifv'
|
||||||
media = (
|
media = (
|
||||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import MediaGallery from 'flavours/glitch/components/media_gallery';
|
||||||
import Video from 'flavours/glitch/features/video';
|
import Video from 'flavours/glitch/features/video';
|
||||||
import Card from 'flavours/glitch/features/status/components/card';
|
import Card from 'flavours/glitch/features/status/components/card';
|
||||||
import Poll from 'flavours/glitch/components/poll';
|
import Poll from 'flavours/glitch/components/poll';
|
||||||
|
import Audio from 'flavours/glitch/features/audio';
|
||||||
import ModalRoot from 'flavours/glitch/components/modal_root';
|
import ModalRoot from 'flavours/glitch/components/modal_root';
|
||||||
import MediaModal from 'flavours/glitch/features/ui/components/media_modal';
|
import MediaModal from 'flavours/glitch/features/ui/components/media_modal';
|
||||||
import { List as ImmutableList, fromJS } from 'immutable';
|
import { List as ImmutableList, fromJS } from 'immutable';
|
||||||
|
@ -14,7 +15,7 @@ import { List as ImmutableList, fromJS } from 'immutable';
|
||||||
const { localeData, messages } = getLocale();
|
const { localeData, messages } = getLocale();
|
||||||
addLocaleData(localeData);
|
addLocaleData(localeData);
|
||||||
|
|
||||||
const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll };
|
const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll, Audio };
|
||||||
|
|
||||||
export default class MediaContainer extends PureComponent {
|
export default class MediaContainer extends PureComponent {
|
||||||
|
|
||||||
|
|
219
app/javascript/flavours/glitch/features/audio/index.js
Normal file
219
app/javascript/flavours/glitch/features/audio/index.js
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import WaveSurfer from 'wavesurfer.js';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import { formatTime } from 'flavours/glitch/features/video';
|
||||||
|
import Icon from 'flavours/glitch/components/icon';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { throttle } from 'lodash';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
play: { id: 'video.play', defaultMessage: 'Play' },
|
||||||
|
pause: { id: 'video.pause', defaultMessage: 'Pause' },
|
||||||
|
mute: { id: 'video.mute', defaultMessage: 'Mute sound' },
|
||||||
|
unmute: { id: 'video.unmute', defaultMessage: 'Unmute sound' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const arrayOf = (length, fill) => (new Array(length)).fill(fill);
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class Audio extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
src: PropTypes.string.isRequired,
|
||||||
|
alt: PropTypes.string,
|
||||||
|
duration: PropTypes.number,
|
||||||
|
height: PropTypes.number,
|
||||||
|
preload: PropTypes.bool,
|
||||||
|
editable: PropTypes.bool,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
currentTime: 0,
|
||||||
|
duration: null,
|
||||||
|
paused: true,
|
||||||
|
muted: false,
|
||||||
|
volume: 0.5,
|
||||||
|
};
|
||||||
|
|
||||||
|
// hard coded in components.scss
|
||||||
|
// any way to get ::before values programatically?
|
||||||
|
|
||||||
|
volWidth = 50;
|
||||||
|
|
||||||
|
volOffset = 70;
|
||||||
|
|
||||||
|
volHandleOffset = v => {
|
||||||
|
const offset = v * this.volWidth + this.volOffset;
|
||||||
|
return (offset > 110) ? 110 : offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVolumeRef = c => {
|
||||||
|
this.volume = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
setWaveformRef = c => {
|
||||||
|
this.waveform = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
if (this.waveform) {
|
||||||
|
this._updateWaveform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.waveform && prevProps.src !== this.props.src) {
|
||||||
|
this._updateWaveform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
if (this.wavesurfer) {
|
||||||
|
this.wavesurfer.destroy();
|
||||||
|
this.wavesurfer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateWaveform () {
|
||||||
|
const { src, height, duration, preload } = this.props;
|
||||||
|
|
||||||
|
const progressColor = window.getComputedStyle(document.querySelector('.audio-player__progress-placeholder')).getPropertyValue('background-color');
|
||||||
|
const waveColor = window.getComputedStyle(document.querySelector('.audio-player__wave-placeholder')).getPropertyValue('background-color');
|
||||||
|
|
||||||
|
if (this.wavesurfer) {
|
||||||
|
this.wavesurfer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wavesurfer = WaveSurfer.create({
|
||||||
|
container: this.waveform,
|
||||||
|
height,
|
||||||
|
barWidth: 3,
|
||||||
|
cursorWidth: 0,
|
||||||
|
progressColor,
|
||||||
|
waveColor,
|
||||||
|
forceDecode: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
wavesurfer.setVolume(this.state.volume);
|
||||||
|
|
||||||
|
if (preload) {
|
||||||
|
wavesurfer.load(src);
|
||||||
|
} else {
|
||||||
|
wavesurfer.load(src, arrayOf(1, 0.5), null, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
wavesurfer.on('ready', () => this.setState({ duration: Math.floor(wavesurfer.getDuration()) }));
|
||||||
|
wavesurfer.on('audioprocess', () => this.setState({ currentTime: Math.floor(wavesurfer.getCurrentTime()) }));
|
||||||
|
wavesurfer.on('pause', () => this.setState({ paused: true }));
|
||||||
|
wavesurfer.on('play', () => this.setState({ paused: false }));
|
||||||
|
wavesurfer.on('volume', volume => this.setState({ volume }));
|
||||||
|
wavesurfer.on('mute', muted => this.setState({ muted }));
|
||||||
|
|
||||||
|
this.wavesurfer = wavesurfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePlay = () => {
|
||||||
|
if (this.state.paused) {
|
||||||
|
if (!this.props.preload) {
|
||||||
|
this.wavesurfer.createBackend();
|
||||||
|
this.wavesurfer.createPeakCache();
|
||||||
|
this.wavesurfer.load(this.props.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wavesurfer.play();
|
||||||
|
} else {
|
||||||
|
this.wavesurfer.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMute = () => {
|
||||||
|
this.wavesurfer.setMute(!this.state.muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleVolumeMouseDown = e => {
|
||||||
|
document.addEventListener('mousemove', this.handleMouseVolSlide, true);
|
||||||
|
document.addEventListener('mouseup', this.handleVolumeMouseUp, true);
|
||||||
|
document.addEventListener('touchmove', this.handleMouseVolSlide, true);
|
||||||
|
document.addEventListener('touchend', this.handleVolumeMouseUp, true);
|
||||||
|
|
||||||
|
this.handleMouseVolSlide(e);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleVolumeMouseUp = () => {
|
||||||
|
document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
|
||||||
|
document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
|
||||||
|
document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
|
||||||
|
document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseVolSlide = throttle(e => {
|
||||||
|
const rect = this.volume.getBoundingClientRect();
|
||||||
|
const x = (e.clientX - rect.left) / this.volWidth; // x position within the element.
|
||||||
|
|
||||||
|
if(!isNaN(x)) {
|
||||||
|
let slideamt = x;
|
||||||
|
|
||||||
|
if (x > 1) {
|
||||||
|
slideamt = 1;
|
||||||
|
} else if(x < 0) {
|
||||||
|
slideamt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wavesurfer.setVolume(slideamt);
|
||||||
|
}
|
||||||
|
}, 60);
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { height, intl, alt, editable } = this.props;
|
||||||
|
const { paused, muted, volume, currentTime } = this.state;
|
||||||
|
|
||||||
|
const volumeWidth = muted ? 0 : volume * this.volWidth;
|
||||||
|
const volumeHandleLoc = muted ? this.volHandleOffset(0) : this.volHandleOffset(volume);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('audio-player', { editable })}>
|
||||||
|
<div className='audio-player__progress-placeholder' style={{ display: 'none' }} />
|
||||||
|
<div className='audio-player__wave-placeholder' style={{ display: 'none' }} />
|
||||||
|
|
||||||
|
<div
|
||||||
|
className='audio-player__waveform'
|
||||||
|
aria-label={alt}
|
||||||
|
title={alt}
|
||||||
|
style={{ height }}
|
||||||
|
ref={this.setWaveformRef}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='video-player__controls active'>
|
||||||
|
<div className='video-player__buttons-bar'>
|
||||||
|
<div className='video-player__buttons left'>
|
||||||
|
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><Icon icon={paused ? 'play' : 'pause'} fixedWidth /></button>
|
||||||
|
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><Icon icon={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
|
||||||
|
|
||||||
|
<div className='video-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
||||||
|
<div className='video-player__volume__current' style={{ width: `${volumeWidth}px` }} />
|
||||||
|
|
||||||
|
<span
|
||||||
|
className={classNames('video-player__volume__handle')}
|
||||||
|
tabIndex='0'
|
||||||
|
style={{ left: `${volumeHandleLoc}px` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<span className='video-player__time-current'>{formatTime(currentTime)}</span>
|
||||||
|
<span className='video-player__time-sep'>/</span>
|
||||||
|
<span className='video-player__time-total'>{formatTime(this.state.duration || Math.floor(this.props.duration))}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import { FormattedDate, FormattedNumber } from 'react-intl';
|
||||||
import Card from './card';
|
import Card from './card';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Video from 'flavours/glitch/features/video';
|
import Video from 'flavours/glitch/features/video';
|
||||||
|
import Audio from 'flavours/glitch/features/audio';
|
||||||
import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
|
import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
|
||||||
import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task';
|
import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -131,7 +132,20 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
} else if (status.get('media_attachments').size > 0) {
|
} else if (status.get('media_attachments').size > 0) {
|
||||||
if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
|
if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
|
||||||
media = <AttachmentList media={status.get('media_attachments')} />;
|
media = <AttachmentList media={status.get('media_attachments')} />;
|
||||||
} else if (['video', 'audio'].includes(status.getIn(['media_attachments', 0, 'type']))) {
|
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
||||||
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
|
media = (
|
||||||
|
<Audio
|
||||||
|
src={attachment.get('url')}
|
||||||
|
alt={attachment.get('description')}
|
||||||
|
duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
|
||||||
|
height={150}
|
||||||
|
preload
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
mediaIcon = 'music';
|
||||||
|
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||||
const attachment = status.getIn(['media_attachments', 0]);
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
media = (
|
media = (
|
||||||
<Video
|
<Video
|
||||||
|
@ -150,7 +164,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
onToggleVisibility={this.props.onToggleMediaVisibility}
|
onToggleVisibility={this.props.onToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
mediaIcon = attachment.get('type') === 'video' ? 'video-camera' : 'music';
|
mediaIcon = 'video-camera';
|
||||||
} else {
|
} else {
|
||||||
media = (
|
media = (
|
||||||
<MediaGallery
|
<MediaGallery
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||||
import IconButton from 'flavours/glitch/components/icon_button';
|
import IconButton from 'flavours/glitch/components/icon_button';
|
||||||
import Button from 'flavours/glitch/components/button';
|
import Button from 'flavours/glitch/components/button';
|
||||||
import Video from 'flavours/glitch/features/video';
|
import Video from 'flavours/glitch/features/video';
|
||||||
|
import Audio from 'flavours/glitch/features/audio';
|
||||||
import Textarea from 'react-textarea-autosize';
|
import Textarea from 'react-textarea-autosize';
|
||||||
import UploadProgress from 'flavours/glitch/features/compose/components/upload_progress';
|
import UploadProgress from 'flavours/glitch/features/compose/components/upload_progress';
|
||||||
import CharacterCounter from 'flavours/glitch/features/compose/components/character_counter';
|
import CharacterCounter from 'flavours/glitch/features/compose/components/character_counter';
|
||||||
|
@ -244,12 +245,23 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{['audio', 'video'].includes(media.get('type')) && (
|
{media.get('type') === 'video' && (
|
||||||
<Video
|
<Video
|
||||||
preview={media.get('preview_url')}
|
preview={media.get('preview_url')}
|
||||||
blurhash={media.get('blurhash')}
|
blurhash={media.get('blurhash')}
|
||||||
src={media.get('url')}
|
src={media.get('url')}
|
||||||
detailed
|
detailed
|
||||||
|
inline
|
||||||
|
editable
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{media.get('type') === 'audio' && (
|
||||||
|
<Audio
|
||||||
|
src={media.get('url')}
|
||||||
|
duration={media.getIn(['meta', 'original', 'duration'], 0)}
|
||||||
|
height={150}
|
||||||
|
preload
|
||||||
editable
|
editable
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -20,7 +20,7 @@ const messages = defineMessages({
|
||||||
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
|
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const formatTime = secondsNum => {
|
export const formatTime = secondsNum => {
|
||||||
let hours = Math.floor(secondsNum / 3600);
|
let hours = Math.floor(secondsNum / 3600);
|
||||||
let minutes = Math.floor((secondsNum - (hours * 3600)) / 60);
|
let minutes = Math.floor((secondsNum - (hours * 3600)) / 60);
|
||||||
let seconds = secondsNum - (hours * 3600) - (minutes * 60);
|
let seconds = secondsNum - (hours * 3600) - (minutes * 60);
|
||||||
|
|
|
@ -333,15 +333,50 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.audio-player {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
background: darken($ui-base-color, 8%);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-bottom: 44px;
|
||||||
|
|
||||||
|
&.editable {
|
||||||
|
border-radius: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waveform {
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__progress-placeholder {
|
||||||
|
background-color: rgba(lighten($ui-highlight-color, 8%), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__wave-placeholder {
|
||||||
|
background-color: lighten($ui-base-color, 16%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player__controls {
|
||||||
|
padding: 0 15px;
|
||||||
|
padding-top: 10px;
|
||||||
|
background: darken($ui-base-color, 8%);
|
||||||
|
border-top: 1px solid lighten($ui-base-color, 4%);
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.video-player {
|
.video-player {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: $base-shadow-color;
|
background: $base-shadow-color;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
&.editable {
|
&.editable {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
|
|
|
@ -107,7 +107,8 @@
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
|
||||||
.media-gallery,
|
.media-gallery,
|
||||||
.video-player {
|
.video-player,
|
||||||
|
.audio-player {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +132,8 @@
|
||||||
|
|
||||||
.media-gallery,
|
.media-gallery,
|
||||||
&__action-bar,
|
&__action-bar,
|
||||||
.video-player {
|
.video-player,
|
||||||
|
.audio-player {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,7 +263,8 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
animation: fade 150ms linear;
|
animation: fade 150ms linear;
|
||||||
|
|
||||||
.video-player {
|
.video-player,
|
||||||
|
.audio-player {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +454,8 @@
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-player {
|
.video-player,
|
||||||
|
.audio-player {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
}
|
}
|
||||||
|
@ -561,7 +563,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-player {
|
.video-player,
|
||||||
|
.audio-player {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,6 +138,10 @@ export function Video () {
|
||||||
return import(/* webpackChunkName: "flavours/glitch/async/video" */'flavours/glitch/features/video');
|
return import(/* webpackChunkName: "flavours/glitch/async/video" */'flavours/glitch/features/video');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Audio () {
|
||||||
|
return import(/* webpackChunkName: "features/glitch/async/audio" */'flavours/glitch/features/audio');
|
||||||
|
}
|
||||||
|
|
||||||
export function EmbedModal () {
|
export function EmbedModal () {
|
||||||
return import(/* webpackChunkName: "flavours/glitch/async/embed_modal" */'flavours/glitch/features/ui/components/embed_modal');
|
return import(/* webpackChunkName: "flavours/glitch/async/embed_modal" */'flavours/glitch/features/ui/components/embed_modal');
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue