From ea34a7f303fa62aa7a730c13458cdad85d2a11ba Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 22 Apr 2022 13:13:40 -0500 Subject: [PATCH] DELETE INTERSECTION OBSERVER ARTICLE --- app/soapbox/actions/height_cache.js | 17 --- .../intersection_observer_article.js | 131 ------------------ ...intersection_observer_article_container.js | 18 --- app/soapbox/features/ui/index.js | 12 +- .../features/ui/util/get_rect_from_entry.js | 21 --- .../ui/util/intersection_observer_wrapper.js | 57 -------- .../reducers/__tests__/height_cache-test.js | 14 -- app/soapbox/reducers/height_cache.js | 24 ---- app/soapbox/reducers/index.ts | 2 - 9 files changed, 1 insertion(+), 295 deletions(-) delete mode 100644 app/soapbox/actions/height_cache.js delete mode 100644 app/soapbox/components/intersection_observer_article.js delete mode 100644 app/soapbox/containers/intersection_observer_article_container.js delete mode 100644 app/soapbox/features/ui/util/get_rect_from_entry.js delete mode 100644 app/soapbox/features/ui/util/intersection_observer_wrapper.js delete mode 100644 app/soapbox/reducers/__tests__/height_cache-test.js diff --git a/app/soapbox/actions/height_cache.js b/app/soapbox/actions/height_cache.js deleted file mode 100644 index 74b8dd398..000000000 --- a/app/soapbox/actions/height_cache.js +++ /dev/null @@ -1,17 +0,0 @@ -export const HEIGHT_CACHE_SET = 'HEIGHT_CACHE_SET'; -export const HEIGHT_CACHE_CLEAR = 'HEIGHT_CACHE_CLEAR'; - -export function setHeight(key, id, height) { - return { - type: HEIGHT_CACHE_SET, - key, - id, - height, - }; -} - -export function clearHeight() { - return { - type: HEIGHT_CACHE_CLEAR, - }; -} diff --git a/app/soapbox/components/intersection_observer_article.js b/app/soapbox/components/intersection_observer_article.js deleted file mode 100644 index 96c1a5484..000000000 --- a/app/soapbox/components/intersection_observer_article.js +++ /dev/null @@ -1,131 +0,0 @@ -import { is } from 'immutable'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import getRectFromEntry from '../features/ui/util/get_rect_from_entry'; -import scheduleIdleTask from '../features/ui/util/schedule_idle_task'; - -// Diff these props in the "rendered" state -const updateOnPropsForRendered = ['id', 'index', 'listLength']; -// Diff these props in the "unrendered" state -const updateOnPropsForUnrendered = ['id', 'index', 'listLength', 'cachedHeight']; - -export default class IntersectionObserverArticle extends React.Component { - - static propTypes = { - intersectionObserverWrapper: PropTypes.object.isRequired, - id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - listLength: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - saveHeightKey: PropTypes.string, - cachedHeight: PropTypes.number, - onHeightChange: PropTypes.func, - children: PropTypes.node, - }; - - state = { - isHidden: false, // set to true in requestIdleCallback to trigger un-render - isIntersecting: true, - } - - shouldComponentUpdate(nextProps, nextState) { - const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight); - const willBeUnrendered = !nextState.isIntersecting && (nextState.isHidden || nextProps.cachedHeight); - if (!!isUnrendered !== !!willBeUnrendered) { - // If we're going from rendered to unrendered (or vice versa) then update - return true; - } - // Otherwise, diff based on props - const propsToDiff = isUnrendered ? updateOnPropsForUnrendered : updateOnPropsForRendered; - return !propsToDiff.every(prop => is(nextProps[prop], this.props[prop])); - } - - componentDidMount() { - const { intersectionObserverWrapper, id } = this.props; - - intersectionObserverWrapper.observe( - id, - this.node, - this.handleIntersection, - ); - - this.componentMounted = true; - } - - componentWillUnmount() { - const { intersectionObserverWrapper, id } = this.props; - intersectionObserverWrapper.unobserve(id, this.node); - - this.componentMounted = false; - } - - handleIntersection = (entry) => { - this.entry = entry; - - scheduleIdleTask(this.calculateHeight); - this.setState(this.updateStateAfterIntersection); - } - - updateStateAfterIntersection = (prevState) => { - if (prevState.isIntersecting !== false && !this.entry.isIntersecting) { - scheduleIdleTask(this.hideIfNotIntersecting); - } - return { - isIntersecting: this.entry.isIntersecting, - isHidden: false, - }; - } - - calculateHeight = () => { - const { onHeightChange, saveHeightKey, id } = this.props; - // save the height of the fully-rendered element (this is expensive - // on Chrome, where we need to fall back to getBoundingClientRect) - this.height = getRectFromEntry(this.entry).height; - - if (onHeightChange && saveHeightKey) { - onHeightChange(saveHeightKey, id, this.height); - } - } - - hideIfNotIntersecting = () => { - if (!this.componentMounted) { - return; - } - - // When the browser gets a chance, test if we're still not intersecting, - // and if so, set our isHidden to true to trigger an unrender. The point of - // this is to save DOM nodes and avoid using up too much memory. - this.setState((prevState) => ({ isHidden: !prevState.isIntersecting })); - } - - handleRef = (node) => { - this.node = node; - } - - render() { - const { children, id, index, listLength, cachedHeight } = this.props; - const { isIntersecting, isHidden } = this.state; - - if (!isIntersecting && (isHidden || cachedHeight)) { - return ( -
- {children && React.cloneElement(children, { hidden: true })} -
- ); - } - - return ( -
- {children && React.cloneElement(children, { hidden: false })} -
- ); - } - -} diff --git a/app/soapbox/containers/intersection_observer_article_container.js b/app/soapbox/containers/intersection_observer_article_container.js deleted file mode 100644 index a112069c6..000000000 --- a/app/soapbox/containers/intersection_observer_article_container.js +++ /dev/null @@ -1,18 +0,0 @@ -import { connect } from 'react-redux'; - -import { setHeight } from '../actions/height_cache'; -import IntersectionObserverArticle from '../components/intersection_observer_article'; - -const makeMapStateToProps = (state, props) => ({ - cachedHeight: state.getIn(['height_cache', props.saveHeightKey, props.id]), -}); - -const mapDispatchToProps = (dispatch) => ({ - - onHeightChange(key, id, height) { - dispatch(setHeight(key, id, height)); - }, - -}); - -export default connect(makeMapStateToProps, mapDispatchToProps)(IntersectionObserverArticle); diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index f0761f1b1..767325638 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -35,7 +35,6 @@ import { fetchFollowRequests } from '../../actions/accounts'; import { fetchReports, fetchUsers, fetchConfig } from '../../actions/admin'; import { uploadCompose, resetCompose } from '../../actions/compose'; import { fetchFilters } from '../../actions/filters'; -import { clearHeight } from '../../actions/height_cache'; import { openModal } from '../../actions/modals'; import { expandNotifications } from '../../actions/notifications'; import { fetchScheduledStatuses } from '../../actions/scheduled_statuses'; @@ -187,7 +186,6 @@ class SwitchingColumnsArea extends React.PureComponent { static propTypes = { children: PropTypes.node, location: PropTypes.object, - onLayoutChange: PropTypes.func.isRequired, soapbox: ImmutablePropTypes.record.isRequired, features: PropTypes.object.isRequired, }; @@ -205,9 +203,6 @@ class SwitchingColumnsArea extends React.PureComponent { } handleResize = debounce(() => { - // The cached heights are no longer accurate, invalidate - this.props.onLayoutChange(); - this.setState({ mobile: isMobile(window.innerWidth) }); }, 500, { trailing: true, @@ -408,11 +403,6 @@ class UI extends React.PureComponent { mobile: isMobile(window.innerWidth), }; - handleLayoutChange = () => { - // The cached heights are no longer accurate, invalidate - this.props.dispatch(clearHeight()); - } - handleDragEnter = (e) => { e.preventDefault(); @@ -756,7 +746,7 @@ class UI extends React.PureComponent {
- + {children} diff --git a/app/soapbox/features/ui/util/get_rect_from_entry.js b/app/soapbox/features/ui/util/get_rect_from_entry.js deleted file mode 100644 index c266cd7dc..000000000 --- a/app/soapbox/features/ui/util/get_rect_from_entry.js +++ /dev/null @@ -1,21 +0,0 @@ - -// 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/soapbox/features/ui/util/intersection_observer_wrapper.js b/app/soapbox/features/ui/util/intersection_observer_wrapper.js deleted file mode 100644 index 3a85894a8..000000000 --- a/app/soapbox/features/ui/util/intersection_observer_wrapper.js +++ /dev/null @@ -1,57 +0,0 @@ -// 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/soapbox/reducers/__tests__/height_cache-test.js b/app/soapbox/reducers/__tests__/height_cache-test.js deleted file mode 100644 index 4b3ed8c21..000000000 --- a/app/soapbox/reducers/__tests__/height_cache-test.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from '../height_cache'; -import { HEIGHT_CACHE_CLEAR } from '../height_cache'; - -describe('height_cache reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {})).toEqual(ImmutableMap()); - }); - - it('should handle HEIGHT_CACHE_CLEAR', () => { - expect(reducer(undefined, { type: HEIGHT_CACHE_CLEAR })).toEqual(ImmutableMap()); - }); -}); diff --git a/app/soapbox/reducers/height_cache.js b/app/soapbox/reducers/height_cache.js index 2664d4f82..e69de29bb 100644 --- a/app/soapbox/reducers/height_cache.js +++ b/app/soapbox/reducers/height_cache.js @@ -1,24 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import { HEIGHT_CACHE_SET, HEIGHT_CACHE_CLEAR } from '../actions/height_cache'; - -const initialState = ImmutableMap(); - -const setHeight = (state, key, id, height) => { - return state.update(key, ImmutableMap(), map => map.set(id, height)); -}; - -const clearHeights = () => { - return ImmutableMap(); -}; - -export default function statuses(state = initialState, action) { - switch(action.type) { - case HEIGHT_CACHE_SET: - return setHeight(state, action.key, action.id, action.height); - case HEIGHT_CACHE_CLEAR: - return clearHeights(); - default: - return state; - } -} diff --git a/app/soapbox/reducers/index.ts b/app/soapbox/reducers/index.ts index 4e54ec19f..85116e902 100644 --- a/app/soapbox/reducers/index.ts +++ b/app/soapbox/reducers/index.ts @@ -28,7 +28,6 @@ import group_editor from './group_editor'; import group_lists from './group_lists'; import group_relationships from './group_relationships'; import groups from './groups'; -import height_cache from './height_cache'; import identity_proofs from './identity_proofs'; import instance from './instance'; import listAdder from './list_adder'; @@ -83,7 +82,6 @@ const reducers = { compose, search, notifications, - height_cache, custom_emojis, identity_proofs, lists,