After merge resolutions
This commit is contained in:
parent
0a3c6cea89
commit
8c9cac2beb
@ -1,8 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import StatusListContainer from '../../containers/status_list_container';
|
import PropTypes from 'prop-types';
|
||||||
|
import StatusListContainer from '../ui/containers/status_list_container';
|
||||||
import Column from '../../components/column';
|
import Column from '../../components/column';
|
||||||
import ColumnSettings from './components/column_settings';
|
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||||
import { HomeColumnHeader } from '../../components/column_header';
|
import HomeColumnHeader from '../../components/home_column_header';
|
||||||
import {
|
import {
|
||||||
expandCommunityTimeline,
|
expandCommunityTimeline,
|
||||||
expandPublicTimeline,
|
expandPublicTimeline,
|
||||||
@ -32,17 +35,12 @@ const mapStateToProps = state => {
|
|||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
@injectIntl
|
@injectIntl
|
||||||
class CommunityTimeline extends PureComponent {
|
class CommunityTimeline extends React.PureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
onlyMedia: false,
|
|
||||||
allFediverse: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
@ -104,9 +102,9 @@ class CommunityTimeline extends PureComponent {
|
|||||||
const { intl, hasUnread, onlyMedia, timelineId, allFediverse } = this.props;
|
const { intl, hasUnread, onlyMedia, timelineId, allFediverse } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column heading={intl.formatMessage(messages.title)}>
|
<Column label={intl.formatMessage(messages.title)}>
|
||||||
<HomeColumnHeader activeItem='all' active={hasUnread} >
|
<HomeColumnHeader activeItem='all' active={hasUnread} >
|
||||||
<ColumnSettings />
|
<ColumnSettingsContainer />
|
||||||
</HomeColumnHeader>
|
</HomeColumnHeader>
|
||||||
<StatusListContainer
|
<StatusListContainer
|
||||||
scrollKey={`${timelineId}_timeline`}
|
scrollKey={`${timelineId}_timeline`}
|
||||||
|
@ -1,36 +1,44 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import { Switch, Redirect, withRouter } from 'react-router-dom';
|
import { Switch, Redirect, withRouter } from 'react-router-dom';
|
||||||
import NotificationsContainer from '../../containers/notifications_container';
|
import PropTypes from 'prop-types';
|
||||||
import LoadingBarContainer from '../../containers/loading_bar_container';
|
import NotificationsContainer from './containers/notifications_container';
|
||||||
import ModalContainer from '../../containers/modal_container';
|
import LoadingBarContainer from './containers/loading_bar_container';
|
||||||
import { isMobile } from '../../utils/is_mobile';
|
import ModalContainer from './containers/modal_container';
|
||||||
|
import { isMobile } from '../../is_mobile';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { uploadCompose, resetCompose } from '../../actions/compose';
|
import { uploadCompose, resetCompose } from '../../actions/compose';
|
||||||
import { expandHomeTimeline } from '../../actions/timelines';
|
import { expandHomeTimeline } from '../../actions/timelines';
|
||||||
import { expandNotifications } from '../../actions/notifications';
|
import {
|
||||||
|
initializeNotifications,
|
||||||
|
expandNotifications,
|
||||||
|
} from '../../actions/notifications';
|
||||||
import { fetchFilters } from '../../actions/filters';
|
import { fetchFilters } from '../../actions/filters';
|
||||||
import { clearHeight } from '../../actions/height_cache';
|
import { clearHeight } from '../../actions/height_cache';
|
||||||
import { openModal } from '../../actions/modal';
|
import { openModal } from '../../actions/modal';
|
||||||
import WrappedRoute from './util/wrapped_route';
|
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
|
||||||
import UploadArea from '../../components/upload_area';
|
import UploadArea from './components/upload_area';
|
||||||
import TabsBar from '../../components/tabs_bar';
|
import TabsBar from './components/tabs_bar';
|
||||||
import { WhoToFollowPanel } from '../../components/panel';
|
import FooterBar from './components/footer_bar';
|
||||||
import LinkFooter from '../../components/link_footer';
|
// import TrendsPanel from './components/trends_panel';
|
||||||
|
import WhoToFollowPanel from './components/who_to_follow_panel';
|
||||||
|
import LinkFooter from './components/link_footer';
|
||||||
import ProfilePage from 'gabsocial/pages/profile_page';
|
import ProfilePage from 'gabsocial/pages/profile_page';
|
||||||
|
import GroupsPage from 'gabsocial/pages/groups_page';
|
||||||
import GroupPage from 'gabsocial/pages/group_page';
|
import GroupPage from 'gabsocial/pages/group_page';
|
||||||
import SearchPage from 'gabsocial/pages/search_page';
|
import SearchPage from 'gabsocial/pages/search_page';
|
||||||
import HomePage from 'gabsocial/pages/home_page';
|
import HomePage from 'gabsocial/pages/home_page';
|
||||||
import GroupSidebarPanel from '../groups/sidebar_panel';
|
import GroupSidebarPanel from '../groups/sidebar_panel';
|
||||||
import FloatingActionButton from '../../components/floating_action_button';
|
import SidebarMenu from '../../components/sidebar_menu';
|
||||||
import PromoPanel from '../../components/promo_panel/promo_panel';
|
|
||||||
import UserPanel from '../../components/user_panel/user_panel';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Status,
|
Status,
|
||||||
|
GettingStarted,
|
||||||
CommunityTimeline,
|
CommunityTimeline,
|
||||||
AccountTimeline,
|
AccountTimeline,
|
||||||
AccountGallery,
|
AccountGallery,
|
||||||
@ -38,6 +46,8 @@ import {
|
|||||||
Followers,
|
Followers,
|
||||||
Following,
|
Following,
|
||||||
Reblogs,
|
Reblogs,
|
||||||
|
Favourites,
|
||||||
|
DirectTimeline,
|
||||||
HashtagTimeline,
|
HashtagTimeline,
|
||||||
Notifications,
|
Notifications,
|
||||||
FollowRequests,
|
FollowRequests,
|
||||||
@ -48,6 +58,7 @@ import {
|
|||||||
Mutes,
|
Mutes,
|
||||||
PinnedStatuses,
|
PinnedStatuses,
|
||||||
Search,
|
Search,
|
||||||
|
Explore,
|
||||||
Groups,
|
Groups,
|
||||||
GroupTimeline,
|
GroupTimeline,
|
||||||
ListTimeline,
|
ListTimeline,
|
||||||
@ -58,15 +69,13 @@ import {
|
|||||||
GroupEdit,
|
GroupEdit,
|
||||||
} from './util/async-components';
|
} from './util/async-components';
|
||||||
import { me, meUsername } from '../../initial_state';
|
import { me, meUsername } from '../../initial_state';
|
||||||
|
import { previewState as previewMediaState } from './components/media_modal';
|
||||||
|
import { previewState as previewVideoState } from './components/video_modal';
|
||||||
|
|
||||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||||
// Without this it ends up in ~8 very commonly used bundles.
|
// Without this it ends up in ~8 very commonly used bundles.
|
||||||
import '../../components/status';
|
import '../../components/status';
|
||||||
import { fetchGroups } from '../../actions/groups';
|
import { fetchGroups } from '../../actions/groups';
|
||||||
import { Fragment } from 'react';
|
|
||||||
|
|
||||||
import '../../../styles/application.scss';
|
|
||||||
import './ui.scss';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Gab Social.' },
|
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Gab Social.' },
|
||||||
@ -114,46 +123,30 @@ const LAYOUT = {
|
|||||||
RIGHT: null,
|
RIGHT: null,
|
||||||
},
|
},
|
||||||
DEFAULT: {
|
DEFAULT: {
|
||||||
LEFT: (
|
LEFT: [
|
||||||
<Fragment>
|
<WhoToFollowPanel key='0' />,
|
||||||
<WhoToFollowPanel />
|
<LinkFooter key='1' />,
|
||||||
<LinkFooter />
|
],
|
||||||
</Fragment>
|
RIGHT: [
|
||||||
),
|
// <TrendsPanel />,
|
||||||
RIGHT: <GroupSidebarPanel />
|
<GroupSidebarPanel key='0' />
|
||||||
|
],
|
||||||
},
|
},
|
||||||
STATUS: {
|
STATUS: {
|
||||||
TOP: null,
|
TOP: null,
|
||||||
LEFT: null,
|
LEFT: null,
|
||||||
RIGHT: (
|
RIGHT: [
|
||||||
<Fragment>
|
<GroupSidebarPanel key='0' />,
|
||||||
<GroupSidebarPanel />
|
<WhoToFollowPanel key='1' />,
|
||||||
<WhoToFollowPanel />
|
// <TrendsPanel />,
|
||||||
<LinkFooter />
|
<LinkFooter key='2' />,
|
||||||
</Fragment>
|
],
|
||||||
),
|
|
||||||
},
|
},
|
||||||
GROUPS: {
|
|
||||||
TOP: null,
|
|
||||||
LEFT: (
|
|
||||||
<Fragment>
|
|
||||||
<UserPanel />
|
|
||||||
<PromoPanel />
|
|
||||||
<LinkFooter />
|
|
||||||
</Fragment>
|
|
||||||
),
|
|
||||||
RIGHT: (
|
|
||||||
<Fragment>
|
|
||||||
<GroupSidebarPanel />
|
|
||||||
<WhoToFollowPanel />
|
|
||||||
</Fragment>
|
|
||||||
),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const shouldHideFAB = path => path.match(/^\/posts\/|^\/search/);
|
const shouldHideFAB = path => path.match(/^\/posts\/|^\/search|^\/getting-started/);
|
||||||
|
|
||||||
class SwitchingColumnsArea extends PureComponent {
|
class SwitchingColumnsArea extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
@ -187,7 +180,8 @@ class SwitchingColumnsArea extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children } = this.props;
|
const { children, account } = this.props;
|
||||||
|
const { mobile } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
@ -195,23 +189,26 @@ class SwitchingColumnsArea extends PureComponent {
|
|||||||
<WrappedRoute path='/home' exact page={HomePage} component={HomeTimeline} content={children} />
|
<WrappedRoute path='/home' exact page={HomePage} component={HomeTimeline} content={children} />
|
||||||
<WrappedRoute path='/timeline/all' exact page={HomePage} component={CommunityTimeline} content={children} />
|
<WrappedRoute path='/timeline/all' exact page={HomePage} component={CommunityTimeline} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/groups' exact layout={LAYOUT.GROUPS} component={Groups} content={children} componentParams={{ activeTab: 'featured' }} />
|
<WrappedRoute path='/groups' exact page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'featured' }} />
|
||||||
<WrappedRoute path='/groups/create' layout={LAYOUT.GROUPS} component={Groups} content={children} componentParams={{ showCreateForm: true, activeTab: 'featured' }} />
|
<WrappedRoute path='/groups/create' page={GroupsPage} component={Groups} content={children} componentParams={{ showCreateForm: true, activeTab: 'featured' }} />
|
||||||
<WrappedRoute path='/groups/browse/member' layout={LAYOUT.GROUPS} component={Groups} content={children} componentParams={{ activeTab: 'member' }} />
|
<WrappedRoute path='/groups/browse/member' page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'member' }} />
|
||||||
<WrappedRoute path='/groups/browse/admin' layout={LAYOUT.GROUPS} component={Groups} content={children} componentParams={{ activeTab: 'admin' }} />
|
<WrappedRoute path='/groups/browse/admin' page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'admin' }} />
|
||||||
<WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} />
|
<WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} />
|
||||||
<WrappedRoute path='/groups/:id/removed_accounts' page={GroupPage} component={GroupRemovedAccounts} content={children} />
|
<WrappedRoute path='/groups/:id/removed_accounts' page={GroupPage} component={GroupRemovedAccounts} content={children} />
|
||||||
<WrappedRoute path='/groups/:id/edit' page={GroupPage} component={GroupEdit} content={children} />
|
<WrappedRoute path='/groups/:id/edit' page={GroupPage} component={GroupEdit} content={children} />
|
||||||
<WrappedRoute path='/groups/:id' page={GroupPage} component={GroupTimeline} content={children} />
|
<WrappedRoute path='/groups/:id' page={GroupPage} component={GroupTimeline} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/tags/:id' component={HashtagTimeline} content={children} />
|
<WrappedRoute path='/tags/:id' publicRoute component={HashtagTimeline} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/lists' layout={LAYOUT.DEFAULT} component={Lists} content={children} />
|
<WrappedRoute path='/lists' layout={LAYOUT.DEFAULT} component={Lists} content={children} />
|
||||||
<WrappedRoute path='/list/:id' page={HomePage} component={ListTimeline} content={children} />
|
<WrappedRoute path='/list/:id' page={HomePage} component={ListTimeline} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/notifications' layout={LAYOUT.DEFAULT} component={Notifications} content={children} />
|
<WrappedRoute path='/notifications' layout={LAYOUT.DEFAULT} component={Notifications} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/search' publicRoute page={SearchPage} component={Search} content={children} />
|
<WrappedRoute path='/search' exact publicRoute page={SearchPage} component={Search} content={children} />
|
||||||
|
<WrappedRoute path='/search/people' exact page={SearchPage} component={Search} content={children} />
|
||||||
|
<WrappedRoute path='/search/hashtags' exact page={SearchPage} component={Search} content={children} />
|
||||||
|
<WrappedRoute path='/search/groups' exact page={SearchPage} component={Search} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/follow_requests' layout={LAYOUT.DEFAULT} component={FollowRequests} content={children} />
|
<WrappedRoute path='/follow_requests' layout={LAYOUT.DEFAULT} component={FollowRequests} content={children} />
|
||||||
<WrappedRoute path='/blocks' layout={LAYOUT.DEFAULT} component={Blocks} content={children} />
|
<WrappedRoute path='/blocks' layout={LAYOUT.DEFAULT} component={Blocks} content={children} />
|
||||||
@ -257,7 +254,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
@injectIntl
|
@injectIntl
|
||||||
@withRouter
|
@withRouter
|
||||||
class UI extends PureComponent {
|
class UI extends React.PureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object.isRequired,
|
router: PropTypes.object.isRequired,
|
||||||
@ -344,7 +341,9 @@ class UI extends PureComponent {
|
|||||||
|
|
||||||
this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
|
this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
|
||||||
|
|
||||||
if (this.dragTargets.length > 0) return;
|
if (this.dragTargets.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ draggingOver: false });
|
this.setState({ draggingOver: false });
|
||||||
}
|
}
|
||||||
@ -385,6 +384,7 @@ class UI extends PureComponent {
|
|||||||
if (me) {
|
if (me) {
|
||||||
this.props.dispatch(expandHomeTimeline());
|
this.props.dispatch(expandHomeTimeline());
|
||||||
this.props.dispatch(expandNotifications());
|
this.props.dispatch(expandNotifications());
|
||||||
|
this.props.dispatch(initializeNotifications());
|
||||||
this.props.dispatch(fetchGroups('member'));
|
this.props.dispatch(fetchGroups('member'));
|
||||||
|
|
||||||
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
|
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
|
||||||
@ -393,7 +393,6 @@ class UI extends PureComponent {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (!me) return;
|
if (!me) return;
|
||||||
|
|
||||||
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
||||||
return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
|
return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
|
||||||
};
|
};
|
||||||
@ -479,6 +478,10 @@ class UI extends PureComponent {
|
|||||||
this.context.router.history.push('/notifications');
|
this.context.router.history.push('/notifications');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHotkeyGoToStart = () => {
|
||||||
|
this.context.router.history.push('/getting-started');
|
||||||
|
}
|
||||||
|
|
||||||
handleHotkeyGoToFavourites = () => {
|
handleHotkeyGoToFavourites = () => {
|
||||||
this.context.router.history.push(`/${meUsername}/favorites`);
|
this.context.router.history.push(`/${meUsername}/favorites`);
|
||||||
}
|
}
|
||||||
@ -520,6 +523,7 @@ class UI extends PureComponent {
|
|||||||
back: this.handleHotkeyBack,
|
back: this.handleHotkeyBack,
|
||||||
goToHome: this.handleHotkeyGoToHome,
|
goToHome: this.handleHotkeyGoToHome,
|
||||||
goToNotifications: this.handleHotkeyGoToNotifications,
|
goToNotifications: this.handleHotkeyGoToNotifications,
|
||||||
|
goToStart: this.handleHotkeyGoToStart,
|
||||||
goToFavourites: this.handleHotkeyGoToFavourites,
|
goToFavourites: this.handleHotkeyGoToFavourites,
|
||||||
goToPinned: this.handleHotkeyGoToPinned,
|
goToPinned: this.handleHotkeyGoToPinned,
|
||||||
goToProfile: this.handleHotkeyGoToProfile,
|
goToProfile: this.handleHotkeyGoToProfile,
|
||||||
@ -528,7 +532,7 @@ class UI extends PureComponent {
|
|||||||
goToRequests: this.handleHotkeyGoToRequests,
|
goToRequests: this.handleHotkeyGoToRequests,
|
||||||
} : {};
|
} : {};
|
||||||
|
|
||||||
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : (<FloatingActionButton onClick={this.handleOpenComposeModal} message={intl.formatMessage(messages.publish)} />);
|
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <button key='floating-action-button' onClick={this.handleOpenComposeModal} className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}></button>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
||||||
@ -537,6 +541,7 @@ class UI extends PureComponent {
|
|||||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange}>
|
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange}>
|
||||||
{children}
|
{children}
|
||||||
</SwitchingColumnsArea>
|
</SwitchingColumnsArea>
|
||||||
|
<FooterBar />
|
||||||
|
|
||||||
{me && floatingActionButton}
|
{me && floatingActionButton}
|
||||||
|
|
||||||
@ -544,6 +549,7 @@ class UI extends PureComponent {
|
|||||||
<LoadingBarContainer className='loading-bar' />
|
<LoadingBarContainer className='loading-bar' />
|
||||||
<ModalContainer />
|
<ModalContainer />
|
||||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||||
|
{me && <SidebarMenu />}
|
||||||
</div>
|
</div>
|
||||||
</HotKeys>
|
</HotKeys>
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { fromJS, is } from 'immutable';
|
import { fromJS, is } from 'immutable';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { decode } from 'blurhash';
|
import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
|
||||||
import { isFullscreen, requestFullscreen, exitFullscreen } from '../../utils/fullscreen';
|
|
||||||
import { getPointerPosition } from '../../utils/element_position';
|
|
||||||
import { displayMedia } from '../../initial_state';
|
import { displayMedia } from '../../initial_state';
|
||||||
import Icon from '../../components/icon';
|
import Icon from 'gabsocial/components/icon';
|
||||||
|
import { decode } from 'blurhash';
|
||||||
import './video.scss';
|
import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
play: { id: 'video.play', defaultMessage: 'Play' },
|
play: { id: 'video.play', defaultMessage: 'Play' },
|
||||||
@ -20,8 +20,6 @@ const messages = defineMessages({
|
|||||||
close: { id: 'video.close', defaultMessage: 'Close video' },
|
close: { id: 'video.close', defaultMessage: 'Close video' },
|
||||||
fullscreen: { id: 'video.fullscreen', defaultMessage: 'Full screen' },
|
fullscreen: { id: 'video.fullscreen', defaultMessage: 'Full screen' },
|
||||||
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
|
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
|
||||||
warning: { id: 'status.sensitive_warning', defaultMessage: 'Sensitive content' },
|
|
||||||
hidden: { id: 'status.media_hidden', defaultMessage: 'Media hidden' },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const formatTime = secondsNum => {
|
const formatTime = secondsNum => {
|
||||||
@ -36,8 +34,61 @@ const formatTime = secondsNum => {
|
|||||||
return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
|
return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const findElementPosition = el => {
|
||||||
|
let box;
|
||||||
|
|
||||||
|
if (el.getBoundingClientRect && el.parentNode) {
|
||||||
|
box = el.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!box) {
|
||||||
|
return {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const docEl = document.documentElement;
|
||||||
|
const body = document.body;
|
||||||
|
|
||||||
|
const clientLeft = docEl.clientLeft || body.clientLeft || 0;
|
||||||
|
const scrollLeft = window.pageXOffset || body.scrollLeft;
|
||||||
|
const left = (box.left + scrollLeft) - clientLeft;
|
||||||
|
|
||||||
|
const clientTop = docEl.clientTop || body.clientTop || 0;
|
||||||
|
const scrollTop = window.pageYOffset || body.scrollTop;
|
||||||
|
const top = (box.top + scrollTop) - clientTop;
|
||||||
|
|
||||||
|
return {
|
||||||
|
left: Math.round(left),
|
||||||
|
top: Math.round(top),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPointerPosition = (el, event) => {
|
||||||
|
const position = {};
|
||||||
|
const box = findElementPosition(el);
|
||||||
|
const boxW = el.offsetWidth;
|
||||||
|
const boxH = el.offsetHeight;
|
||||||
|
const boxY = box.top;
|
||||||
|
const boxX = box.left;
|
||||||
|
|
||||||
|
let pageY = event.pageY;
|
||||||
|
let pageX = event.pageX;
|
||||||
|
|
||||||
|
if (event.changedTouches) {
|
||||||
|
pageX = event.changedTouches[0].pageX;
|
||||||
|
pageY = event.changedTouches[0].pageY;
|
||||||
|
}
|
||||||
|
|
||||||
|
position.y = Math.max(0, Math.min(1, (pageY - boxY) / boxH));
|
||||||
|
position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
|
||||||
|
|
||||||
|
return position;
|
||||||
|
};
|
||||||
|
|
||||||
export default @injectIntl
|
export default @injectIntl
|
||||||
class Video extends PureComponent {
|
class Video extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
preview: PropTypes.string,
|
preview: PropTypes.string,
|
||||||
@ -57,6 +108,7 @@ class Video extends PureComponent {
|
|||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
blurhash: PropTypes.string,
|
blurhash: PropTypes.string,
|
||||||
link: PropTypes.node,
|
link: PropTypes.node,
|
||||||
|
aspectRatio: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -154,13 +206,11 @@ class Video extends PureComponent {
|
|||||||
|
|
||||||
if (!isNaN(x)) {
|
if (!isNaN(x)) {
|
||||||
var slideamt = x;
|
var slideamt = x;
|
||||||
|
|
||||||
if (x > 1) {
|
if (x > 1) {
|
||||||
slideamt = 1;
|
slideamt = 1;
|
||||||
} else if (x < 0) {
|
} else if (x < 0) {
|
||||||
slideamt = 0;
|
slideamt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.video.volume = slideamt;
|
this.video.volume = slideamt;
|
||||||
this.setState({ volume: slideamt });
|
this.setState({ volume: slideamt });
|
||||||
}
|
}
|
||||||
@ -294,6 +344,7 @@ class Video extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleProgress = () => {
|
handleProgress = () => {
|
||||||
|
if (!this.video.buffered) return;
|
||||||
if (this.video.buffered.length > 0) {
|
if (this.video.buffered.length > 0) {
|
||||||
this.setState({ buffer: this.video.buffered.end(0) / this.video.duration * 100 });
|
this.setState({ buffer: this.video.buffered.end(0) / this.video.duration * 100 });
|
||||||
}
|
}
|
||||||
@ -325,7 +376,7 @@ class Video extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link } = this.props;
|
const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link, aspectRatio } = this.props;
|
||||||
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
||||||
const progress = (currentTime / duration) * 100;
|
const progress = (currentTime / duration) * 100;
|
||||||
|
|
||||||
@ -337,7 +388,15 @@ class Video extends PureComponent {
|
|||||||
|
|
||||||
if (inline && containerWidth) {
|
if (inline && containerWidth) {
|
||||||
width = containerWidth;
|
width = containerWidth;
|
||||||
height = containerWidth / (16 / 9);
|
const minSize = containerWidth / (16 / 9);
|
||||||
|
|
||||||
|
if (isPanoramic(aspectRatio)) {
|
||||||
|
height = Math.max(Math.floor(containerWidth / maximumAspectRatio), minSize);
|
||||||
|
} else if (isPortrait(aspectRatio)) {
|
||||||
|
height = Math.max(Math.floor(containerWidth / minimumAspectRatio), minSize);
|
||||||
|
} else {
|
||||||
|
height = Math.floor(containerWidth / aspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
playerStyle.height = height;
|
playerStyle.height = height;
|
||||||
}
|
}
|
||||||
@ -352,6 +411,14 @@ class Video extends PureComponent {
|
|||||||
preload = 'none';
|
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' />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
role='menuitem'
|
role='menuitem'
|
||||||
@ -366,6 +433,7 @@ class Video extends PureComponent {
|
|||||||
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': revealed })} />
|
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': revealed })} />
|
||||||
|
|
||||||
{revealed && <video
|
{revealed && <video
|
||||||
|
playsInline
|
||||||
ref={this.setVideoRef}
|
ref={this.setVideoRef}
|
||||||
src={src}
|
src={src}
|
||||||
poster={preview}
|
poster={preview}
|
||||||
@ -389,7 +457,7 @@ class Video extends PureComponent {
|
|||||||
|
|
||||||
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed })}>
|
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed })}>
|
||||||
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
|
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
|
||||||
<span className='spoiler-button__overlay__label'>{intl.formatMessage(sensitive ? messages.warning : messages.hidden)}</span>
|
<span className='spoiler-button__overlay__label'>{warning}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user