Merge pull request #489 from ThibG/glitch-soc/features/port-public-ui-changes

Port public UI changes from upstream
lolsob-rspec
David Yip 2018-05-18 07:53:53 -05:00 committed by GitHub
commit e6e5d6ca4d
11 changed files with 147 additions and 140 deletions

View File

@ -259,8 +259,8 @@ export default class Status extends ImmutablePureComponent {
} }
}; };
handleOpenVideo = startTime => { handleOpenVideo = (media, startTime) => {
this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), startTime); this.props.onOpenVideo(media, startTime);
} }
handleHotkeyReply = e => { handleHotkeyReply = e => {

View File

@ -1,18 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Card from 'flavours/glitch/features/status/components/card';
import { fromJS } from 'immutable';
export default class CardContainer extends React.PureComponent {
static propTypes = {
locale: PropTypes.string,
card: PropTypes.array.isRequired,
};
render () {
const { card, ...props } = this.props;
return <Card card={fromJS(card)} {...props} />;
}
}

View File

@ -0,0 +1,90 @@
import React, { PureComponent, Fragment } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { IntlProvider, addLocaleData } from 'react-intl';
import { getLocale } from 'mastodon/locales';
import MediaGallery from 'flavours/glitch/components/media_gallery';
import Video from 'flavours/glitch/features/video';
import Card from 'flavours/glitch/features/status/components/card';
import ModalRoot from 'flavours/glitch/components/modal_root';
import MediaModal from 'flavours/glitch/features/ui/components/media_modal';
import { List as ImmutableList, fromJS } from 'immutable';
const { localeData, messages } = getLocale();
addLocaleData(localeData);
const MEDIA_COMPONENTS = { MediaGallery, Video, Card };
export default class MediaContainer extends PureComponent {
static propTypes = {
locale: PropTypes.string.isRequired,
components: PropTypes.object.isRequired,
};
state = {
media: null,
index: null,
time: null,
};
handleOpenMedia = (media, index) => {
document.body.classList.add('media-standalone__body');
this.setState({ media, index });
}
handleOpenVideo = (video, time) => {
const media = ImmutableList([video]);
document.body.classList.add('media-standalone__body');
this.setState({ media, time });
}
handleCloseMedia = () => {
document.body.classList.remove('media-standalone__body');
this.setState({ media: null, index: null, time: null });
}
render () {
const { locale, components } = this.props;
return (
<IntlProvider locale={locale} messages={messages}>
<Fragment>
{[].map.call(components, (component, i) => {
const componentName = component.getAttribute('data-component');
const Component = MEDIA_COMPONENTS[componentName];
const { media, card, ...props } = JSON.parse(component.getAttribute('data-props'));
Object.assign(props, {
...(media ? { media: fromJS(media) } : {}),
...(card ? { card: fromJS(card) } : {}),
...(componentName === 'Video' ? {
onOpenVideo: this.handleOpenVideo,
} : {
onOpenMedia: this.handleOpenMedia,
}),
});
return ReactDOM.createPortal(
<Component {...props} key={`media-${i}`} />,
component,
);
})}
<ModalRoot onClose={this.handleCloseMedia}>
{this.state.media && (
<MediaModal
media={this.state.media}
index={this.state.index || 0}
time={this.state.time}
onClose={this.handleCloseMedia}
/>
)}
</ModalRoot>
</Fragment>
</IntlProvider>
);
}
}

View File

@ -1,68 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { IntlProvider, addLocaleData } from 'react-intl';
import { getLocale } from 'mastodon/locales';
import MediaGallery from 'flavours/glitch/components/media_gallery';
import ModalRoot from 'flavours/glitch/components/modal_root';
import MediaModal from 'flavours/glitch/features/ui/components/media_modal';
import { fromJS } from 'immutable';
const { localeData, messages } = getLocale();
addLocaleData(localeData);
export default class MediaGalleriesContainer extends React.PureComponent {
static propTypes = {
locale: PropTypes.string.isRequired,
galleries: PropTypes.object.isRequired,
};
state = {
media: null,
index: null,
};
handleOpenMedia = (media, index) => {
document.body.classList.add('media-gallery-standalone__body');
this.setState({ media, index });
}
handleCloseMedia = () => {
document.body.classList.remove('media-gallery-standalone__body');
this.setState({ media: null, index: null });
}
render () {
const { locale, galleries } = this.props;
return (
<IntlProvider locale={locale} messages={messages}>
<React.Fragment>
{[].map.call(galleries, gallery => {
const { media, ...props } = JSON.parse(gallery.getAttribute('data-props'));
return ReactDOM.createPortal(
<MediaGallery
{...props}
media={fromJS(media)}
onOpenMedia={this.handleOpenMedia}
/>,
gallery
);
})}
<ModalRoot onClose={this.handleCloseMedia}>
{this.state.media === null || this.state.index === null ? null : (
<MediaModal
media={this.state.media}
index={this.state.index}
onClose={this.handleCloseMedia}
/>
)}
</ModalRoot>
</React.Fragment>
</IntlProvider>
);
}
}

View File

@ -1,26 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { IntlProvider, addLocaleData } from 'react-intl';
import { getLocale } from 'mastodon/locales';
import Video from 'flavours/glitch/features/video';
const { localeData, messages } = getLocale();
addLocaleData(localeData);
export default class VideoContainer extends React.PureComponent {
static propTypes = {
locale: PropTypes.string.isRequired,
};
render () {
const { locale, ...props } = this.props;
return (
<IntlProvider locale={locale} messages={messages}>
<Video {...props} />
</IntlProvider>
);
}
}

View File

@ -37,8 +37,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
e.stopPropagation(); e.stopPropagation();
} }
handleOpenVideo = startTime => { handleOpenVideo = (media, startTime) => {
this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), startTime); this.props.onOpenVideo(media, startTime);
} }
render () { render () {

View File

@ -2,6 +2,7 @@ import React from 'react';
import ReactSwipeableViews from 'react-swipeable-views'; import ReactSwipeableViews from 'react-swipeable-views';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Video from 'flavours/glitch/features/video';
import ExtendedVideoPlayer from 'flavours/glitch/components/extended_video_player'; import ExtendedVideoPlayer from 'flavours/glitch/components/extended_video_player';
import classNames from 'classnames'; import classNames from 'classnames';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
@ -112,6 +113,22 @@ export default class MediaModal extends ImmutablePureComponent {
onClick={this.toggleNavigation} onClick={this.toggleNavigation}
/> />
); );
} else if (image.get('type') === 'video') {
const { time } = this.props;
return (
<Video
preview={image.get('preview_url')}
src={image.get('url')}
width={image.get('width')}
height={image.get('height')}
startTime={time || 0}
onCloseVideo={onClose}
detailed
description={image.get('description')}
key={image.get('url')}
/>
);
} else if (image.get('type') === 'gifv') { } else if (image.get('type') === 'gifv') {
return ( return (
<ExtendedVideoPlayer <ExtendedVideoPlayer

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { fromJS } from 'immutable';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import classNames from 'classnames'; import classNames from 'classnames';
import { isFullscreen, requestFullscreen, exitFullscreen } from 'flavours/glitch/util/fullscreen'; import { isFullscreen, requestFullscreen, exitFullscreen } from 'flavours/glitch/util/fullscreen';
@ -133,6 +134,8 @@ export default class Video extends React.PureComponent {
this.seek = c; this.seek = c;
} }
handleClickRoot = e => e.stopPropagation();
handlePlay = () => { handlePlay = () => {
this.setState({ paused: false }); this.setState({ paused: false });
} }
@ -246,8 +249,17 @@ export default class Video extends React.PureComponent {
} }
handleOpenVideo = () => { handleOpenVideo = () => {
const { src, preview, width, height } = this.props;
const media = fromJS({
type: 'video',
url: src,
preview_url: preview,
width,
height,
});
this.video.pause(); this.video.pause();
this.props.onOpenVideo(this.video.currentTime); this.props.onOpenVideo(media, this.video.currentTime);
} }
handleCloseVideo = () => { handleCloseVideo = () => {
@ -279,7 +291,15 @@ export default class Video extends React.PureComponent {
} }
return ( return (
<div className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, letterbox, 'full-width': fullwidth })} style={playerStyle} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> <div
className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, letterbox, 'full-width': fullwidth })}
style={playerStyle}
ref={this.setPlayerRef}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onClick={this.handleClickRoot}
tabIndex={0}
>
<video <video
ref={this.setVideoRef} ref={this.setVideoRef}
src={src} src={src}

