mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 05:53:47 -05:00
History migration: Add error handling for all history requests (#12872)
* Add error handling for all history requests * Remove comment GitOrigin-RevId: 528dc98a0fc4ab523f8536274996c4166be45064
This commit is contained in:
parent
70bae34bd8
commit
1fb921de99
9 changed files with 86 additions and 49 deletions
|
@ -4,7 +4,7 @@ import { Alert } from 'react-bootstrap'
|
||||||
// Using this workaround due to inconsistent and improper error responses from the server
|
// Using this workaround due to inconsistent and improper error responses from the server
|
||||||
type ModalErrorProps = {
|
type ModalErrorProps = {
|
||||||
error: {
|
error: {
|
||||||
response: Response
|
response?: Response
|
||||||
data?: {
|
data?: {
|
||||||
message?: string
|
message?: string
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ type ModalErrorProps = {
|
||||||
function ModalError({ error }: ModalErrorProps) {
|
function ModalError({ error }: ModalErrorProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
if (error.response.status === 400 && error?.data?.message) {
|
if (error.response?.status === 400 && error.data?.message) {
|
||||||
return <Alert bsStyle="danger">{error.data.message}</Alert>
|
return <Alert bsStyle="danger">{error.data.message}</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,13 @@ import { useHistoryContext } from '../../context/history-context'
|
||||||
import { diffDoc } from '../../services/api'
|
import { diffDoc } from '../../services/api'
|
||||||
import { highlightsFromDiffResponse } from '../../utils/highlights-from-diff-response'
|
import { highlightsFromDiffResponse } from '../../utils/highlights-from-diff-response'
|
||||||
import useAsync from '../../../../shared/hooks/use-async'
|
import useAsync from '../../../../shared/hooks/use-async'
|
||||||
|
import ErrorMessage from '../error-message'
|
||||||
|
|
||||||
function DiffView() {
|
function DiffView() {
|
||||||
const [diff, setDiff] = useState<Nullable<Diff>>(null)
|
const [diff, setDiff] = useState<Nullable<Diff>>(null)
|
||||||
const { selection, projectId } = useHistoryContext()
|
const { selection, projectId } = useHistoryContext()
|
||||||
|
|
||||||
const { isLoading, runAsync } = useAsync<DocDiffResponse>()
|
const { isLoading, runAsync, error } = useAsync<DocDiffResponse>()
|
||||||
|
|
||||||
const { updateRange, selectedFile } = selection
|
const { updateRange, selectedFile } = selection
|
||||||
|
|
||||||
|
@ -23,9 +24,8 @@ function DiffView() {
|
||||||
|
|
||||||
const { fromV, toV } = updateRange
|
const { fromV, toV } = updateRange
|
||||||
|
|
||||||
// TODO: Error handling
|
runAsync(diffDoc(projectId, fromV, toV, selectedFile.pathname))
|
||||||
runAsync(diffDoc(projectId, fromV, toV, selectedFile.pathname)).then(
|
.then(data => {
|
||||||
data => {
|
|
||||||
let diff: Diff | undefined
|
let diff: Diff | undefined
|
||||||
|
|
||||||
if (!data?.diff) {
|
if (!data?.diff) {
|
||||||
|
@ -42,18 +42,24 @@ function DiffView() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setDiff(diff)
|
setDiff(diff)
|
||||||
}
|
})
|
||||||
)
|
.catch(console.error)
|
||||||
}, [projectId, runAsync, updateRange, selectedFile])
|
}, [projectId, runAsync, updateRange, selectedFile])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="doc-panel">
|
<div className="doc-panel">
|
||||||
<div className="history-header toolbar-container">
|
{error ? (
|
||||||
<Toolbar diff={diff} selection={selection} />
|
<ErrorMessage />
|
||||||
</div>
|
) : (
|
||||||
<div className="doc-container">
|
<>
|
||||||
<Main diff={diff} isLoading={isLoading} />
|
<div className="history-header toolbar-container">
|
||||||
</div>
|
<Toolbar diff={diff} selection={selection} />
|
||||||
|
</div>
|
||||||
|
<div className="doc-container">
|
||||||
|
<Main diff={diff} isLoading={isLoading} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export default function ErrorMessage() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="history-error">
|
||||||
|
<div className="text-danger error">
|
||||||
|
{t('generic_something_went_wrong')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -7,13 +7,13 @@ import {
|
||||||
import HistoryFileTreeFolderList from './file-tree/history-file-tree-folder-list'
|
import HistoryFileTreeFolderList from './file-tree/history-file-tree-folder-list'
|
||||||
|
|
||||||
export default function HistoryFileTree() {
|
export default function HistoryFileTree() {
|
||||||
const { files } = useHistoryContext().selection
|
const { selection, error } = useHistoryContext()
|
||||||
|
|
||||||
const fileTree = _.reduce(files, reducePathsToTree, [])
|
const fileTree = _.reduce(selection.files, reducePathsToTree, [])
|
||||||
|
|
||||||
const mappedFileTree = fileTreeDiffToFileTreeData(fileTree)
|
const mappedFileTree = fileTreeDiffToFileTreeData(fileTree)
|
||||||
|
|
||||||
return (
|
return error ? null : (
|
||||||
<HistoryFileTreeFolderList
|
<HistoryFileTreeFolderList
|
||||||
folders={mappedFileTree.folders}
|
folders={mappedFileTree.folders}
|
||||||
docs={mappedFileTree.docs ?? []}
|
docs={mappedFileTree.docs ?? []}
|
||||||
|
|
|
@ -4,27 +4,33 @@ import { HistoryProvider, useHistoryContext } from '../context/history-context'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import HistoryFileTree from './history-file-tree'
|
import HistoryFileTree from './history-file-tree'
|
||||||
import LoadingSpinner from '../../../shared/components/loading-spinner'
|
import LoadingSpinner from '../../../shared/components/loading-spinner'
|
||||||
|
import ErrorMessage from './error-message'
|
||||||
|
|
||||||
const fileTreeContainer = document.getElementById('history-file-tree')
|
const fileTreeContainer = document.getElementById('history-file-tree')
|
||||||
|
|
||||||
function Main() {
|
function Main() {
|
||||||
const { loadingState } = useHistoryContext()
|
const { loadingState, error } = useHistoryContext()
|
||||||
|
|
||||||
|
let content = null
|
||||||
|
if (loadingState === 'loadingInitial') {
|
||||||
|
content = <LoadingSpinner />
|
||||||
|
} else if (error) {
|
||||||
|
content = <ErrorMessage />
|
||||||
|
} else {
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
<DiffView />
|
||||||
|
<ChangeList />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{fileTreeContainer
|
{fileTreeContainer
|
||||||
? createPortal(<HistoryFileTree />, fileTreeContainer)
|
? createPortal(<HistoryFileTree />, fileTreeContainer)
|
||||||
: null}
|
: null}
|
||||||
<div className="history-react">
|
<div className="history-react">{content}</div>
|
||||||
{loadingState === 'loadingInitial' ? (
|
|
||||||
<LoadingSpinner />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<DiffView />
|
|
||||||
<ChangeList />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ function useHistory() {
|
||||||
const [labels, setLabels] = useState<HistoryContextValue['labels']>(null)
|
const [labels, setLabels] = useState<HistoryContextValue['labels']>(null)
|
||||||
const [loadingState, setLoadingState] =
|
const [loadingState, setLoadingState] =
|
||||||
useState<HistoryContextValue['loadingState']>('loadingInitial')
|
useState<HistoryContextValue['loadingState']>('loadingInitial')
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState<HistoryContextValue['error']>(null)
|
||||||
|
|
||||||
const fetchNextBatchOfUpdates = useCallback(() => {
|
const fetchNextBatchOfUpdates = useCallback(() => {
|
||||||
const loadUpdates = (updatesData: Update[]) => {
|
const loadUpdates = (updatesData: Update[]) => {
|
||||||
|
@ -197,28 +197,32 @@ function useHistory() {
|
||||||
}
|
}
|
||||||
const { fromV, toV } = updateRange
|
const { fromV, toV } = updateRange
|
||||||
|
|
||||||
diffFiles(projectId, fromV, toV).then(({ diff: files }) => {
|
diffFiles(projectId, fromV, toV)
|
||||||
const selectedFile = autoSelectFile(
|
.then(({ diff: files }) => {
|
||||||
files,
|
const selectedFile = autoSelectFile(
|
||||||
updateRange.toV,
|
files,
|
||||||
comparing,
|
updateRange.toV,
|
||||||
updates
|
comparing,
|
||||||
)
|
updates
|
||||||
const newFiles = files.map(file => {
|
)
|
||||||
if (isFileRenamed(file) && file.newPathname) {
|
const newFiles = files.map(file => {
|
||||||
return renamePathnameKey(file)
|
if (isFileRenamed(file) && file.newPathname) {
|
||||||
}
|
return renamePathnameKey(file)
|
||||||
|
}
|
||||||
|
|
||||||
return file
|
return file
|
||||||
|
})
|
||||||
|
setSelection({
|
||||||
|
updateRange,
|
||||||
|
comparing,
|
||||||
|
files: newFiles,
|
||||||
|
selectedFile,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
setSelection({
|
.catch(error => {
|
||||||
updateRange,
|
setError(error)
|
||||||
comparing,
|
|
||||||
files: newFiles,
|
|
||||||
selectedFile,
|
|
||||||
})
|
})
|
||||||
})
|
}, [updateRange, projectId, updates, comparing, setError])
|
||||||
}, [updateRange, projectId, updates, comparing])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Set update range if there isn't one and updates have loaded
|
// Set update range if there isn't one and updates have loaded
|
||||||
|
@ -239,6 +243,7 @@ function useHistory() {
|
||||||
const value = useMemo<HistoryContextValue>(
|
const value = useMemo<HistoryContextValue>(
|
||||||
() => ({
|
() => ({
|
||||||
error,
|
error,
|
||||||
|
setError,
|
||||||
loadingState,
|
loadingState,
|
||||||
setLoadingState,
|
setLoadingState,
|
||||||
updatesInfo,
|
updatesInfo,
|
||||||
|
@ -255,6 +260,7 @@ function useHistory() {
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
error,
|
error,
|
||||||
|
setError,
|
||||||
loadingState,
|
loadingState,
|
||||||
setLoadingState,
|
setLoadingState,
|
||||||
updatesInfo,
|
updatesInfo,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type { HistoryContextValue } from '../types/history-context-value'
|
||||||
|
|
||||||
export function useRestoreDeletedFile() {
|
export function useRestoreDeletedFile() {
|
||||||
const { runAsync } = useAsync()
|
const { runAsync } = useAsync()
|
||||||
const { setLoadingState, projectId } = useHistoryContext()
|
const { setLoadingState, projectId, setError } = useHistoryContext()
|
||||||
const ide = useIdeContext()
|
const ide = useIdeContext()
|
||||||
const { setView } = useLayoutContext()
|
const { setView } = useLayoutContext()
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ export function useRestoreDeletedFile() {
|
||||||
|
|
||||||
setView('editor')
|
setView('editor')
|
||||||
})
|
})
|
||||||
|
.catch(error => setError(error))
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoadingState('ready')
|
setLoadingState('ready')
|
||||||
})
|
})
|
||||||
|
|
|
@ -27,6 +27,7 @@ export type HistoryContextValue = {
|
||||||
React.SetStateAction<HistoryContextValue['loadingState']>
|
React.SetStateAction<HistoryContextValue['loadingState']>
|
||||||
>
|
>
|
||||||
error: Nullable<unknown>
|
error: Nullable<unknown>
|
||||||
|
setError: React.Dispatch<React.SetStateAction<HistoryContextValue['error']>>
|
||||||
labels: Nullable<LoadedLabel[]>
|
labels: Nullable<LoadedLabel[]>
|
||||||
setLabels: React.Dispatch<React.SetStateAction<HistoryContextValue['labels']>>
|
setLabels: React.Dispatch<React.SetStateAction<HistoryContextValue['labels']>>
|
||||||
projectId: string
|
projectId: string
|
||||||
|
|
|
@ -485,3 +485,7 @@ history-root {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-error {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue