diff options
author | Claire <claire.github-309c@sitedethib.com> | 2022-10-11 10:51:33 +0200 |
---|---|---|
committer | Claire <claire.github-309c@sitedethib.com> | 2022-10-11 11:31:11 +0200 |
commit | 015e798394a98bb826446ceb55bfadacc5888215 (patch) | |
tree | 2666538bc8abb01b1aaa3ec886eb566b88204051 /app/javascript/flavours/glitch/features | |
parent | d3879c07b1b2140bd19433ae06855894228bd90f (diff) |
Move some modules from flavours/glitch/utils/ back to flavours/glitch/features/ui/util/
Diffstat (limited to 'app/javascript/flavours/glitch/features')
22 files changed, 286 insertions, 15 deletions
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js index 0cab9a8a8..21835e628 100644 --- a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js +++ b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js @@ -10,7 +10,7 @@ import Icon from 'flavours/glitch/components/icon'; // Utils. import { withPassive } from 'flavours/glitch/utils/dom_helpers'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../../ui/util/optional_motion'; import { assignHandlers } from 'flavours/glitch/utils/react_helpers'; // The spring to use with our motion. diff --git a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js index 563a9d687..31f1d4e73 100644 --- a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js +++ b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { injectIntl, defineMessages } from 'react-intl'; import TextIconButton from './text_icon_button'; import Overlay from 'react-overlays/lib/Overlay'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from 'flavours/glitch/features/ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import { supportsPassiveEvents } from 'detect-passive-events'; import classNames from 'classnames'; diff --git a/app/javascript/flavours/glitch/features/compose/components/options.js b/app/javascript/flavours/glitch/features/compose/components/options.js index b885ae5b0..32a464011 100644 --- a/app/javascript/flavours/glitch/features/compose/components/options.js +++ b/app/javascript/flavours/glitch/features/compose/components/options.js @@ -16,7 +16,7 @@ import LanguageDropdown from '../containers/language_dropdown_container'; import ImmutablePureComponent from 'react-immutable-pure-component'; // Utils. -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../../ui/util/optional_motion'; import { pollLimits } from 'flavours/glitch/initial_state'; // Messages. diff --git a/app/javascript/flavours/glitch/features/compose/components/search.js b/app/javascript/flavours/glitch/features/compose/components/search.js index 148e43260..a59418e46 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.js +++ b/app/javascript/flavours/glitch/features/compose/components/search.js @@ -17,7 +17,7 @@ import Icon from 'flavours/glitch/components/icon'; // Utils. import { focusRoot } from 'flavours/glitch/utils/dom_helpers'; import { searchEnabled } from 'flavours/glitch/initial_state'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../../ui/util/optional_motion'; const messages = defineMessages({ placeholder: { id: 'search.placeholder', defaultMessage: 'Search' }, diff --git a/app/javascript/flavours/glitch/features/compose/components/upload.js b/app/javascript/flavours/glitch/features/compose/components/upload.js index b344dece1..b926c455d 100644 --- a/app/javascript/flavours/glitch/features/compose/components/upload.js +++ b/app/javascript/flavours/glitch/features/compose/components/upload.js @@ -1,7 +1,7 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { FormattedMessage } from 'react-intl'; diff --git a/app/javascript/flavours/glitch/features/compose/components/upload_progress.js b/app/javascript/flavours/glitch/features/compose/components/upload_progress.js index b5126bd4e..8896bbffd 100644 --- a/app/javascript/flavours/glitch/features/compose/components/upload_progress.js +++ b/app/javascript/flavours/glitch/features/compose/components/upload_progress.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import Icon from 'flavours/glitch/components/icon'; diff --git a/app/javascript/flavours/glitch/features/compose/components/warning.js b/app/javascript/flavours/glitch/features/compose/components/warning.js index 3bcf9a7f7..4009be8c6 100644 --- a/app/javascript/flavours/glitch/features/compose/components/warning.js +++ b/app/javascript/flavours/glitch/features/compose/components/warning.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; export default class Warning extends React.PureComponent { diff --git a/app/javascript/flavours/glitch/features/compose/index.js b/app/javascript/flavours/glitch/features/compose/index.js index 7c723b048..567bb3711 100644 --- a/app/javascript/flavours/glitch/features/compose/index.js +++ b/app/javascript/flavours/glitch/features/compose/index.js @@ -8,7 +8,7 @@ import { mountCompose, unmountCompose } from 'flavours/glitch/actions/compose'; import { injectIntl, defineMessages } from 'react-intl'; import classNames from 'classnames'; import SearchContainer from './containers/search_container'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import SearchResultsContainer from './containers/search_results_container'; import { me, mascot } from 'flavours/glitch/initial_state'; diff --git a/app/javascript/flavours/glitch/features/list_editor/index.js b/app/javascript/flavours/glitch/features/list_editor/index.js index 505b07cfc..c2ca07053 100644 --- a/app/javascript/flavours/glitch/features/list_editor/index.js +++ b/app/javascript/flavours/glitch/features/list_editor/index.js @@ -8,7 +8,7 @@ import { setupListEditor, clearListSuggestions, resetListEditor } from 'flavours import AccountContainer from './containers/account_container'; import SearchContainer from './containers/search_container'; import EditListForm from './components/edit_list_form'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; const mapStateToProps = state => ({ diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js index 30fc0b6f9..43ae0ec2f 100644 --- a/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js +++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js @@ -7,7 +7,7 @@ import { injectIntl, FormattedMessage } from 'react-intl'; import { fetchPinnedAccounts, clearPinnedAccountsSuggestions, resetPinnedAccountsEditor } from 'flavours/glitch/actions/accounts'; import AccountContainer from './containers/account_container'; import SearchContainer from './containers/search_container'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from 'flavours/glitch/features/ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; const mapStateToProps = state => ({ diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index ec03e6ba0..46770930f 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -13,7 +13,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import Video from 'flavours/glitch/features/video'; import Audio from 'flavours/glitch/features/audio'; import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon'; -import scheduleIdleTask from 'flavours/glitch/utils/schedule_idle_task'; +import scheduleIdleTask from '../../ui/util/schedule_idle_task'; import classNames from 'classnames'; import PollContainer from 'flavours/glitch/containers/poll_container'; import Icon from 'flavours/glitch/components/icon'; diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 5ce2aab02..c967ef34d 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -48,7 +48,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { HotKeys } from 'react-hotkeys'; import { boostModal, favouriteModal, deleteModal, title } from 'flavours/glitch/initial_state'; -import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'flavours/glitch/utils/fullscreen'; +import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen'; import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning'; import { textForScreenReader, defaultMediaVisibility } from 'flavours/glitch/components/status'; import Icon from 'flavours/glitch/components/icon'; diff --git a/app/javascript/flavours/glitch/features/ui/components/upload_area.js b/app/javascript/flavours/glitch/features/ui/components/upload_area.js index 0f8f4535c..6958ba9df 100644 --- a/app/javascript/flavours/glitch/features/ui/components/upload_area.js +++ b/app/javascript/flavours/glitch/features/ui/components/upload_area.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Motion from 'flavours/glitch/utils/optional_motion'; +import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import { FormattedMessage } from 'react-intl'; diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index f8e868790..f77e89591 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -14,7 +14,7 @@ import { fetchServer } from 'flavours/glitch/actions/server'; import { clearHeight } from 'flavours/glitch/actions/height_cache'; import { changeLayout } from 'flavours/glitch/actions/app'; import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavours/glitch/actions/markers'; -import { WrappedSwitch, WrappedRoute } from 'flavours/glitch/utils/react_router_helpers'; +import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; import UploadArea from './components/upload_area'; import PermaLink from 'flavours/glitch/components/permalink'; import ColumnsAreaContainer from './containers/columns_area_container'; diff --git a/app/javascript/flavours/glitch/features/ui/util/fullscreen.js b/app/javascript/flavours/glitch/features/ui/util/fullscreen.js new file mode 100644 index 000000000..cf5d0cf98 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/fullscreen.js @@ -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); + } +}; diff --git a/app/javascript/flavours/glitch/features/ui/util/get_rect_from_entry.js b/app/javascript/flavours/glitch/features/ui/util/get_rect_from_entry.js new file mode 100644 index 000000000..c266cd7dc --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/get_rect_from_entry.js @@ -0,0 +1,21 @@ + +// Get the bounding client rect from an IntersectionObserver entry. +// This is to work around a bug in Chrome: https://crbug.com/737228 + +let hasBoundingRectBug; + +function getRectFromEntry(entry) { + if (typeof hasBoundingRectBug !== 'boolean') { + const boundingRect = entry.target.getBoundingClientRect(); + const observerRect = entry.boundingClientRect; + hasBoundingRectBug = boundingRect.height !== observerRect.height || + boundingRect.top !== observerRect.top || + boundingRect.width !== observerRect.width || + boundingRect.bottom !== observerRect.bottom || + boundingRect.left !== observerRect.left || + boundingRect.right !== observerRect.right; + } + return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect; +} + +export default getRectFromEntry; diff --git a/app/javascript/flavours/glitch/features/ui/util/intersection_observer_wrapper.js b/app/javascript/flavours/glitch/features/ui/util/intersection_observer_wrapper.js new file mode 100644 index 000000000..2b24c6583 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/intersection_observer_wrapper.js @@ -0,0 +1,57 @@ +// Wrapper for IntersectionObserver in order to make working with it +// a bit easier. We also follow this performance advice: +// "If you need to observe multiple elements, it is both possible and +// advised to observe multiple elements using the same IntersectionObserver +// instance by calling observe() multiple times." +// https://developers.google.com/web/updates/2016/04/intersectionobserver + +class IntersectionObserverWrapper { + + callbacks = {}; + observerBacklog = []; + observer = null; + + connect (options) { + const onIntersection = (entries) => { + entries.forEach(entry => { + const id = entry.target.getAttribute('data-id'); + if (this.callbacks[id]) { + this.callbacks[id](entry); + } + }); + }; + + this.observer = new IntersectionObserver(onIntersection, options); + this.observerBacklog.forEach(([ id, node, callback ]) => { + this.observe(id, node, callback); + }); + this.observerBacklog = null; + } + + observe (id, node, callback) { + if (!this.observer) { + this.observerBacklog.push([ id, node, callback ]); + } else { + this.callbacks[id] = callback; + this.observer.observe(node); + } + } + + unobserve (id, node) { + if (this.observer) { + delete this.callbacks[id]; + this.observer.unobserve(node); + } + } + + disconnect () { + if (this.observer) { + this.callbacks = {}; + this.observer.disconnect(); + this.observer = null; + } + } + +} + +export default IntersectionObserverWrapper; diff --git a/app/javascript/flavours/glitch/features/ui/util/optional_motion.js b/app/javascript/flavours/glitch/features/ui/util/optional_motion.js new file mode 100644 index 000000000..a7fbe6310 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/optional_motion.js @@ -0,0 +1,5 @@ +import { reduceMotion } from 'flavours/glitch/initial_state'; +import ReducedMotion from './reduced_motion'; +import Motion from 'react-motion/lib/Motion'; + +export default reduceMotion ? ReducedMotion : Motion; diff --git a/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js new file mode 100644 index 000000000..e36c512f3 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js @@ -0,0 +1,69 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Switch, Route } from 'react-router-dom'; + +import ColumnLoading from 'flavours/glitch/features/ui/components/column_loading'; +import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error'; +import BundleContainer from 'flavours/glitch/features/ui/containers/bundle_container'; + +// Small wrapper to pass multiColumn to the route components +export class WrappedSwitch extends React.PureComponent { + + render () { + const { multiColumn, children } = this.props; + + return ( + <Switch> + {React.Children.map(children, child => React.cloneElement(child, { multiColumn }))} + </Switch> + ); + } + +} + +WrappedSwitch.propTypes = { + multiColumn: PropTypes.bool, + children: PropTypes.node, +}; + +// Small Wraper to extract the params from the route and pass +// them to the rendered component, together with the content to +// be rendered inside (the children) +export class WrappedRoute extends React.Component { + + static propTypes = { + component: PropTypes.func.isRequired, + content: PropTypes.node, + multiColumn: PropTypes.bool, + componentParams: PropTypes.object, + } + + static defaultProps = { + componentParams: {}, + }; + + renderComponent = ({ match }) => { + const { component, content, multiColumn, componentParams } = this.props; + + return ( + <BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}> + {Component => <Component params={match.params} multiColumn={multiColumn} {...componentParams}>{content}</Component>} + </BundleContainer> + ); + } + + renderLoading = () => { + return <ColumnLoading />; + } + + renderError = (props) => { + return <BundleColumnError {...props} />; + } + + render () { + const { component: Component, content, ...rest } = this.props; + + return <Route {...rest} render={this.renderComponent} />; + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js b/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js new file mode 100644 index 000000000..95519042b --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js @@ -0,0 +1,44 @@ +// Like react-motion's Motion, but reduces all animations to cross-fades +// for the benefit of users with motion sickness. +import React from 'react'; +import Motion from 'react-motion/lib/Motion'; +import PropTypes from 'prop-types'; + +const stylesToKeep = ['opacity', 'backgroundOpacity']; + +const extractValue = (value) => { + // This is either an object with a "val" property or it's a number + return (typeof value === 'object' && value && 'val' in value) ? value.val : value; +}; + +class ReducedMotion extends React.Component { + + static propTypes = { + defaultStyle: PropTypes.object, + style: PropTypes.object, + children: PropTypes.func, + } + + render() { + + const { style, defaultStyle, children } = this.props; + + Object.keys(style).forEach(key => { + if (stylesToKeep.includes(key)) { + return; + } + // If it's setting an x or height or scale or some other value, we need + // to preserve the end-state value without actually animating it + style[key] = defaultStyle[key] = extractValue(style[key]); + }); + + return ( + <Motion style={style} defaultStyle={defaultStyle}> + {children} + </Motion> + ); + } + +} + +export default ReducedMotion; diff --git a/app/javascript/flavours/glitch/features/ui/util/schedule_idle_task.js b/app/javascript/flavours/glitch/features/ui/util/schedule_idle_task.js new file mode 100644 index 000000000..b04d4a8ee --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/schedule_idle_task.js @@ -0,0 +1,29 @@ +// Wrapper to call requestIdleCallback() to schedule low-priority work. +// See https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API +// for a good breakdown of the concepts behind this. + +import Queue from 'tiny-queue'; + +const taskQueue = new Queue(); +let runningRequestIdleCallback = false; + +function runTasks(deadline) { + while (taskQueue.length && deadline.timeRemaining() > 0) { + taskQueue.shift()(); + } + if (taskQueue.length) { + requestIdleCallback(runTasks); + } else { + runningRequestIdleCallback = false; + } +} + +function scheduleIdleTask(task) { + taskQueue.push(task); + if (!runningRequestIdleCallback) { + runningRequestIdleCallback = true; + requestIdleCallback(runTasks); + } +} + +export default scheduleIdleTask; diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js index 0a37cff14..cb4655f7f 100644 --- a/app/javascript/flavours/glitch/features/video/index.js +++ b/app/javascript/flavours/glitch/features/video/index.js @@ -4,7 +4,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { is } from 'immutable'; import { throttle, debounce } from 'lodash'; import classNames from 'classnames'; -import { isFullscreen, requestFullscreen, exitFullscreen } from 'flavours/glitch/utils/fullscreen'; +import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen'; import { displayMedia, useBlurhash } from 'flavours/glitch/initial_state'; import Icon from 'flavours/glitch/components/icon'; import Blurhash from 'flavours/glitch/components/blurhash'; |