Merge pull request #15909 from overleaf/ii-ide-page-prototype-review-panel-refresh-ranges

[web] React ide page refresh ranges

GitOrigin-RevId: 7f79b8f63869ee39fef9a101e6dcc56c39af8df7
This commit is contained in:
ilkin-overleaf 2023-11-24 16:54:58 +02:00 committed by Copybot
parent a75b44a6fc
commit 81f5a1308e
4 changed files with 94 additions and 17 deletions

View file

@ -1,5 +1,6 @@
import { useState, useEffect, useMemo, useCallback, useRef } from 'react'
import { isEqual, cloneDeep } from 'lodash'
import usePersistedState from '@/shared/hooks/use-persisted-state'
import useScopeValue from '../../../../../shared/hooks/use-scope-value'
import useSocketListener from '@/features/ide-react/hooks/use-socket-listener'
import useAsync from '@/shared/hooks/use-async'
@ -130,9 +131,9 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
const [nVisibleSelectedChanges] = useScopeValue<
ReviewPanel.Value<'nVisibleSelectedChanges'>
>('reviewPanel.nVisibleSelectedChanges')
const [collapsed, setCollapsed] = useScopeValue<
const [collapsed, setCollapsed] = usePersistedState<
ReviewPanel.Value<'collapsed'>
>('reviewPanel.overview.docsCollapsedState')
>(`docs_collapsed_state:${projectId}`, {}, false, true)
const [commentThreads, setCommentThreads] = useState<
ReviewPanel.Value<'commentThreads'>
>({})
@ -273,7 +274,7 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
const getChangeTracker = useCallback(
(docId: DocId) => {
if (!rangesTrackers.current[docId]) {
rangesTrackers.current[docId] = new RangesTracker() as RangesTracker
rangesTrackers.current[docId] = new RangesTracker()
rangesTrackers.current[docId].resolvedThreadIds = {
...resolvedThreadIds,
}
@ -890,9 +891,47 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
[onThreadDeleted, projectId]
)
const [refreshResolvedCommentsDropdown] = useScopeValue<
ReviewPanel.UpdaterFn<'refreshResolvedCommentsDropdown'>
>('refreshResolvedCommentsDropdown')
const refreshRanges = useCallback(() => {
type Doc = {
id: DocId
ranges: {
comments?: unknown[]
changes?: unknown[]
}
}
return getJSON<Doc[]>(`/project/${projectId}/ranges`)
.then(docs => {
setCollapsed(prevState => {
const collapsed = { ...prevState }
docs.forEach(doc => {
if (collapsed[doc.id] == null) {
collapsed[doc.id] = false
}
})
return collapsed
})
docs.forEach(async doc => {
if (doc.id !== currentDocumentId) {
// this is kept up to date in real-time, don't overwrite
const rangesTracker = getChangeTracker(doc.id)
rangesTracker.comments = doc.ranges?.comments ?? []
rangesTracker.changes = doc.ranges?.changes ?? []
}
})
return Promise.all(docs.map(doc => updateEntries(doc.id)))
})
.catch(debugConsole.error)
}, [
currentDocumentId,
getChangeTracker,
projectId,
setCollapsed,
updateEntries,
])
const [acceptChanges] =
useScopeValue<ReviewPanel.UpdaterFn<'acceptChanges'>>('acceptChanges')
const [rejectChanges] =
@ -1056,7 +1095,7 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
submitNewComment,
deleteComment,
unresolveComment,
refreshResolvedCommentsDropdown,
refreshResolvedCommentsDropdown: refreshRanges,
deleteThread,
toggleTrackChangesForEveryone,
toggleTrackChangesForUser,
@ -1084,7 +1123,7 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
submitNewComment,
deleteComment,
unresolveComment,
refreshResolvedCommentsDropdown,
refreshRanges,
deleteThread,
toggleTrackChangesForEveryone,
toggleTrackChangesForUser,

View file

@ -1,7 +1,6 @@
import { ReactScopeValueStore } from '@/features/ide-react/scope-value-store/react-scope-value-store'
export default function populateReviewPanelScope(store: ReactScopeValueStore) {
store.set('reviewPanel.overview.docsCollapsedState', {})
store.set('reviewPanel.overview.loading', false)
store.set('reviewPanel.nVisibleSelectedChanges', 0)
store.set('permissions', {
@ -18,7 +17,6 @@ export default function populateReviewPanelScope(store: ReactScopeValueStore) {
store.set('deleteComment', () => {})
store.set('gotoEntry', () => {})
store.set('saveEdit', () => {})
store.set('refreshResolvedCommentsDropdown', async () => {})
store.set('acceptChanges', () => {})
store.set('rejectChanges', () => {})
store.set('bulkAcceptActions', () => {})

View file

@ -1,6 +1,7 @@
import {
CommentId,
ReviewPanelCommentThreads,
ReviewPanelDocEntries,
ReviewPanelEntries,
ReviewPanelUsers,
SubView,
@ -72,7 +73,9 @@ export interface ReviewPanelState {
) => void
unresolveComment: (threadId: ThreadId) => void
deleteThread: (docId: DocId, threadId: ThreadId) => void
refreshResolvedCommentsDropdown: () => Promise<void>
refreshResolvedCommentsDropdown: () => Promise<
void | ReviewPanelDocEntries[]
>
submitNewComment: (content: string) => Promise<void>
setEntryHover: React.Dispatch<React.SetStateAction<Value<'entryHover'>>>
setIsAddingComment: React.Dispatch<

View file

@ -7,14 +7,51 @@ import {
} from 'react'
import _ from 'lodash'
import localStorage from '../../infrastructure/local-storage'
import { debugConsole } from '@/utils/debugging'
const safeStringify = (value: unknown) => {
try {
return JSON.stringify(value)
} catch (e) {
debugConsole.error('double stringify exception', e)
return null
}
}
const safeParse = (value: string) => {
try {
return JSON.parse(value)
} catch (e) {
debugConsole.error('double parse exception', e)
return null
}
}
function usePersistedState<T = any>(
key: string,
defaultValue?: T,
listen = false
listen = false,
// The option below is for backward compatibility with Angular
// which sometimes stringifies the values twice
doubleStringifyAndParse = false
): [T, Dispatch<SetStateAction<T>>] {
const getItem = useCallback(
(key: string) => {
const item = localStorage.getItem(key)
return doubleStringifyAndParse ? safeParse(item) : item
},
[doubleStringifyAndParse]
)
const setItem = useCallback(
(key: string, value: unknown) => {
const val = doubleStringifyAndParse ? safeStringify(value) : value
localStorage.setItem(key, val)
},
[doubleStringifyAndParse]
)
const [value, setValue] = useState<T>(() => {
return localStorage.getItem(key) ?? defaultValue
return getItem(key) ?? defaultValue
})
const updateFunction = useCallback(
@ -27,13 +64,13 @@ function usePersistedState<T = any>(
if (actualNewValue === defaultValue) {
localStorage.removeItem(key)
} else {
localStorage.setItem(key, actualNewValue)
setItem(key, actualNewValue)
}
return actualNewValue
})
},
[key, defaultValue]
[key, defaultValue, setItem]
)
useEffect(() => {
@ -42,7 +79,7 @@ function usePersistedState<T = any>(
if (event.key === key) {
// note: this value is read via getItem rather than from event.newValue
// because getItem handles deserializing the JSON that's stored in localStorage.
setValue(localStorage.getItem(key) ?? defaultValue)
setValue(getItem(key) ?? defaultValue)
}
}
@ -52,7 +89,7 @@ function usePersistedState<T = any>(
window.removeEventListener('storage', listener)
}
}
}, [key, listen, defaultValue])
}, [defaultValue, key, listen, getItem])
return [value, updateFunction]
}