Merge upstream 2.0ish #165
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Link from 'react-router-dom/Link';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const ColumnLink = ({ icon, text, to, onClick, href, method }) => {
|
||||
if (href) {
|
||||
|
@@ -29,7 +29,7 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
handleSwipe = (index) => {
|
||||
this.setState({ index: (index) % this.props.media.size });
|
||||
this.setState({ index: index % this.props.media.size });
|
||||
}
|
||||
|
||||
handleNextClick = () => {
|
||||
@@ -40,6 +40,11 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
this.setState({ index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size });
|
||||
}
|
||||
|
||||
handleChangeIndex = (e) => {
|
||||
const index = Number(e.currentTarget.getAttribute('data-index'));
|
||||
this.setState({ index: index % this.props.media.size });
|
||||
}
|
||||
|
||||
handleKeyUp = (e) => {
|
||||
switch(e.key) {
|
||||
case 'ArrowLeft':
|
||||
@@ -67,33 +72,51 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
const { media, intl, onClose } = this.props;
|
||||
|
||||
const index = this.getIndex();
|
||||
let pagination = [];
|
||||
|
||||
const leftNav = media.size > 1 && <button tabIndex='0' className='modal-container__nav modal-container__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><i className='fa fa-fw fa-chevron-left' /></button>;
|
||||
const rightNav = media.size > 1 && <button tabIndex='0' className='modal-container__nav modal-container__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><i className='fa fa-fw fa-chevron-right' /></button>;
|
||||
|
||||
if (media.size > 1) {
|
||||
pagination = media.map((item, i) => {
|
||||
const classes = ['media-modal__button'];
|
||||
if (i === index) {
|
||||
classes.push('media-modal__button--active');
|
||||
}
|
||||
return (<li className='media-modal__page-dot' key={i}><button tabIndex='0' className={classes.join(' ')} onClick={this.handleChangeIndex} data-index={i}>{i + 1}</button></li>);
|
||||
});
|
||||
}
|
||||
|
||||
const content = media.map((image) => {
|
||||
const width = image.getIn(['meta', 'original', 'width']) || null;
|
||||
const height = image.getIn(['meta', 'original', 'height']) || null;
|
||||
|
||||
if (image.get('type') === 'image') {
|
||||
return <ImageLoader previewSrc={image.get('preview_url')} src={image.get('url')} width={width} height={height} key={image.get('preview_url')} />;
|
||||
return <ImageLoader previewSrc={image.get('preview_url')} src={image.get('url')} width={width} height={height} alt={image.get('description')} key={image.get('preview_url')} />;
|
||||
} else if (image.get('type') === 'gifv') {
|
||||
return <ExtendedVideoPlayer src={image.get('url')} muted controls={false} width={width} height={height} key={image.get('preview_url')} />;
|
||||
return <ExtendedVideoPlayer src={image.get('url')} muted controls={false} width={width} height={height} key={image.get('preview_url')} alt={image.get('description')} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}).toArray();
|
||||
|
||||
const containerStyle = {
|
||||
alignItems: 'center', // center vertically
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal media-modal'>
|
||||
{leftNav}
|
||||
|
||||
<div className='media-modal__content'>
|
||||
<IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} />
|
||||
<ReactSwipeableViews onChangeIndex={this.handleSwipe} index={index} animateHeight>
|
||||
<ReactSwipeableViews containerStyle={containerStyle} onChangeIndex={this.handleSwipe} index={index}>
|
||||
{content}
|
||||
</ReactSwipeableViews>
|
||||
</div>
|
||||
<ul className='media-modal__pagination'>
|
||||
{pagination}
|
||||
</ul>
|
||||
|
||||
{rightNav}
|
||||
</div>
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TransitionMotion from 'react-motion/lib/TransitionMotion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import BundleContainer from '../containers/bundle_container';
|
||||
import BundleModalError from './bundle_modal_error';
|
||||
import ModalLoading from './modal_loading';
|
||||
@@ -39,6 +37,10 @@ export default class ModalRoot extends React.PureComponent {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
revealed: false,
|
||||
};
|
||||
|
||||
handleKeyUp = (e) => {
|
||||
if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
|
||||
&& !!this.props.type) {
|
||||
@@ -55,6 +57,8 @@ export default class ModalRoot extends React.PureComponent {
|
||||
this.activeElement = document.activeElement;
|
||||
|
||||
this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true));
|
||||
} else if (!nextProps.type) {
|
||||
this.setState({ revealed: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +68,11 @@ export default class ModalRoot extends React.PureComponent {
|
||||
this.activeElement.focus();
|
||||
this.activeElement = null;
|
||||
}
|
||||
if (this.props.type) {
|
||||
requestAnimationFrame(() => {
|
||||
this.setState({ revealed: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
@@ -78,14 +87,6 @@ export default class ModalRoot extends React.PureComponent {
|
||||
this.node = ref;
|
||||
}
|
||||
|
||||
willEnter () {
|
||||
return { opacity: 0, scale: 0.98 };
|
||||
}
|
||||
|
||||
willLeave () {
|
||||
return { opacity: spring(0), scale: spring(0.98) };
|
||||
}
|
||||
|
||||
renderLoading = modalId => () => {
|
||||
return ['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
|
||||
}
|
||||
@@ -98,38 +99,30 @@ export default class ModalRoot extends React.PureComponent {
|
||||
|
||||
render () {
|
||||
const { type, props, onClose } = this.props;
|
||||
const { revealed } = this.state;
|
||||
const visible = !!type;
|
||||
const items = [];
|
||||
|
||||
if (visible) {
|
||||
items.push({
|
||||
key: type,
|
||||
data: { type, props },
|
||||
style: { opacity: spring(1), scale: spring(1, { stiffness: 120, damping: 14 }) },
|
||||
});
|
||||
if (!visible) {
|
||||
return (
|
||||
<div className='modal-root' ref={this.setRef} style={{ opacity: 0 }} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TransitionMotion
|
||||
styles={items}
|
||||
willEnter={this.willEnter}
|
||||
willLeave={this.willLeave}
|
||||
>
|
||||
{interpolatedStyles =>
|
||||
<div className='modal-root' ref={this.setRef}>
|
||||
{interpolatedStyles.map(({ key, data: { type, props }, style }) => (
|
||||
<div key={key} style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||
<div role='presentation' className='modal-root__overlay' style={{ opacity: style.opacity }} onClick={onClose} />
|
||||
<div role='dialog' className='modal-root__container' style={{ opacity: style.opacity, transform: `translateZ(0px) scale(${style.scale})` }}>
|
||||
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
|
||||
{(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
|
||||
</BundleContainer>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className='modal-root' ref={this.setRef} style={{ opacity: revealed ? 1 : 0 }}>
|
||||
<div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||
<div role='presentation' className='modal-root__overlay' onClick={onClose} />
|
||||
<div role='dialog' className='modal-root__container'>
|
||||
{
|
||||
visible ?
|
||||
(<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
|
||||
{(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
|
||||
</BundleContainer>) :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</TransitionMotion>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import NavLink from 'react-router-dom/NavLink';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { debounce } from 'lodash';
|
||||
import { isUserTouching } from '../../../is_mobile';
|
||||
|
@@ -23,6 +23,7 @@ export default class VideoModal extends ImmutablePureComponent {
|
||||
src={media.get('url')}
|
||||
startTime={time}
|
||||
onCloseVideo={onClose}
|
||||
description={media.get('description')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -8,7 +8,7 @@ import { connect } from 'react-redux';
|
||||
import { Redirect, withRouter } from 'react-router-dom';
|
||||
import { isMobile } from '../../is_mobile';
|
||||
import { debounce } from 'lodash';
|
||||
import { uploadCompose } from '../../actions/compose';
|
||||
import { uploadCompose, resetCompose } from '../../actions/compose';
|
||||
import { refreshHomeTimeline } from '../../actions/timelines';
|
||||
import { refreshNotifications } from '../../actions/notifications';
|
||||
import { clearHeight } from '../../actions/height_cache';
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
Mutes,
|
||||
PinnedStatuses,
|
||||
} from './util/async-components';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
|
||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||
// Without this it ends up in ~8 very commonly used bundles.
|
||||
@@ -48,12 +49,39 @@ const mapStateToProps = state => ({
|
||||
layout: state.getIn(['local_settings', 'layout']),
|
||||
isWide: state.getIn(['local_settings', 'stretch']),
|
||||
navbarUnder: state.getIn(['local_settings', 'navbar_under']),
|
||||
me: state.getIn(['meta', 'me']),
|
||||
isComposing: state.getIn(['compose', 'is_composing']),
|
||||
});
|
||||
|
||||
const keyMap = {
|
||||
new: 'n',
|
||||
search: 's',
|
||||
forceNew: 'option+n',
|
||||
focusColumn: ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
||||
reply: 'r',
|
||||
favourite: 'f',
|
||||
boost: 'b',
|
||||
mention: 'm',
|
||||
open: ['enter', 'o'],
|
||||
openProfile: 'p',
|
||||
moveDown: ['down', 'j'],
|
||||
moveUp: ['up', 'k'],
|
||||
back: 'backspace',
|
||||
goToHome: 'g h',
|
||||
goToNotifications: 'g n',
|
||||
goToLocal: 'g l',
|
||||
goToFederated: 'g t',
|
||||
goToStart: 'g s',
|
||||
goToFavourites: 'g f',
|
||||
goToPinned: 'g p',
|
||||
goToProfile: 'g u',
|
||||
goToBlocked: 'g b',
|
||||
goToMuted: 'g m',
|
||||
};
|
||||
|
||||
@connect(mapStateToProps)
|
||||
@withRouter
|
||||
export default class UI extends React.PureComponent {
|
||||
export default class UI extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
@@ -67,6 +95,7 @@ export default class UI extends React.PureComponent {
|
||||
systemFontUi: PropTypes.bool,
|
||||
navbarUnder: PropTypes.bool,
|
||||
isComposing: PropTypes.bool,
|
||||
me: PropTypes.string,
|
||||
location: PropTypes.object,
|
||||
};
|
||||
|
||||
@@ -164,6 +193,12 @@ export default class UI extends React.PureComponent {
|
||||
this.props.dispatch(refreshNotifications());
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
||||
return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate (nextProps) {
|
||||
if (nextProps.isComposing !== this.props.isComposing) {
|
||||
// Avoid expensive update just to toggle a class
|
||||
@@ -201,8 +236,94 @@ export default class UI extends React.PureComponent {
|
||||
this.columnsAreaNode = c.getWrappedInstance().getWrappedInstance();
|
||||
}
|
||||
|
||||
setOverlayRef = c => {
|
||||
this.overlay = c;
|
||||
handleHotkeyNew = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const element = this.node.querySelector('.compose-form__autosuggest-wrapper textarea');
|
||||
|
||||
if (element) {
|
||||
element.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleHotkeySearch = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const element = this.node.querySelector('.search__input');
|
||||
|
||||
if (element) {
|
||||
element.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleHotkeyForceNew = e => {
|
||||
this.handleHotkeyNew(e);
|
||||
this.props.dispatch(resetCompose());
|
||||
}
|
||||
|
||||
handleHotkeyFocusColumn = e => {
|
||||
const index = (e.key * 1) + 1; // First child is drawer, skip that
|
||||
const column = this.node.querySelector(`.column:nth-child(${index})`);
|
||||
|
||||
if (column) {
|
||||
const status = column.querySelector('.focusable');
|
||||
|
||||
if (status) {
|
||||
status.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleHotkeyBack = () => {
|
||||
if (window.history && window.history.length === 1) {
|
||||
this.context.router.history.push('/');
|
||||
} else {
|
||||
this.context.router.history.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
setHotkeysRef = c => {
|
||||
this.hotkeys = c;
|
||||
}
|
||||
|
||||
handleHotkeyGoToHome = () => {
|
||||
this.context.router.history.push('/timelines/home');
|
||||
}
|
||||
|
||||
handleHotkeyGoToNotifications = () => {
|
||||
this.context.router.history.push('/notifications');
|
||||
}
|
||||
|
||||
handleHotkeyGoToLocal = () => {
|
||||
this.context.router.history.push('/timelines/public/local');
|
||||
}
|
||||
|
||||
handleHotkeyGoToFederated = () => {
|
||||
this.context.router.history.push('/timelines/public');
|
||||
}
|
||||
|
||||
handleHotkeyGoToStart = () => {
|
||||
this.context.router.history.push('/getting-started');
|
||||
}
|
||||
|
||||
handleHotkeyGoToFavourites = () => {
|
||||
this.context.router.history.push('/favourites');
|
||||
}
|
||||
|
||||
handleHotkeyGoToPinned = () => {
|
||||
this.context.router.history.push('/pinned');
|
||||
}
|
||||
|
||||
handleHotkeyGoToProfile = () => {
|
||||
this.context.router.history.push(`/accounts/${this.props.me}`);
|
||||
}
|
||||
|
||||
handleHotkeyGoToBlocked = () => {
|
||||
this.context.router.history.push('/blocks');
|
||||
}
|
||||
|
||||
handleHotkeyGoToMuted = () => {
|
||||
this.context.router.history.push('/mutes');
|
||||
}
|
||||
|
||||
render () {
|
||||
@@ -226,45 +347,67 @@ export default class UI extends React.PureComponent {
|
||||
'navbar-under': navbarUnder,
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
new: this.handleHotkeyNew,
|
||||
search: this.handleHotkeySearch,
|
||||
forceNew: this.handleHotkeyForceNew,
|
||||
focusColumn: this.handleHotkeyFocusColumn,
|
||||
back: this.handleHotkeyBack,
|
||||
goToHome: this.handleHotkeyGoToHome,
|
||||
goToNotifications: this.handleHotkeyGoToNotifications,
|
||||
goToLocal: this.handleHotkeyGoToLocal,
|
||||
goToFederated: this.handleHotkeyGoToFederated,
|
||||
goToStart: this.handleHotkeyGoToStart,
|
||||
goToFavourites: this.handleHotkeyGoToFavourites,
|
||||
goToPinned: this.handleHotkeyGoToPinned,
|
||||
goToProfile: this.handleHotkeyGoToProfile,
|
||||
goToBlocked: this.handleHotkeyGoToBlocked,
|
||||
goToMuted: this.handleHotkeyGoToMuted,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className} ref={this.setRef}>
|
||||
{navbarUnder ? null : (<TabsBar />)}
|
||||
<ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={isMobile(width, layout)}>
|
||||
<WrappedSwitch>
|
||||
<Redirect from='/' to='/getting-started' exact />
|
||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
|
||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef}>
|
||||
<div className={className} ref={this.setRef}>
|
||||
{navbarUnder ? null : (<TabsBar />)}
|
||||
|
||||
<WrappedRoute path='/notifications' component={Notifications} content={children} />
|
||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
|
||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
|
||||
<ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={isMobile(width, layout)}>
|
||||
<WrappedSwitch>
|
||||
<Redirect from='/' to='/getting-started' exact />
|
||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
|
||||
|
||||
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
|
||||
<WrappedRoute path='/notifications' component={Notifications} content={children} />
|
||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
|
||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
|
||||
|
||||
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
||||
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
|
||||
|
||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
||||
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
||||
|
||||
<WrappedRoute component={GenericNotFound} content={children} />
|
||||
</WrappedSwitch>
|
||||
</ColumnsAreaContainer>
|
||||
<NotificationsContainer />
|
||||
{navbarUnder ? (<TabsBar />) : null}
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
<ModalContainer />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
</div>
|
||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
||||
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
||||
|
||||
<WrappedRoute component={GenericNotFound} content={children} />
|
||||
</WrappedSwitch>
|
||||
</ColumnsAreaContainer>
|
||||
|
||||
<NotificationsContainer />
|
||||
{navbarUnder ? (<TabsBar />) : null}
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
<ModalContainer />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,7 @@
|
||||
export function EmojiPicker () {
|
||||
return import(/* webpackChunkName: "emoji_picker" */'../../emoji/emoji_picker');
|
||||
}
|
||||
|
||||
export function Compose () {
|
||||
return import(/* webpackChunkName: "features/compose" */'../../compose');
|
||||
}
|
||||
@@ -101,10 +105,6 @@ export function MediaGallery () {
|
||||
return import(/* webpackChunkName: "status/media_gallery" */'../../../components/media_gallery');
|
||||
}
|
||||
|
||||
export function VideoPlayer () {
|
||||
return import(/* webpackChunkName: "status/video_player" */'../../../components/video_player');
|
||||
}
|
||||
|
||||
export function Video () {
|
||||
return import(/* webpackChunkName: "features/video" */'../../video');
|
||||
}
|
||||
|
46
app/javascript/mastodon/features/ui/util/fullscreen.js
Normal file
46
app/javascript/mastodon/features/ui/util/fullscreen.js
Normal file
@@ -0,0 +1,46 @@
|
||||
// APIs for normalizing fullscreen operations. Note that Edge uses
|
||||
// the WebKit-prefixed APIs currently (as of Edge 16).
|
||||
|
||||
export const isFullscreen = () => document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement;
|
||||
|
||||
export const exitFullscreen = () => {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
}
|
||||
};
|
||||
|
||||
export const requestFullscreen = el => {
|
||||
if (el.requestFullscreen) {
|
||||
el.requestFullscreen();
|
||||
} else if (el.webkitRequestFullscreen) {
|
||||
el.webkitRequestFullscreen();
|
||||
} else if (el.mozRequestFullScreen) {
|
||||
el.mozRequestFullScreen();
|
||||
}
|
||||
};
|
||||
|
||||
export const attachFullscreenListener = (listener) => {
|
||||
if ('onfullscreenchange' in document) {
|
||||
document.addEventListener('fullscreenchange', listener);
|
||||
} else if ('onwebkitfullscreenchange' in document) {
|
||||
document.addEventListener('webkitfullscreenchange', listener);
|
||||
} else if ('onmozfullscreenchange' in document) {
|
||||
document.addEventListener('mozfullscreenchange', listener);
|
||||
}
|
||||
};
|
||||
|
||||
export const detachFullscreenListener = (listener) => {
|
||||
if ('onfullscreenchange' in document) {
|
||||
document.removeEventListener('fullscreenchange', listener);
|
||||
} else if ('onwebkitfullscreenchange' in document) {
|
||||
document.removeEventListener('webkitfullscreenchange', listener);
|
||||
} else if ('onmozfullscreenchange' in document) {
|
||||
document.removeEventListener('mozfullscreenchange', listener);
|
||||
}
|
||||
};
|
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Switch from 'react-router-dom/Switch';
|
||||
import Route from 'react-router-dom/Route';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
|
||||
import ColumnLoading from '../components/column_loading';
|
||||
import BundleColumnError from '../components/bundle_column_error';
|
||||
|
Reference in New Issue
Block a user