diff --git a/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx b/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx index 10932641d9..8efc53d0e8 100644 --- a/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx +++ b/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx @@ -6,19 +6,16 @@ import { OwnerPaywallPrompt } from './owner-paywall-prompt' import { NonOwnerPaywallPrompt } from './non-owner-paywall-prompt' function AllHistoryList() { - const { - updatesInfo, - loadingState, - fetchNextBatchOfUpdates, - currentUserIsOwner, - } = useHistoryContext() + const { updatesInfo, fetchNextBatchOfUpdates, currentUserIsOwner } = + useHistoryContext() + const updatesLoadingState = updatesInfo.loadingState const { visibleUpdateCount, updates, atEnd } = updatesInfo const scrollerRef = useRef(null) const bottomRef = useRef(null) const intersectionObserverRef = useRef(null) const [bottomVisible, setBottomVisible] = useState(false) const showPaywall = - loadingState === 'ready' && updatesInfo.freeHistoryLimitHit + updatesLoadingState === 'ready' && updatesInfo.freeHistoryLimitHit const showOwnerPaywall = showPaywall && currentUserIsOwner const showNonOwnerPaywall = showPaywall && !currentUserIsOwner const visibleUpdates = @@ -27,7 +24,7 @@ function AllHistoryList() { // Create an intersection observer that watches for any part of an element // positioned at the bottom of the list to be visible useEffect(() => { - if (loadingState === 'ready' && !intersectionObserverRef.current) { + if (updatesLoadingState === 'ready' && !intersectionObserverRef.current) { const scroller = scrollerRef.current const bottom = bottomRef.current @@ -48,13 +45,13 @@ function AllHistoryList() { } } } - }, [loadingState]) + }, [updatesLoadingState]) useEffect(() => { - if (!atEnd && loadingState === 'ready' && bottomVisible) { + if (!atEnd && updatesLoadingState === 'ready' && bottomVisible) { fetchNextBatchOfUpdates() } - }, [atEnd, bottomVisible, fetchNextBatchOfUpdates, loadingState]) + }, [atEnd, bottomVisible, fetchNextBatchOfUpdates, updatesLoadingState]) // While updates are loading, remove the intersection observer and set // bottomVisible to false. This is to avoid loading more updates immediately @@ -62,14 +59,14 @@ function AllHistoryList() { // the intersection observer is asynchronous and won't have noticed that the // bottom is no longer visible useEffect(() => { - if (loadingState !== 'ready' && intersectionObserverRef.current) { + if (updatesLoadingState !== 'ready' && intersectionObserverRef.current) { setBottomVisible(false) if (intersectionObserverRef.current) { intersectionObserverRef.current.disconnect() intersectionObserverRef.current = null } } - }, [loadingState]) + }, [updatesLoadingState]) return (
@@ -92,8 +89,8 @@ function AllHistoryList() {
{showOwnerPaywall ? : null} {showNonOwnerPaywall ? : null} - {loadingState === 'loadingInitial' || - loadingState === 'loadingUpdates' ? ( + {updatesLoadingState === 'loadingInitial' || + updatesLoadingState === 'loadingUpdates' ? ( ) : null} diff --git a/services/web/frontend/js/features/history/components/diff-view/diff-view.tsx b/services/web/frontend/js/features/history/components/diff-view/diff-view.tsx index dcc2ca03ab..abcdd9509d 100644 --- a/services/web/frontend/js/features/history/components/diff-view/diff-view.tsx +++ b/services/web/frontend/js/features/history/components/diff-view/diff-view.tsx @@ -9,8 +9,7 @@ import useAsync from '../../../../shared/hooks/use-async' import ErrorMessage from '../error-message' function DiffView() { - const { selection, projectId, loadingState } = useHistoryContext() - const loadingFileDiffs = loadingState === 'loadingFileDiffs' + const { selection, projectId, loadingFileDiffs } = useHistoryContext() const { isLoading, data, runAsync, error } = useAsync() const { updateRange, selectedFile } = selection diff --git a/services/web/frontend/js/features/history/components/diff-view/toolbar/toolbar-restore-file-button.tsx b/services/web/frontend/js/features/history/components/diff-view/toolbar/toolbar-restore-file-button.tsx index ec51e5f340..c8fbbc4047 100644 --- a/services/web/frontend/js/features/history/components/diff-view/toolbar/toolbar-restore-file-button.tsx +++ b/services/web/frontend/js/features/history/components/diff-view/toolbar/toolbar-restore-file-button.tsx @@ -1,6 +1,5 @@ import { Button } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import { useHistoryContext } from '../../../context/history-context' import { useRestoreDeletedFile } from '../../../context/hooks/use-restore-deleted-file' import type { HistoryContextValue } from '../../../context/types/history-context-value' @@ -12,21 +11,18 @@ export default function ToolbarRestoreFileButton({ selection, }: ToolbarRestoreFileButtonProps) { const { t } = useTranslation() - const { loadingState } = useHistoryContext() - const onRestoreFile = useRestoreDeletedFile() + const { restoreDeletedFile, isLoading } = useRestoreDeletedFile() return ( ) } diff --git a/services/web/frontend/js/features/history/components/history-root.tsx b/services/web/frontend/js/features/history/components/history-root.tsx index f84fed47b6..345139c7c7 100644 --- a/services/web/frontend/js/features/history/components/history-root.tsx +++ b/services/web/frontend/js/features/history/components/history-root.tsx @@ -9,10 +9,10 @@ import ErrorMessage from './error-message' const fileTreeContainer = document.getElementById('history-file-tree') function Main() { - const { loadingState, error } = useHistoryContext() + const { updatesInfo, error } = useHistoryContext() let content = null - if (loadingState === 'loadingInitial') { + if (updatesInfo.loadingState === 'loadingInitial') { content = } else if (error) { content = diff --git a/services/web/frontend/js/features/history/context/history-context.tsx b/services/web/frontend/js/features/history/context/history-context.tsx index 38b812ccb6..17339cfa18 100644 --- a/services/web/frontend/js/features/history/context/history-context.tsx +++ b/services/web/frontend/js/features/history/context/history-context.tsx @@ -85,17 +85,19 @@ function useHistory() { atEnd: false, freeHistoryLimitHit: false, nextBeforeTimestamp: undefined, + loadingState: 'loadingInitial', }) const [labels, setLabels] = useState(null) const [labelsOnly, setLabelsOnly] = usePersistedState( `history.userPrefs.showOnlyLabels.${projectId}`, false ) - const [loadingState, setLoadingState] = - useState('loadingInitial') + const [loadingFileDiffs, setLoadingFileDiffs] = useState(false) const [error, setError] = useState(null) const fetchNextBatchOfUpdates = useCallback(() => { + const updatesLoadingState = updatesInfo.loadingState + const loadUpdates = (updatesData: Update[]) => { const dateTimeNow = new Date() const timestamp24hoursAgo = dateTimeNow.setDate(dateTimeNow.getDate() - 1) @@ -140,7 +142,10 @@ function useHistory() { if ( updatesInfo.atEnd || - !(loadingState === 'loadingInitial' || loadingState === 'ready') + !( + updatesLoadingState === 'loadingInitial' || + updatesLoadingState === 'ready' + ) ) { return } @@ -150,9 +155,11 @@ function useHistory() { ) const labelsPromise = labels == null ? fetchLabels(projectId) : null - setLoadingState( - loadingState === 'ready' ? 'loadingUpdates' : 'loadingInitial' - ) + setUpdatesInfo({ + ...updatesInfo, + loadingState: + updatesLoadingState === 'ready' ? 'loadingUpdates' : 'loadingInitial', + }) Promise.all([updatesPromise, labelsPromise]) .then(([{ updates: updatesData, nextBeforeTimestamp }, labels]) => { const lastUpdateToV = updatesData.length ? updatesData[0].toV : null @@ -173,16 +180,14 @@ function useHistory() { freeHistoryLimitHit, atEnd, nextBeforeTimestamp, + loadingState: 'ready', }) }) .catch(error => { setError(error) - setUpdatesInfo({ ...updatesInfo, atEnd: true }) + setUpdatesInfo({ ...updatesInfo, atEnd: true, loadingState: 'ready' }) }) - .finally(() => { - setLoadingState('ready') - }) - }, [loadingState, labels, projectId, userHasFullFeature, updatesInfo]) + }, [updatesInfo, projectId, labels, userHasFullFeature]) const resetSelection = useCallback(() => { setSelection(selectionInitialState) @@ -203,12 +208,12 @@ function useHistory() { // Load files when the update selection changes useEffect(() => { - if (!updateRange || loadingState !== 'ready' || !filesEmpty) { + if (!updateRange || updatesInfo.loadingState !== 'ready' || !filesEmpty) { return } const { fromV, toV } = updateRange - setLoadingState('loadingFileDiffs') + setLoadingFileDiffs(true) diffFiles(projectId, fromV, toV) .then(({ diff: files }) => { @@ -236,7 +241,7 @@ function useHistory() { setError(error) }) .finally(() => { - setLoadingState('ready') + setLoadingFileDiffs(false) }) }, [ updateRange, @@ -244,7 +249,7 @@ function useHistory() { updates, comparing, setError, - loadingState, + updatesInfo.loadingState, filesEmpty, ]) @@ -268,8 +273,8 @@ function useHistory() { () => ({ error, setError, - loadingState, - setLoadingState, + loadingFileDiffs, + setLoadingFileDiffs, updatesInfo, setUpdatesInfo, labels, @@ -287,8 +292,8 @@ function useHistory() { [ error, setError, - loadingState, - setLoadingState, + loadingFileDiffs, + setLoadingFileDiffs, updatesInfo, setUpdatesInfo, labels, diff --git a/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts b/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts index 79584b79b5..2382ed66d7 100644 --- a/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts +++ b/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts @@ -9,17 +9,18 @@ import { useHistoryContext } from '../history-context' import type { HistoryContextValue } from '../types/history-context-value' export function useRestoreDeletedFile() { - const { runAsync } = useAsync() - const { setLoadingState, projectId, setError } = useHistoryContext() + const { isLoading, runAsync } = useAsync() + const { projectId, setError } = useHistoryContext() const ide = useIdeContext() const { setView } = useLayoutContext() - return async (selection: HistoryContextValue['selection']) => { + const restoreDeletedFile = async ( + selection: HistoryContextValue['selection'] + ) => { const { selectedFile } = selection if (selectedFile && selectedFile.pathname && isFileRemoved(selectedFile)) { sendMB('history-v2-restore-deleted') - setLoadingState('restoringFile') await runAsync( restoreFile(projectId, selectedFile) @@ -40,10 +41,9 @@ export function useRestoreDeletedFile() { setView('editor') }) .catch(error => setError(error)) - .finally(() => { - setLoadingState('ready') - }) ) } } + + return { restoreDeletedFile, isLoading } } diff --git a/services/web/frontend/js/features/history/context/types/history-context-value.ts b/services/web/frontend/js/features/history/context/types/history-context-value.ts index 8d604dffe9..fca47cba3c 100644 --- a/services/web/frontend/js/features/history/context/types/history-context-value.ts +++ b/services/web/frontend/js/features/history/context/types/history-context-value.ts @@ -3,12 +3,7 @@ import { LoadedUpdate } from '../../services/types/update' import { LoadedLabel } from '../../services/types/label' import { Selection } from '../../services/types/selection' -type LoadingState = - | 'loadingInitial' - | 'loadingUpdates' - | 'loadingFileDiffs' - | 'restoringFile' - | 'ready' +type UpdatesLoadingState = 'loadingInitial' | 'loadingUpdates' | 'ready' export type HistoryContextValue = { updatesInfo: { @@ -17,16 +12,14 @@ export type HistoryContextValue = { atEnd: boolean nextBeforeTimestamp: number | undefined freeHistoryLimitHit: boolean + loadingState: UpdatesLoadingState } setUpdatesInfo: React.Dispatch< React.SetStateAction > userHasFullFeature: boolean currentUserIsOwner: boolean - loadingState: LoadingState - setLoadingState: React.Dispatch< - React.SetStateAction - > + loadingFileDiffs: boolean error: Nullable setError: React.Dispatch> labels: Nullable