From 40922d50a13ecf730974272af9fbeaf2cc8225a8 Mon Sep 17 00:00:00 2001 From: miko <34790748+keikari@users.noreply.github.com> Date: Fri, 31 May 2024 16:22:02 +0300 Subject: [PATCH] Add sort and search on uploads page - Improvement attempt (#3105) * Change "Uploads" to mass fetch all claims * Reset page number when filter changes * Add sorting options and search on uploads page (Copied from playlists page) * Bring back old logic Add toggle to enable filters * Mass fetch all my uploads only once * Show info text + spinner, if loading all claims takes long * Appstring * Fix Refresh button on claim_search tabs * Creation Time -> Release Time * Use reposted claim when searching and sorting by name * Sort by name: handle numeric --------- Co-authored-by: miko --- flow-typed/Claim.js | 2 +- flow-typed/Claims.js | 1 + static/app-strings.json | 2 + ui/constants/file_list.js | 43 +++ ui/page/fileListPublished/index.js | 12 + .../internal/fileListHeader/index.jsx | 107 ++++++ .../internal/rightSideActions/index.js | 10 + .../internal/rightSideActions/view.jsx | 106 ++++++ ui/page/fileListPublished/view.jsx | 324 ++++++++++++++---- ui/redux/actions/claims.js | 84 ++--- ui/redux/reducers/claims.js | 8 +- ui/redux/selectors/claims.js | 32 ++ ui/scss/component/_claim-search.scss | 31 +- 13 files changed, 650 insertions(+), 112 deletions(-) create mode 100644 ui/constants/file_list.js create mode 100644 ui/page/fileListPublished/internal/fileListHeader/index.jsx create mode 100644 ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/index.js create mode 100644 ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/view.jsx diff --git a/flow-typed/Claim.js b/flow-typed/Claim.js index e0bcbc5885..d0884f40e3 100644 --- a/flow-typed/Claim.js +++ b/flow-typed/Claim.js @@ -37,7 +37,7 @@ declare type GenericClaim = { type: 'claim' | 'update' | 'support', value_type: 'stream' | 'channel' | 'collection', signing_channel?: ChannelClaim, - reposted_claim?: GenericClaim, + reposted_claim?: Claim, repost_channel_url?: string, repost_url?: string, repost_bid_amount?: string, diff --git a/flow-typed/Claims.js b/flow-typed/Claims.js index 8e564397dc..9e14c9ae58 100644 --- a/flow-typed/Claims.js +++ b/flow-typed/Claims.js @@ -15,6 +15,7 @@ declare type ClaimsState = { failedToResolveUris: Array, failedToResolveIds: Array, reflectingById: { [ClaimId]: ReflectingUpdate }, + isAllMyClaimsFetched: ?boolean, myClaims: ?Array, myChannelClaimsById: ?{ [channelClaimId: string]: ChannelClaim }, resolvedCollectionsById: { [collectionClaimId: string]: Collection }, diff --git a/static/app-strings.json b/static/app-strings.json index 1dd60015e5..584042b8a6 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2933,6 +2933,8 @@ "Disable Tipping and Boosting": "Disable Tipping and Boosting", "You will not see content under 1min long. Also hides non-video/audio content.": "You will not see content under 1min long. Also hides non-video/audio content.", "Hide short content": "Hide short content", + "Larger upload list may take time to load with filters enabled": "Larger upload list may take time to load with filters enabled", + "Enable filters": "Enable filters", "--end--": "--end--" } diff --git a/ui/constants/file_list.js b/ui/constants/file_list.js new file mode 100644 index 0000000000..cb78a889fc --- /dev/null +++ b/ui/constants/file_list.js @@ -0,0 +1,43 @@ +export const FILTER_TYPE_KEY = 'filterType'; + +export const SEARCH_TERM_KEY = 'search'; + +export const PAGE_SIZE_ALL_ITEMS = 99999; + +export const FILE_TYPE = Object.freeze({ + ALL: { key: 'All', cmd: 'stream,repost', label: 'All', ariaLabel: 'All uploads' }, + UPLOADS: { key: 'Uploads', cmd: 'stream', label: 'Uploads' }, + REPOSTS: { key: 'Reposts', cmd: 'repost', label: 'Reposts' }, + UNLISTED: { key: 'Unlisted', cmd: '', label: 'Unlisted' }, + SCHEDULED: { key: 'Scheduled', cmd: '', label: 'Scheduled' }, +}); + +export const SORT_ORDER = Object.freeze({ + ASC: 'asc', // ascending + DESC: 'desc', // descending +}); + +export const SORT_KEYS = Object.freeze({ + NAME: 'name', + RELEASED_AT: 'releasedAt', + UPDATED_AT: 'updatedAt', +}); + +export const SORT_VALUES = Object.freeze({ + [SORT_KEYS.NAME]: { str: 'Name', orders: { [SORT_ORDER.ASC]: 'A-Z', [SORT_ORDER.DESC]: 'Z-A' } }, + [SORT_KEYS.RELEASED_AT]: { + str: 'Release Time', + orders: { [SORT_ORDER.ASC]: 'Newest First', [SORT_ORDER.DESC]: 'Oldest First' }, + }, + [SORT_KEYS.UPDATED_AT]: { + str: 'Updated Time', + orders: { [SORT_ORDER.ASC]: 'Newest First', [SORT_ORDER.DESC]: 'Oldest First' }, + }, +}); + +export const METHOD = Object.freeze({ + CLAIM_LIST: 'CLAIM_LIST', + CLAIM_SEARCH: 'CLAIM_SEARCH', +}); + +export const DEFAULT_SORT = { key: SORT_KEYS.UPDATED_AT, value: SORT_ORDER.ASC }; diff --git a/ui/page/fileListPublished/index.js b/ui/page/fileListPublished/index.js index b920d5fe09..9b59c58fb7 100644 --- a/ui/page/fileListPublished/index.js +++ b/ui/page/fileListPublished/index.js @@ -5,6 +5,12 @@ import { selectMyClaimsPageItemCount, selectFetchingMyClaimsPageError, selectMyChannelClaimIds, + selectMyPublicationClaims, + selectMyStreamClaims, + selectMyRepostClaims, + selectMyUnlistedClaims, + selectMyScheduledClaims, + selectIsAllMyClaimsFetched, } from 'redux/selectors/claims'; import { selectUploadCount } from 'redux/selectors/publish'; import { doFetchClaimListMine, doCheckPendingClaims, doClearClaimSearch } from 'redux/actions/claims'; @@ -25,6 +31,12 @@ const select = (state, props) => { fetching: selectIsFetchingClaimListMine(state), urls: selectMyClaimsPage(state), urlTotal: selectMyClaimsPageItemCount(state), + isAllMyClaimsFetched: selectIsAllMyClaimsFetched(state), + myClaims: selectMyPublicationClaims(state), + myStreamClaims: selectMyStreamClaims(state), + myRepostClaims: selectMyRepostClaims(state), + myUnlistedClaims: selectMyUnlistedClaims(state), + myScheduledClaims: selectMyScheduledClaims(state), error: selectFetchingMyClaimsPageError(state), uploadCount: selectUploadCount(state), myChannelIds: selectMyChannelClaimIds(state), diff --git a/ui/page/fileListPublished/internal/fileListHeader/index.jsx b/ui/page/fileListPublished/internal/fileListHeader/index.jsx new file mode 100644 index 0000000000..33a0280500 --- /dev/null +++ b/ui/page/fileListPublished/internal/fileListHeader/index.jsx @@ -0,0 +1,107 @@ +// @flow +import React from 'react'; +import Button from 'component/button'; +import * as FILE_LIST from 'constants/file_list'; +import { FileListContext } from 'page/fileListPublished/view'; +import classnames from 'classnames'; +import { FormField } from 'component/common/form'; +import { useHistory } from 'react-router'; +import RightSideActions from './internal/rightSideActions/index'; + +type Props = { + filterType: string, + sortOption: { key: string, value: string }, + setFilterType: (type: string) => void, + setSortOption: (params: { key: string, value: string }) => void, +}; + +export default function ClaimListHeader(props: Props) { + const { filterType, sortOption, setFilterType, setSortOption } = props; + + const { isFilteringEnabled } = React.useContext(FileListContext); + + const history = useHistory(); + const { + location: { search }, + } = history; + + const urlParams = new URLSearchParams(search); + + function handleChange(sortObj) { + // can only have one sorting option at a time + Object.keys(FILE_LIST.SORT_VALUES).forEach((k) => urlParams.get(k) && urlParams.delete(k)); + + urlParams.set(sortObj.key, sortObj.value); + setSortOption(sortObj); + + const url = `?${urlParams.toString()}`; + history.push(url); + } + + function handleFilterTypeChange(value) { + urlParams.set(FILE_LIST.FILTER_TYPE_KEY, value); + setFilterType(value); + + const url = `?${urlParams.toString()}`; + history.push(url); + } + + return ( +
+
+
+ {/* Filter Options */} +
+
+ {/* $FlowFixMe */} + {Object.values(FILE_LIST.FILE_TYPE).map((info: FilterInfo) => ( +
+ {isFilteringEnabled && ( +
+ handleChange({ key: e.target.value, value: FILE_LIST.SORT_ORDER.ASC })} + > + {Object.entries(FILE_LIST.SORT_VALUES).map(([key, value]) => ( + + ))} + + handleChange({ key: sortOption.key, value: e.target.value })} + > + {Object.entries(FILE_LIST.SORT_ORDER).map(([key, value]) => ( + // $FlowFixMe + + ))} + +
+ )} +
+
+ + +
+
+ ); +} diff --git a/ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/index.js b/ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/index.js new file mode 100644 index 0000000000..e1a2113462 --- /dev/null +++ b/ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/index.js @@ -0,0 +1,10 @@ +import { connect } from 'react-redux'; +import { doClearClaimSearch, doFetchClaimListMine } from 'redux/actions/claims'; +import RightSideActions from './view'; + +const perform = { + doClearClaimSearch, + fetchClaimListMine: doFetchClaimListMine, +}; + +export default connect(null, perform)(RightSideActions); diff --git a/ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/view.jsx b/ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/view.jsx new file mode 100644 index 0000000000..012d553d11 --- /dev/null +++ b/ui/page/fileListPublished/internal/fileListHeader/internal/rightSideActions/view.jsx @@ -0,0 +1,106 @@ +// @flow +import type { DoFetchClaimListMine } from 'redux/actions/claims'; + +import React from 'react'; +import { FormField, Form } from 'component/common/form'; +import { useHistory } from 'react-router'; +import { FileListContext } from 'page/fileListPublished/view'; +import * as FILE_LIST from 'constants/file_list'; +import * as KEYCODES from 'constants/keycodes'; +import * as ICONS from 'constants/icons'; +import Icon from 'component/common/icon'; +import Button from 'component/button'; + +type Props = { + // -- redux -- + fetchClaimListMine: DoFetchClaimListMine, + doClearClaimSearch: () => void, +}; + +const RightSideActions = (props: Props) => { + const { fetchClaimListMine, doClearClaimSearch } = props; + const { searchText, setSearchText, isFilteringEnabled, setIsFilteringEnabled, fetching, method } = + React.useContext(FileListContext); + + const history = useHistory(); + const { + location: { search }, + } = history; + const urlParams = new URLSearchParams(search); + + function handleSearchTextChange(value) { + setSearchText(value); + + if (value === '') { + urlParams.get(FILE_LIST.SEARCH_TERM_KEY) && urlParams.delete(FILE_LIST.SEARCH_TERM_KEY); + } else { + urlParams.set(FILE_LIST.SEARCH_TERM_KEY, value); + } + + const url = `?${urlParams.toString()}`; + history.push(url); + } + + function escapeListener(e: SyntheticKeyboardEvent<*>) { + if (e.keyCode === KEYCODES.ESCAPE) { + e.preventDefault(); + setSearchText(''); + } + } + + function onTextareaFocus() { + window.addEventListener('keydown', escapeListener); + } + + function onTextareaBlur() { + window.removeEventListener('keydown', escapeListener); + } + + return ( +
+ {/* Search Field */} +
+ {isFilteringEnabled && ( +
{}} className="wunderbar--inline"> + + handleSearchTextChange(e.target.value)} + type="text" + placeholder={__('Search')} + /> + + )} +
+ setIsFilteringEnabled(!isFilteringEnabled)} + /> +
+
+ {/* Playlist Create Button */} +
+ ); +}; + +export default RightSideActions; diff --git a/ui/page/fileListPublished/view.jsx b/ui/page/fileListPublished/view.jsx index 0331a39e49..d1a2d94a89 100644 --- a/ui/page/fileListPublished/view.jsx +++ b/ui/page/fileListPublished/view.jsx @@ -3,7 +3,9 @@ import type { DoFetchClaimListMine } from 'redux/actions/claims'; import './style.scss'; import * as ICONS from 'constants/icons'; +import * as FILE_LIST from 'constants/file_list'; import React, { useEffect } from 'react'; +import { useLocation } from 'react-router'; import Button from 'component/button'; import ClaimList from 'component/claimList'; import ClaimPreview from 'component/claimPreview'; @@ -16,22 +18,9 @@ import { SCHEDULED_TAGS, VISIBILITY_TAGS } from 'constants/tags'; import WebUploadList from 'component/webUploadList'; import Spinner from 'component/spinner'; import Yrbl from 'component/yrbl'; -import classnames from 'classnames'; +import usePersistedState from 'effects/use-persisted-state'; -type FilterInfo = { key: string, cmd: string, label: string, ariaLabel?: string }; - -const FILTER: { [string]: FilterInfo } = Object.freeze({ - ALL: { key: 'ALL', cmd: 'stream,repost', label: 'All', ariaLabel: 'All uploads' }, - UPLOADS: { key: 'UPLOADS', cmd: 'stream', label: 'Uploads' }, - REPOSTS: { key: 'REPOSTS', cmd: 'repost', label: 'Reposts' }, - UNLISTED: { key: 'UNLISTED', cmd: '', label: 'Unlisted' }, - SCHEDULED: { key: 'SCHEDULED', cmd: '', label: 'Scheduled' }, -}); - -const METHOD = { - CLAIM_LIST: 'CLAIM_LIST', - CLAIM_SEARCH: 'CLAIM_SEARCH', -}; +import ClaimListHeader from './internal/fileListHeader/index'; type Props = { uploadCount: number, @@ -41,6 +30,12 @@ type Props = { fetching: boolean, urls: Array, urlTotal: number, + isAllMyClaimsFetched: ?boolean, + myClaims: Array, + myStreamClaims: Array, + myRepostClaims: Array, + myUnlistedClaims: Array, + myScheduledClaims: Array, history: { replace: (string) => void, push: (string) => void }, page: number, pageSize: number, @@ -48,6 +43,9 @@ type Props = { doClearClaimSearch: () => void, }; +// Avoid prop drilling +export const FileListContext = React.createContext(); + function FileListPublished(props: Props) { const { uploadCount, @@ -57,15 +55,40 @@ function FileListPublished(props: Props) { fetching, urls, urlTotal, + isAllMyClaimsFetched, + myClaims, + myStreamClaims, + myRepostClaims, + myUnlistedClaims, + myScheduledClaims, page, pageSize, myChannelIds, doClearClaimSearch, } = props; - const [filterBy, setFilterBy] = React.useState(FILTER.ALL.key); + const { search } = useLocation(); + const urlParams = new URLSearchParams(search); + + const [isLoadingLong, setIsLoadingLong] = React.useState(false); + + const sortByParam = Object.keys(FILE_LIST.SORT_VALUES).find((key) => urlParams.get(key)); + const [isFilteringEnabled, setIsFilteringEnabled] = usePersistedState('filelist-enable-filters', false); + const [persistedOption, setPersistedOption] = usePersistedState('filelist-sort', FILE_LIST.DEFAULT_SORT); + const defaultSortOption = sortByParam ? { key: sortByParam, value: urlParams.get(sortByParam) } : persistedOption; + const defaultFilterType = urlParams.get(FILE_LIST.FILTER_TYPE_KEY) || 'All'; + const defaultSearchTerm = urlParams.get(FILE_LIST.SEARCH_TERM_KEY) || ''; + + const [filterType, setFilterType] = React.useState(defaultFilterType); + const [searchText, setSearchText] = React.useState(defaultSearchTerm); + const [sortOption, setSortOption] = React.useState(defaultSortOption); + const [filterParamsChanged, setFilterParamsChanged] = React.useState(false); + const method = - filterBy === FILTER.UNLISTED.key || filterBy === FILTER.SCHEDULED.key ? METHOD.CLAIM_SEARCH : METHOD.CLAIM_LIST; + (filterType === FILE_LIST.FILE_TYPE.UNLISTED.key || filterType === FILE_LIST.FILE_TYPE.SCHEDULED.key) && + !isFilteringEnabled + ? FILE_LIST.METHOD.CLAIM_SEARCH + : FILE_LIST.METHOD.CLAIM_LIST; const params = React.useMemo(() => { return { @@ -99,8 +122,124 @@ function FileListPublished(props: Props) { }; }, [myChannelIds]); + const claimsToShow = React.useMemo(() => { + if (!isFilteringEnabled) { + return []; + } + let claims; + switch (filterType) { + case FILE_LIST.FILE_TYPE.ALL.key: + claims = myClaims; + break; + case FILE_LIST.FILE_TYPE.UPLOADS.key: + claims = myStreamClaims; + break; + case FILE_LIST.FILE_TYPE.REPOSTS.key: + claims = myRepostClaims; + break; + case FILE_LIST.FILE_TYPE.UNLISTED.key: + claims = myUnlistedClaims; + break; + case FILE_LIST.FILE_TYPE.SCHEDULED.key: + claims = myScheduledClaims; + break; + default: + claims = []; + break; + } + return claims; + }, [myClaims, myStreamClaims, myRepostClaims, myUnlistedClaims, myScheduledClaims, filterType, isFilteringEnabled]); + + const filteredClaims = React.useMemo(() => { + if (!isFilteringEnabled) { + return []; + } + let result = claimsToShow; + + // First handle search + if (searchText) { + result = result.filter((claim) => { + const contentClaim = claim.reposted_claim ? claim.reposted_claim : claim; + // $FlowFixMe + return contentClaim.value?.title?.toLocaleLowerCase().includes(searchText.toLocaleLowerCase()); + }); + } + + // Then the sorting selected setting + return result.sort((claimA, claimB) => { + const nameComparisonClaimA = claimA.reposted_claim ? claimA.reposted_claim : claimA; + const nameComparisonClaimB = claimB.reposted_claim ? claimB.reposted_claim : claimB; + + let firstComparisonItem = + sortOption.value === FILE_LIST.SORT_ORDER.ASC ? nameComparisonClaimA : nameComparisonClaimB; + let secondComparisonItem = + sortOption.value === FILE_LIST.SORT_ORDER.ASC ? nameComparisonClaimB : nameComparisonClaimA; + const comparisonObj = {}; + + if (sortOption.key === FILE_LIST.SORT_KEYS.NAME) { + const nameComparisonObj = { + a: firstComparisonItem.value?.title || firstComparisonItem.name, + b: secondComparisonItem.value?.title || secondComparisonItem.name, + }; + + Object.assign(comparisonObj, nameComparisonObj); + + // Only name (string) has a different return than when sorting numbers + // $FlowFixMe + return comparisonObj.a.localeCompare(comparisonObj.b, undefined, { numeric: true, sensitivity: 'base' }); + } + + function getComparisonObj() { + let timestampComparisonObj = {}; + switch (sortOption.key) { + case FILE_LIST.SORT_KEYS.RELEASED_AT: + firstComparisonItem = sortOption.value === FILE_LIST.SORT_ORDER.DESC ? claimA : claimB; + secondComparisonItem = sortOption.value === FILE_LIST.SORT_ORDER.DESC ? claimB : claimA; + + timestampComparisonObj = { + // $FlowFixMe + a: firstComparisonItem.value?.release_time || firstComparisonItem.meta.creation_timestamp, + // $FlowFixMe + b: secondComparisonItem.value?.release_time || secondComparisonItem.meta.creation_timestamp, + }; + + Object.assign(comparisonObj, timestampComparisonObj); + + break; + + case FILE_LIST.SORT_KEYS.UPDATED_AT: + firstComparisonItem = sortOption.value === FILE_LIST.SORT_ORDER.DESC ? claimA : claimB; + secondComparisonItem = sortOption.value === FILE_LIST.SORT_ORDER.DESC ? claimB : claimA; + + timestampComparisonObj = { + a: firstComparisonItem.height > 0 ? firstComparisonItem.height : 999999999999999, + b: secondComparisonItem.height > 0 ? secondComparisonItem.height : 999999999999999, + }; + + Object.assign(comparisonObj, timestampComparisonObj); + + break; + } + } + + getComparisonObj(); + + // $FlowFixMe + if ((comparisonObj.a || 0) > (comparisonObj.b || 0)) { + return 1; + } + + // $FlowFixMe + if ((comparisonObj.a || 0) < (comparisonObj.b || 0)) { + return -1; + } + + return 0; + }); + }, [claimsToShow, searchText, sortOption, isFilteringEnabled]); + const AdvisoryMsg = () => { - if (filterBy === FILTER.UNLISTED.key) { + if (filterType === FILE_LIST.FILE_TYPE.UNLISTED.key) { return (
@@ -115,43 +254,14 @@ function FileListPublished(props: Props) { return null; }; - function getHeaderJsx() { - return ( -
-
- {/* $FlowIgnore: mixed bug */} - {Object.values(FILTER).map((info: FilterInfo) => ( -
-
- {!fetching && ( -
-
- ); - } - function getClaimListResultsJsx() { + const startIndex = (page - 1) * Number(pageSize); + const endIndex = startIndex + Number(pageSize); + + const claimUrls = !isFilteringEnabled + ? urls + : filteredClaims.slice(startIndex, endIndex).map((claim) => claim.permanent_url); + const totalClaims = !isFilteringEnabled ? urlTotal : filteredClaims?.length; return ( <> {!!urls && ( @@ -160,11 +270,21 @@ function FileListPublished(props: Props) { noEmpty persistedStorageKey="claim-list-published" showHiddenByUser - uris={fetching ? [] : urls} + uris={fetching ? [] : claimUrls} loading={fetching} /> + {isLoadingLong && isFilteringEnabled && ( +
+ +

{__('Larger upload list may take time to load with filters enabled')}

+
+ )} + {getFetchingPlaceholders()} - 0 ? Math.ceil(urlTotal / Number(pageSize)) : 1} /> + 0 ? Math.ceil(totalClaims / Number(pageSize)) : 1} + shouldResetPageNumber={filterParamsChanged} + /> )} @@ -172,7 +292,7 @@ function FileListPublished(props: Props) { } function getClaimSearchResultsJsx() { - const isUnlisted = filterBy === FILTER.UNLISTED.key; + const isUnlisted = filterType === FILE_LIST.FILE_TYPE.UNLISTED.key; const hasChannels = myChannelIds ? myChannelIds.length > 0 : false; return hasChannels ? ( @@ -193,7 +313,7 @@ function FileListPublished(props: Props) { function getFetchingPlaceholders() { return ( <> - {method === METHOD.CLAIM_LIST && + {method === FILE_LIST.METHOD.CLAIM_LIST && fetching && new Array(Number(pageSize)).fill(1).map((x, i) => { return ; @@ -207,33 +327,101 @@ function FileListPublished(props: Props) { }, [checkPendingPublishes]); useEffect(() => { - if (params && fetchClaimListMine && method === METHOD.CLAIM_LIST) { - fetchClaimListMine(params.page, params.page_size, true, FILTER[filterBy].cmd.split(','), true); + if (isFilteringEnabled) { + return; + } + if (method === FILE_LIST.METHOD.CLAIM_LIST) { + fetchClaimListMine( + params.page, + params.page_size, + true, + // $FlowFixMe + Object.values(FILE_LIST.FILE_TYPE) + // $FlowFixMe + .find((fileType) => fileType.key === filterType) + .cmd.split(','), + true + ); + } else { + doClearClaimSearch(); + } + }, [uploadCount, params, filterType, fetchClaimListMine, method, isFilteringEnabled, doClearClaimSearch]); + + useEffect(() => { + if (!isFilteringEnabled || isAllMyClaimsFetched) { + return; + } + fetchClaimListMine(1, FILE_LIST.PAGE_SIZE_ALL_ITEMS, true, [], true); + }, [isFilteringEnabled, isAllMyClaimsFetched, fetchClaimListMine]); + + const firstUpdate = React.useRef(true); + React.useLayoutEffect(() => { + if (firstUpdate.current) { + firstUpdate.current = false; + return; + } + setFilterParamsChanged(true); + }, [searchText, filterType, sortOption]); + + React.useEffect(() => { + if (filterParamsChanged) { + setFilterParamsChanged(false); + } + }, [filterParamsChanged]); + + React.useEffect(() => { + setPersistedOption(sortOption); + // eslint-disable-next-line react-hooks/exhaustive-deps -- (setPersistedOption is custom setState, can ignore) + }, [sortOption]); + + React.useEffect(() => { + let timeout; + setIsLoadingLong(false); + if (fetching === true) { + timeout = setTimeout(() => setIsLoadingLong(true), 7500); } - }, [uploadCount, params, filterBy, fetchClaimListMine, method]); + return () => clearTimeout(timeout); + }, [fetching]); // eslint-disable-line react-hooks/exhaustive-deps return (
- {getHeaderJsx()} + + + - {method === METHOD.CLAIM_LIST && getClaimListResultsJsx()} - {method === METHOD.CLAIM_SEARCH && getClaimSearchResultsJsx()} + {method === FILE_LIST.METHOD.CLAIM_LIST && getClaimListResultsJsx()} + {method === FILE_LIST.METHOD.CLAIM_SEARCH && getClaimSearchResultsJsx()}
- {!(urls && urls.length) && method === METHOD.CLAIM_LIST && ( + {!(myClaims && myClaims.length) && method === FILE_LIST.METHOD.CLAIM_LIST && ( {!fetching ? (