From c97bb0feee6b678eb2c57dda9f67c55b4b497059 Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Thu, 18 Mar 2021 19:22:17 -0500 Subject: [PATCH] Auto-scroll the left pane less frequently --- ts/components/ContactPills.tsx | 5 +- ts/components/LeftPane.tsx | 23 ++++++--- .../conversation/CallingNotification.tsx | 16 +++--- ts/util/hooks.ts | 7 +++ ts/util/lint/exceptions.json | 51 +++++-------------- 5 files changed, 43 insertions(+), 59 deletions(-) diff --git a/ts/components/ContactPills.tsx b/ts/components/ContactPills.tsx index ed3789c78ec0..47577abd0c95 100644 --- a/ts/components/ContactPills.tsx +++ b/ts/components/ContactPills.tsx @@ -9,6 +9,7 @@ import React, { ReactNode, } from 'react'; +import { usePrevious } from '../util/hooks'; import { scrollToBottom } from '../util/scrollToBottom'; type PropsType = { @@ -19,9 +20,7 @@ export const ContactPills: FunctionComponent = ({ children }) => { const elRef = useRef(null); const childCount = Children.count(children); - const previousChildCountRef = useRef(childCount); - const previousChildCount = previousChildCountRef.current; - previousChildCountRef.current = childCount; + const previousChildCount = usePrevious(0, childCount); useEffect(() => { const hasAddedNewChild = childCount > previousChildCount; diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 46e235121d69..ebd220bfbd6b 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -1,7 +1,7 @@ // Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React, { useRef, useEffect, useMemo, CSSProperties } from 'react'; +import React, { useEffect, useMemo, CSSProperties } from 'react'; import Measure, { MeasuredComponentProps } from 'react-measure'; import { isNumber } from 'lodash'; @@ -37,6 +37,7 @@ import { import * as OS from '../OS'; import { LocalizerType } from '../types/Util'; +import { usePrevious } from '../util/hooks'; import { missingCaseError } from '../util/missingCaseError'; import { ConversationList } from './ConversationList'; @@ -140,9 +141,10 @@ export const LeftPane: React.FC = ({ startSettingGroupMetadata, toggleConversationInChooseMembers, }) => { - const previousModeSpecificPropsRef = useRef(modeSpecificProps); - const previousModeSpecificProps = previousModeSpecificPropsRef.current; - previousModeSpecificPropsRef.current = modeSpecificProps; + const previousModeSpecificProps = usePrevious( + modeSpecificProps, + modeSpecificProps + ); // The left pane can be in various modes: the inbox, the archive, the composer, etc. // Ideally, this would render subcomponents such as `` or @@ -331,6 +333,15 @@ export const LeftPane: React.FC = ({ const getRow = useMemo(() => helper.getRow.bind(helper), [helper]); + const previousSelectedConversationId = usePrevious( + selectedConversationId, + selectedConversationId + ); + const rowIndexToScrollTo: undefined | number = + previousSelectedConversationId === selectedConversationId + ? undefined + : helper.getRowIndexToScrollTo(selectedConversationId); + // We ensure that the listKey differs between some modes (e.g. inbox/archived), ensuring // that AutoSizer properly detects the new size of its slot in the flexbox. The // archive explainer text at the top of the archive view causes problems otherwise. @@ -403,9 +414,7 @@ export const LeftPane: React.FC = ({ }} renderMessageSearchResult={renderMessageSearchResult} rowCount={helper.getRowCount()} - scrollToRowIndex={helper.getRowIndexToScrollTo( - selectedConversationId - )} + scrollToRowIndex={rowIndexToScrollTo} shouldRecomputeRowHeights={shouldRecomputeRowHeights} showChooseGroupMembers={showChooseGroupMembers} startNewConversationFromPhoneNumber={ diff --git a/ts/components/conversation/CallingNotification.tsx b/ts/components/conversation/CallingNotification.tsx index e4ad7eb1667b..565503afe9f0 100644 --- a/ts/components/conversation/CallingNotification.tsx +++ b/ts/components/conversation/CallingNotification.tsx @@ -1,7 +1,7 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import Measure from 'react-measure'; import { Timestamp } from './Timestamp'; @@ -11,6 +11,7 @@ import { CallingNotificationType, getCallingNotificationText, } from '../../util/callingNotification'; +import { usePrevious } from '../../util/hooks'; import { missingCaseError } from '../../util/missingCaseError'; import { Tooltip, TooltipPlacement } from '../Tooltip'; @@ -34,23 +35,18 @@ type PropsType = CallingNotificationType & PropsActionsType & PropsHousekeeping; export const CallingNotification: React.FC = React.memo(props => { const { conversationId, i18n, messageId, messageSizeChanged } = props; - const previousHeightRef = useRef(null); const [height, setHeight] = useState(null); + const previousHeight = usePrevious(null, height); useEffect(() => { if (height === null) { return; } - if ( - previousHeightRef.current !== null && - height !== previousHeightRef.current - ) { + if (previousHeight !== null && height !== previousHeight) { messageSizeChanged(messageId, conversationId); } - - previousHeightRef.current = height; - }, [height, conversationId, messageId, messageSizeChanged]); + }, [height, previousHeight, conversationId, messageId, messageSizeChanged]); let timestamp: number; let callType: 'audio' | 'video'; diff --git a/ts/util/hooks.ts b/ts/util/hooks.ts index dad04054d393..04ff591f9510 100644 --- a/ts/util/hooks.ts +++ b/ts/util/hooks.ts @@ -5,6 +5,13 @@ import * as React from 'react'; import { ActionCreatorsMapObject, bindActionCreators } from 'redux'; import { useDispatch } from 'react-redux'; +export function usePrevious(initialValue: T, currentValue: T): T { + const previousValueRef = React.useRef(initialValue); + const result = previousValueRef.current; + previousValueRef.current = currentValue; + return result; +} + // Restore focus on teardown export const useRestoreFocus = ( // The ref for the element to receive initial focus diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index e3cc06b96c56..c6a8228df7d2 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -14375,20 +14375,11 @@ "rule": "React-useRef", "path": "ts/components/ContactPills.js", "line": " const elRef = react_1.useRef(null);", - "lineNumber": 28, + "lineNumber": 29, "reasonCategory": "usageTrusted", "updated": "2021-03-01T18:34:36.638Z", "reasonDetail": "Used for scrolling. Doesn't otherwise manipulate the DOM" }, - { - "rule": "React-useRef", - "path": "ts/components/ContactPills.js", - "line": " const previousChildCountRef = react_1.useRef(childCount);", - "lineNumber": 30, - "reasonCategory": "usageTrusted", - "updated": "2021-03-01T18:34:36.638Z", - "reasonDetail": "Doesn't reference the DOM. Refers to a number" - }, { "rule": "React-useRef", "path": "ts/components/ConversationList.js", @@ -14495,24 +14486,6 @@ "reasonCategory": "falseMatch", "updated": "2020-07-21T18:34:59.251Z" }, - { - "rule": "React-useRef", - "path": "ts/components/LeftPane.js", - "line": " const previousModeSpecificPropsRef = react_1.useRef(modeSpecificProps);", - "lineNumber": 52, - "reasonCategory": "usageTrusted", - "updated": "2021-02-12T16:25:08.285Z", - "reasonDetail": "Doesn't interact with the DOM." - }, - { - "rule": "React-useRef", - "path": "ts/components/LeftPane.tsx", - "line": " const previousModeSpecificPropsRef = useRef(modeSpecificProps);", - "lineNumber": 143, - "reasonCategory": "usageTrusted", - "updated": "2021-02-12T16:25:08.285Z", - "reasonDetail": "Doesn't interact with the DOM." - }, { "rule": "React-createRef", "path": "ts/components/Lightbox.js", @@ -14584,15 +14557,6 @@ "updated": "2020-12-04T00:11:08.128Z", "reasonDetail": "Used to add (and remove) event listeners." }, - { - "rule": "React-useRef", - "path": "ts/components/conversation/CallingNotification.js", - "line": " const previousHeightRef = react_1.useRef(null);", - "lineNumber": 37, - "reasonCategory": "usageTrusted", - "updated": "2020-12-04T00:11:08.128Z", - "reasonDetail": "Doesn't interact with the DOM." - }, { "rule": "React-useRef", "path": "ts/components/conversation/ContactModal.js", @@ -15174,13 +15138,22 @@ "reasonCategory": "falseMatch", "updated": "2020-09-08T23:07:22.682Z" }, + { + "rule": "React-useRef", + "path": "ts/util/hooks.js", + "line": " const previousValueRef = React.useRef(initialValue);", + "lineNumber": 29, + "reasonCategory": "usageTrusted", + "updated": "2021-03-18T21:41:28.361Z", + "reasonDetail": "A generic hook. Typically not to be used with non-DOM values." + }, { "rule": "React-useRef", "path": "ts/util/hooks.js", "line": " const unobserveRef = React.useRef(null);", - "lineNumber": 95, + "lineNumber": 102, "reasonCategory": "usageTrusted", "updated": "2021-01-08T15:46:32.143Z", "reasonDetail": "Doesn't manipulate the DOM. This is just a function." } -] +] \ No newline at end of file