View File

@ -6,8 +6,6 @@ function main() {
const emojify = require('flavours/glitch/util/emoji').default; const emojify = require('flavours/glitch/util/emoji').default;
const { getLocale } = require('locales'); const { getLocale } = require('locales');
const { localeData } = getLocale(); const { localeData } = getLocale();
const VideoContainer = require('flavours/glitch/containers/video_container').default;
const CardContainer = require('flavours/glitch/containers/card_container').default;
const React = require('react'); const React = require('react');
const ReactDOM = require('react-dom'); const ReactDOM = require('react-dom');
@ -52,24 +50,15 @@ function main() {
}); });
}); });
[].forEach.call(document.querySelectorAll('[data-component="Video"]'), (content) => { const reactComponents = document.querySelectorAll('[data-component]');
const props = JSON.parse(content.getAttribute('data-props')); if (reactComponents.length > 0) {
ReactDOM.render(<VideoContainer locale={locale} {...props} />, content); import(/* webpackChunkName: "containers/media_container" */ 'flavours/glitch/containers/media_container')
}); .then(({ default: MediaContainer }) => {
const content = document.createElement('div');
[].forEach.call(document.querySelectorAll('[data-component="Card"]'), (content) => { ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content);
const props = JSON.parse(content.getAttribute('data-props')); document.body.appendChild(content);
ReactDOM.render(<CardContainer locale={locale} {...props} />, content); })
}); .catch(error => console.error(error));
const mediaGalleries = document.querySelectorAll('[data-component="MediaGallery"]');
if (mediaGalleries.length > 0) {
const MediaGalleriesContainer = require('flavours/glitch/containers/media_galleries_container').default;
const content = document.createElement('div');
ReactDOM.render(<MediaGalleriesContainer locale={locale} galleries={mediaGalleries} />, content);
document.body.appendChild(content);
} }
}); });
} }

View File

@ -279,6 +279,10 @@
background: $base-shadow-color; background: $base-shadow-color;
max-width: 100%; max-width: 100%;
&:focus {
outline: 0;
}
.detailed-status & { .detailed-status & {
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -60,8 +60,7 @@
} }
} }
.card-standalone__body, .media-standalone__body {
.media-gallery-standalone__body {
overflow: hidden; overflow: hidden;
} }