diff --git a/app/javascript/gabsocial/components/modal/compose_modal/compose_modal.js b/app/javascript/gabsocial/components/modal/compose_modal/compose_modal.js index 8e8fe869..ccf2fe5a 100644 --- a/app/javascript/gabsocial/components/modal/compose_modal/compose_modal.js +++ b/app/javascript/gabsocial/components/modal/compose_modal/compose_modal.js @@ -52,4 +52,4 @@ class ComposeModal extends ImmutablePureComponent { ); } -} \ No newline at end of file +} diff --git a/app/javascript/gabsocial/features/community_timeline/community_timeline.js b/app/javascript/gabsocial/features/community_timeline/community_timeline.js index 9ef1fc0e..58130882 100644 --- a/app/javascript/gabsocial/features/community_timeline/community_timeline.js +++ b/app/javascript/gabsocial/features/community_timeline/community_timeline.js @@ -1,8 +1,11 @@ +import React from 'react'; +import { connect } from 'react-redux'; 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 ColumnSettings from './components/column_settings'; -import { HomeColumnHeader } from '../../components/column_header'; +import ColumnSettingsContainer from './containers/column_settings_container'; +import HomeColumnHeader from '../../components/home_column_header'; import { expandCommunityTimeline, expandPublicTimeline, @@ -32,17 +35,12 @@ const mapStateToProps = state => { export default @connect(mapStateToProps) @injectIntl -class CommunityTimeline extends PureComponent { +class CommunityTimeline extends React.PureComponent { static contextTypes = { router: PropTypes.object, }; - static defaultProps = { - onlyMedia: false, - allFediverse: false, - }; - static propTypes = { dispatch: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, @@ -104,9 +102,9 @@ class CommunityTimeline extends PureComponent { const { intl, hasUnread, onlyMedia, timelineId, allFediverse } = this.props; return ( - + - + ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import '../../components/status'; import { fetchGroups } from '../../actions/groups'; -import { Fragment } from 'react'; - -import '../../../styles/application.scss'; -import './ui.scss'; const messages = defineMessages({ beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Gab Social.' }, @@ -114,46 +123,30 @@ const LAYOUT = { RIGHT: null, }, DEFAULT: { - LEFT: ( - - - - - ), - RIGHT: + LEFT: [ + , + , + ], + RIGHT: [ + // , + + ], }, STATUS: { TOP: null, LEFT: null, - RIGHT: ( - - - - - - ), + RIGHT: [ + , + , + // , + , + ], }, - GROUPS: { - TOP: null, - LEFT: ( - - - - - - ), - RIGHT: ( - - - - - ), - } }; -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 = { children: PropTypes.node, @@ -187,7 +180,8 @@ class SwitchingColumnsArea extends PureComponent { } render() { - const { children } = this.props; + const { children, account } = this.props; + const { mobile } = this.state; return ( @@ -195,23 +189,26 @@ class SwitchingColumnsArea extends PureComponent { - - - - + + + + - + - + + + + @@ -257,7 +254,7 @@ class SwitchingColumnsArea extends PureComponent { export default @connect(mapStateToProps) @injectIntl @withRouter -class UI extends PureComponent { +class UI extends React.PureComponent { static contextTypes = { 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)); - if (this.dragTargets.length > 0) return; + if (this.dragTargets.length > 0) { + return; + } this.setState({ draggingOver: false }); } @@ -385,6 +384,7 @@ class UI extends PureComponent { if (me) { this.props.dispatch(expandHomeTimeline()); this.props.dispatch(expandNotifications()); + this.props.dispatch(initializeNotifications()); this.props.dispatch(fetchGroups('member')); setTimeout(() => this.props.dispatch(fetchFilters()), 500); @@ -393,7 +393,6 @@ class UI extends PureComponent { componentDidMount() { if (!me) return; - this.hotkeys.__mousetrap__.stopCallback = (e, element) => { return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName); }; @@ -479,6 +478,10 @@ class UI extends PureComponent { this.context.router.history.push('/notifications'); } + handleHotkeyGoToStart = () => { + this.context.router.history.push('/getting-started'); + } + handleHotkeyGoToFavourites = () => { this.context.router.history.push(`/${meUsername}/favorites`); } @@ -520,6 +523,7 @@ class UI extends PureComponent { back: this.handleHotkeyBack, goToHome: this.handleHotkeyGoToHome, goToNotifications: this.handleHotkeyGoToNotifications, + goToStart: this.handleHotkeyGoToStart, goToFavourites: this.handleHotkeyGoToFavourites, goToPinned: this.handleHotkeyGoToPinned, goToProfile: this.handleHotkeyGoToProfile, @@ -528,7 +532,7 @@ class UI extends PureComponent { goToRequests: this.handleHotkeyGoToRequests, } : {}; - const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : (); + const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : ; return ( @@ -537,6 +541,7 @@ class UI extends PureComponent { {children} + {me && floatingActionButton} @@ -544,6 +549,7 @@ class UI extends PureComponent { + {me && } ); diff --git a/app/javascript/gabsocial/features/video/video.js b/app/javascript/gabsocial/features/video/video.js index d8424e75..e378b222 100644 --- a/app/javascript/gabsocial/features/video/video.js +++ b/app/javascript/gabsocial/features/video/video.js @@ -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 { throttle } from 'lodash'; import classNames from 'classnames'; -import { decode } from 'blurhash'; -import { isFullscreen, requestFullscreen, exitFullscreen } from '../../utils/fullscreen'; -import { getPointerPosition } from '../../utils/element_position'; +import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen'; import { displayMedia } from '../../initial_state'; -import Icon from '../../components/icon'; - -import './video.scss'; +import Icon from 'gabsocial/components/icon'; +import { decode } from 'blurhash'; +import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio'; const messages = defineMessages({ play: { id: 'video.play', defaultMessage: 'Play' }, @@ -20,8 +20,6 @@ const messages = defineMessages({ close: { id: 'video.close', defaultMessage: 'Close video' }, fullscreen: { id: 'video.fullscreen', defaultMessage: '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 => { @@ -36,8 +34,61 @@ const formatTime = secondsNum => { 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 -class Video extends PureComponent { +class Video extends React.PureComponent { static propTypes = { preview: PropTypes.string, @@ -57,6 +108,7 @@ class Video extends PureComponent { intl: PropTypes.object.isRequired, blurhash: PropTypes.string, link: PropTypes.node, + aspectRatio: PropTypes.number, }; state = { @@ -154,13 +206,11 @@ class Video extends PureComponent { if (!isNaN(x)) { var slideamt = x; - if (x > 1) { slideamt = 1; } else if (x < 0) { slideamt = 0; } - this.video.volume = slideamt; this.setState({ volume: slideamt }); } @@ -294,6 +344,7 @@ class Video extends PureComponent { } handleProgress = () => { + if (!this.video.buffered) return; if (this.video.buffered.length > 0) { this.setState({ buffer: this.video.buffered.end(0) / this.video.duration * 100 }); } @@ -325,7 +376,7 @@ class Video extends PureComponent { } 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 progress = (currentTime / duration) * 100; @@ -337,7 +388,15 @@ class Video extends PureComponent { if (inline && 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; } @@ -352,6 +411,14 @@ class Video extends PureComponent { preload = 'none'; } + let warning; + + if (sensitive) { + warning = ; + } else { + warning = ; + } + return (
{revealed &&