From 6b42a69947e59e833f3b93771a0f0be7dac908a6 Mon Sep 17 00:00:00 2001 From: Alvaro <110414366+alvaro-signal@users.noreply.github.com> Date: Mon, 14 Nov 2022 11:48:51 -0700 Subject: [PATCH] Extract ListView controller from ConversationList --- stylesheets/_modules.scss | 6 --- stylesheets/components/ListView.scss | 10 ++++ ts/components/ConversationList.tsx | 32 ++++--------- ts/components/ListView.tsx | 70 ++++++++++++++++++++++++++++ ts/util/lint/exceptions.json | 4 +- 5 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 stylesheets/components/ListView.scss create mode 100644 ts/components/ListView.tsx diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index f9ea8381d41b..2f246b76f2f9 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -4365,12 +4365,6 @@ button.module-image__border-overlay:focus { padding-right: 10px; } - &--scroll-behavior { - &-default { - @include smooth-scroll; - } - } - &__item { &--archive-button { @include button-reset; diff --git a/stylesheets/components/ListView.scss b/stylesheets/components/ListView.scss new file mode 100644 index 000000000000..f9c6bc318fc1 --- /dev/null +++ b/stylesheets/components/ListView.scss @@ -0,0 +1,10 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +.ListView { + &--scroll-behavior { + &-default { + @include smooth-scroll; + } + } +} diff --git a/ts/components/ConversationList.tsx b/ts/components/ConversationList.tsx index e7905935e11f..aec7fe55f99e 100644 --- a/ts/components/ConversationList.tsx +++ b/ts/components/ConversationList.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import type { ReactNode } from 'react'; -import React, { useRef, useEffect, useCallback } from 'react'; +import React, { useCallback } from 'react'; import type { ListRowRenderer } from 'react-virtualized'; -import { List } from 'react-virtualized'; import classNames from 'classnames'; import { get, pick } from 'lodash'; @@ -33,6 +32,7 @@ import { SearchResultsLoadingFakeHeader as SearchResultsLoadingFakeHeaderCompone import { SearchResultsLoadingFakeRow as SearchResultsLoadingFakeRowComponent } from './conversationList/SearchResultsLoadingFakeRow'; import { UsernameSearchResultListItem } from './conversationList/UsernameSearchResultListItem'; import { GroupListItem } from './conversationList/GroupListItem'; +import { ListView } from './ListView'; export enum RowType { ArchiveButton = 'ArchiveButton', @@ -203,17 +203,8 @@ export const ConversationList: React.FC = ({ showConversation, theme, }) => { - const listRef = useRef(null); - - useEffect(() => { - const list = listRef.current; - if (shouldRecomputeRowHeights && list) { - list.recomputeRowHeights(); - } - }); - const calculateRowHeight = useCallback( - ({ index }: { index: number }): number => { + (index: number): number => { const row = getRow(index); if (!row) { assertDev(false, `Expected a row at index ${index}`); @@ -472,25 +463,20 @@ export const ConversationList: React.FC = ({ const widthBreakpoint = getConversationListWidthBreakpoint(width); return ( - ` for an explanation of this `any` cast. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - overflowY: scrollable ? ('overlay' as any) : 'hidden', - }} - tabIndex={-1} - width={width} + shouldRecomputeRowHeights={shouldRecomputeRowHeights} + scrollable={scrollable} + scrollBehavior={scrollBehavior} /> ); }; diff --git a/ts/components/ListView.tsx b/ts/components/ListView.tsx new file mode 100644 index 000000000000..0469f3f256f6 --- /dev/null +++ b/ts/components/ListView.tsx @@ -0,0 +1,70 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import classNames from 'classnames'; +import React, { useEffect, useRef } from 'react'; +import type { Index, ListRowRenderer } from 'react-virtualized'; +import { List } from 'react-virtualized'; +import { ScrollBehavior } from '../types/Util'; + +type Props = { + width: number; + height: number; + rowCount: number; + calculateRowHeight: (index: number) => number; + rowRenderer: ListRowRenderer; + scrollToIndex?: number; + scrollable?: boolean; + className?: string; + shouldRecomputeRowHeights?: boolean; + scrollBehavior?: ScrollBehavior; +}; + +/** + * Thin wrapper around react-virtualized List. Simplified API and provides common + * defaults. + */ +export const ListView = ({ + width, + height, + rowCount, + calculateRowHeight, + rowRenderer, + scrollToIndex, + className, + scrollable = true, + shouldRecomputeRowHeights = false, + scrollBehavior = ScrollBehavior.Default, +}: Props): JSX.Element => { + const listRef = useRef(null); + + useEffect(() => { + const list = listRef.current; + if (shouldRecomputeRowHeights && list) { + list.recomputeRowHeights(); + } + }); + + return ( + calculateRowHeight(index.index)} + rowRenderer={rowRenderer} + scrollToIndex={scrollToIndex} + style={{ + // See `` for an explanation of this `any` cast. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + overflowY: scrollable ? ('overlay' as any) : 'hidden', + }} + tabIndex={-1} + /> + ); +}; diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index f4f9e4ec5336..211a37bc6fab 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -9052,10 +9052,10 @@ }, { "rule": "React-useRef", - "path": "ts/components/ConversationList.tsx", + "path": "ts/components/ListView.tsx", "line": " const listRef = useRef(null);", "reasonCategory": "usageTrusted", - "updated": "2021-07-30T16:57:33.618Z" + "updated": "2022-11-11T17:11:07.659Z" }, { "rule": "React-useRef",