mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 14:13:43 +01:00
Add new trackedWrite frontend permission for review panel (#22486)
* Add new trackedWrite frontend permission for review panel * Fix test lint * Remove comment * Fix codemirror tests * hide file actions for reviewer --------- Co-authored-by: Domagoj Kriskovic <dom.kriskovic@overleaf.com> GitOrigin-RevId: 6aab5c662287a02750d81b3960bbdd9c98b325db
This commit is contained in:
committed by
Copybot
co-authored by
Domagoj Kriskovic
parent
4607e8fe06
commit
43d291a9ac
@@ -33,6 +33,7 @@ import {
|
||||
} from '../errors'
|
||||
import { Folder } from '../../../../../types/folder'
|
||||
import { useReferencesContext } from '@/features/ide-react/context/references-context'
|
||||
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
||||
|
||||
type DroppedFile = File & {
|
||||
relativePath?: string
|
||||
@@ -220,6 +221,7 @@ export const FileTreeActionableProvider: FC = ({ children }) => {
|
||||
const { _id: projectId } = useProjectContext()
|
||||
const { fileTreeReadOnly } = useFileTreeData()
|
||||
const { indexAllReferences } = useReferencesContext()
|
||||
const { write } = usePermissionsContext()
|
||||
|
||||
const [state, dispatch] = useReducer(
|
||||
fileTreeReadOnly
|
||||
@@ -501,32 +503,57 @@ export const FileTreeActionableProvider: FC = ({ children }) => {
|
||||
}
|
||||
}, [fileTreeData, projectId, selectedEntityIds])
|
||||
|
||||
// TODO: wrap in useMemo
|
||||
const value = {
|
||||
canDelete: selectedEntityIds.size > 0 && !isRootFolderSelected,
|
||||
canRename: selectedEntityIds.size === 1 && !isRootFolderSelected,
|
||||
canCreate: selectedEntityIds.size < 2,
|
||||
...state,
|
||||
parentFolderId,
|
||||
selectedFileName,
|
||||
isDuplicate,
|
||||
startRenaming,
|
||||
finishRenaming,
|
||||
startDeleting,
|
||||
finishDeleting,
|
||||
finishMoving,
|
||||
startCreatingFile,
|
||||
startCreatingFolder,
|
||||
finishCreatingFolder,
|
||||
startCreatingDocOrFile,
|
||||
startUploadingDocOrFile,
|
||||
finishCreatingDoc,
|
||||
finishCreatingLinkedFile,
|
||||
cancel,
|
||||
droppedFiles,
|
||||
setDroppedFiles,
|
||||
downloadPath,
|
||||
}
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
canDelete: write && selectedEntityIds.size > 0 && !isRootFolderSelected,
|
||||
canRename: write && selectedEntityIds.size === 1 && !isRootFolderSelected,
|
||||
canCreate: write && selectedEntityIds.size < 2,
|
||||
...state,
|
||||
parentFolderId,
|
||||
selectedFileName,
|
||||
isDuplicate,
|
||||
startRenaming,
|
||||
finishRenaming,
|
||||
startDeleting,
|
||||
finishDeleting,
|
||||
finishMoving,
|
||||
startCreatingFile,
|
||||
startCreatingFolder,
|
||||
finishCreatingFolder,
|
||||
startCreatingDocOrFile,
|
||||
startUploadingDocOrFile,
|
||||
finishCreatingDoc,
|
||||
finishCreatingLinkedFile,
|
||||
cancel,
|
||||
droppedFiles,
|
||||
setDroppedFiles,
|
||||
downloadPath,
|
||||
}),
|
||||
[
|
||||
cancel,
|
||||
downloadPath,
|
||||
droppedFiles,
|
||||
finishCreatingDoc,
|
||||
finishCreatingFolder,
|
||||
finishCreatingLinkedFile,
|
||||
finishDeleting,
|
||||
finishMoving,
|
||||
finishRenaming,
|
||||
isDuplicate,
|
||||
isRootFolderSelected,
|
||||
parentFolderId,
|
||||
selectedEntityIds.size,
|
||||
selectedFileName,
|
||||
startCreatingDocOrFile,
|
||||
startCreatingFile,
|
||||
startCreatingFolder,
|
||||
startDeleting,
|
||||
startRenaming,
|
||||
startUploadingDocOrFile,
|
||||
state,
|
||||
write,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<FileTreeActionableContext.Provider value={value}>
|
||||
|
||||
@@ -185,7 +185,10 @@ export const MetadataProvider: FC = ({ children }) => {
|
||||
)
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
if (permissionsRef.current.write) {
|
||||
if (
|
||||
permissionsRef.current.write ||
|
||||
permissionsRef.current.trackedWrite
|
||||
) {
|
||||
loadProjectMetaFromServer()
|
||||
}
|
||||
}, 200)
|
||||
|
||||
@@ -17,27 +17,31 @@ export const PermissionsContext = createContext<Permissions | undefined>(
|
||||
const permissionsMap: DeepReadonly<Record<PermissionsLevel, Permissions>> = {
|
||||
readOnly: {
|
||||
read: true,
|
||||
comment: true,
|
||||
trackedWrite: false,
|
||||
write: false,
|
||||
admin: false,
|
||||
comment: true,
|
||||
},
|
||||
review: {
|
||||
read: true,
|
||||
write: true,
|
||||
admin: false,
|
||||
comment: true,
|
||||
trackedWrite: true,
|
||||
write: false,
|
||||
admin: false,
|
||||
},
|
||||
readAndWrite: {
|
||||
read: true,
|
||||
comment: true,
|
||||
trackedWrite: true,
|
||||
write: true,
|
||||
admin: false,
|
||||
comment: true,
|
||||
},
|
||||
owner: {
|
||||
read: true,
|
||||
comment: true,
|
||||
trackedWrite: true,
|
||||
write: true,
|
||||
admin: true,
|
||||
comment: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
export type Permissions = {
|
||||
read: boolean
|
||||
comment: boolean
|
||||
trackedWrite: boolean
|
||||
write: boolean
|
||||
admin: boolean
|
||||
comment: boolean
|
||||
}
|
||||
|
||||
export type PermissionsLevel = 'owner' | 'readAndWrite' | 'review' | 'readOnly'
|
||||
|
||||
+8
-13
@@ -13,7 +13,6 @@ import {
|
||||
} from '../context/track-changes-state-context'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
||||
|
||||
type Mode = 'viewing' | 'reviewing' | 'editing'
|
||||
@@ -24,11 +23,11 @@ const useCurrentMode = (): Mode => {
|
||||
const trackChangesForCurrentUser =
|
||||
trackChanges?.onForEveryone ||
|
||||
(user && user.id && trackChanges?.onForMembers[user.id])
|
||||
const { write } = usePermissionsContext()
|
||||
const { write, trackedWrite } = usePermissionsContext()
|
||||
|
||||
if (write && !trackChangesForCurrentUser) {
|
||||
return 'editing'
|
||||
} else if (write) {
|
||||
} else if (trackedWrite) {
|
||||
return 'reviewing'
|
||||
}
|
||||
|
||||
@@ -41,12 +40,8 @@ function ReviewModeSwitcher() {
|
||||
useTrackChangesStateActionsContext()
|
||||
const mode = useCurrentMode()
|
||||
|
||||
const { permissionsLevel } = useEditorContext()
|
||||
|
||||
const enableEditing =
|
||||
permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite'
|
||||
const enableReviewing = enableEditing || permissionsLevel === 'review'
|
||||
const showViewOption = !enableReviewing
|
||||
const { write, trackedWrite } = usePermissionsContext()
|
||||
const showViewOption = !trackedWrite
|
||||
|
||||
return (
|
||||
<div className="review-mode-switcher-container">
|
||||
@@ -57,24 +52,24 @@ function ReviewModeSwitcher() {
|
||||
/>
|
||||
<DropdownMenu flip={false}>
|
||||
<OLDropdownMenuItem
|
||||
disabled={!enableEditing}
|
||||
disabled={!write}
|
||||
onClick={() => {
|
||||
saveTrackChangesForCurrentUser(false)
|
||||
}}
|
||||
description={t('can_edit_content')}
|
||||
leadingIcon="edit"
|
||||
active={enableEditing && mode === 'editing'}
|
||||
active={write && mode === 'editing'}
|
||||
>
|
||||
{t('editing')}
|
||||
</OLDropdownMenuItem>
|
||||
<OLDropdownMenuItem
|
||||
disabled={!enableReviewing}
|
||||
disabled={!trackedWrite}
|
||||
onClick={() => {
|
||||
saveTrackChangesForCurrentUser(true)
|
||||
}}
|
||||
description={t('can_add_tracked_changes_and_comments')}
|
||||
leadingIcon="rate_review"
|
||||
active={enableReviewing && mode === 'reviewing'}
|
||||
active={trackedWrite && mode === 'reviewing'}
|
||||
>
|
||||
{t('reviewing')}
|
||||
</OLDropdownMenuItem>
|
||||
|
||||
@@ -67,11 +67,12 @@ import { updateRanges } from '@/features/source-editor/extensions/ranges'
|
||||
import { useThreadsContext } from '@/features/review-panel-new/context/threads-context'
|
||||
import { useHunspell } from '@/features/source-editor/hooks/use-hunspell'
|
||||
import { isBootstrap5 } from '@/features/utils/bootstrap-5'
|
||||
import { Permissions } from '@/features/ide-react/types/permissions'
|
||||
|
||||
function useCodeMirrorScope(view: EditorView) {
|
||||
const { fileTreeData } = useFileTreeData()
|
||||
|
||||
const [permissions] = useScopeValue<{ write: boolean }>('permissions')
|
||||
const [permissions] = useScopeValue<Permissions>('permissions')
|
||||
|
||||
// set up scope listeners
|
||||
|
||||
@@ -111,7 +112,7 @@ function useCodeMirrorScope(view: EditorView) {
|
||||
|
||||
let [spellCheckLanguage] = useScopeValue<string>('project.spellCheckLanguage')
|
||||
// spell check is off when read-only
|
||||
if (!permissions.write) {
|
||||
if (!permissions.write && !permissions.trackedWrite) {
|
||||
spellCheckLanguage = ''
|
||||
}
|
||||
|
||||
@@ -288,7 +289,7 @@ function useCodeMirrorScope(view: EditorView) {
|
||||
}
|
||||
}, [view, fileTreeData])
|
||||
|
||||
const editableRef = useRef(permissions.write)
|
||||
const editableRef = useRef(permissions.write || permissions.trackedWrite)
|
||||
|
||||
const { previewByPath } = useFileTreePathContext()
|
||||
|
||||
@@ -409,11 +410,11 @@ function useCodeMirrorScope(view: EditorView) {
|
||||
}, [view, previewByPath])
|
||||
|
||||
useEffect(() => {
|
||||
editableRef.current = permissions.write
|
||||
editableRef.current = permissions.write || permissions.trackedWrite
|
||||
window.setTimeout(() => {
|
||||
view.dispatch(setEditable(editableRef.current)) // the editor needs to be locked when there's a problem saving data
|
||||
})
|
||||
}, [view, permissions.write])
|
||||
}, [view, permissions.write, permissions.trackedWrite])
|
||||
|
||||
useEffect(() => {
|
||||
phrasesRef.current = phrases
|
||||
|
||||
+3
-1
@@ -27,9 +27,10 @@ const PermissionsProvider: FC = ({ children }) => (
|
||||
<PermissionsContext.Provider
|
||||
value={{
|
||||
read: true,
|
||||
comment: true,
|
||||
trackedWrite: false,
|
||||
write: false,
|
||||
admin: false,
|
||||
comment: true,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@@ -39,6 +40,7 @@ const PermissionsProvider: FC = ({ children }) => (
|
||||
const mountEditor = (content: string) => {
|
||||
const scope = mockScope(content)
|
||||
scope.permissions.write = false
|
||||
scope.permissions.trackedWrite = false
|
||||
scope.editor.showVisual = true
|
||||
|
||||
cy.mount(
|
||||
|
||||
@@ -62,6 +62,7 @@ export const mockScope = (content?: string) => {
|
||||
},
|
||||
permissions: {
|
||||
comment: true,
|
||||
trackedWrite: true,
|
||||
write: true,
|
||||
},
|
||||
reviewPanel: {
|
||||
|
||||
Reference in New Issue
Block a user