From 4a8163b3f3e3d83938fbdf2e067068873427e954 Mon Sep 17 00:00:00 2001 From: David <33458145+davidmcpowell@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:28:40 +0000 Subject: [PATCH] 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 GitOrigin-RevId: 6aab5c662287a02750d81b3960bbdd9c98b325db --- .../contexts/file-tree-actionable.tsx | 79 +++++++++++++------ .../ide-react/context/metadata-context.tsx | 5 +- .../ide-react/context/permissions-context.tsx | 14 ++-- .../features/ide-react/types/permissions.ts | 3 +- .../components/review-mode-switcher.tsx | 21 ++--- .../hooks/use-codemirror-scope.ts | 11 +-- ...codemirror-editor-visual-readonly.spec.tsx | 4 +- .../source-editor/helpers/mock-scope.ts | 1 + 8 files changed, 86 insertions(+), 52 deletions(-) diff --git a/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx b/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx index 115bda9dc2..01bd35359d 100644 --- a/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx +++ b/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx @@ -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 ( diff --git a/services/web/frontend/js/features/ide-react/context/metadata-context.tsx b/services/web/frontend/js/features/ide-react/context/metadata-context.tsx index b9d939ad48..c1e9d14502 100644 --- a/services/web/frontend/js/features/ide-react/context/metadata-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/metadata-context.tsx @@ -185,7 +185,10 @@ export const MetadataProvider: FC = ({ children }) => { ) } window.setTimeout(() => { - if (permissionsRef.current.write) { + if ( + permissionsRef.current.write || + permissionsRef.current.trackedWrite + ) { loadProjectMetaFromServer() } }, 200) diff --git a/services/web/frontend/js/features/ide-react/context/permissions-context.tsx b/services/web/frontend/js/features/ide-react/context/permissions-context.tsx index b58d859791..1dfd5ea0db 100644 --- a/services/web/frontend/js/features/ide-react/context/permissions-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/permissions-context.tsx @@ -17,27 +17,31 @@ export const PermissionsContext = createContext( const permissionsMap: DeepReadonly> = { 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, }, } diff --git a/services/web/frontend/js/features/ide-react/types/permissions.ts b/services/web/frontend/js/features/ide-react/types/permissions.ts index 0234c21e65..435a7f5328 100644 --- a/services/web/frontend/js/features/ide-react/types/permissions.ts +++ b/services/web/frontend/js/features/ide-react/types/permissions.ts @@ -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' diff --git a/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx b/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx index d1956d8d2f..c386655384 100644 --- a/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx +++ b/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx @@ -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 (
@@ -57,24 +52,24 @@ function ReviewModeSwitcher() { /> { saveTrackChangesForCurrentUser(false) }} description={t('can_edit_content')} leadingIcon="edit" - active={enableEditing && mode === 'editing'} + active={write && mode === 'editing'} > {t('editing')} { saveTrackChangesForCurrentUser(true) }} description={t('can_add_tracked_changes_and_comments')} leadingIcon="rate_review" - active={enableReviewing && mode === 'reviewing'} + active={trackedWrite && mode === 'reviewing'} > {t('reviewing')} diff --git a/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts b/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts index 0c62e60f4e..eef644a813 100644 --- a/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts +++ b/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts @@ -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') // set up scope listeners @@ -111,7 +112,7 @@ function useCodeMirrorScope(view: EditorView) { let [spellCheckLanguage] = useScopeValue('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 diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-readonly.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-readonly.spec.tsx index b571e594e9..6072b262e4 100644 --- a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-readonly.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-readonly.spec.tsx @@ -27,9 +27,10 @@ const PermissionsProvider: FC = ({ children }) => ( {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( diff --git a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts index d6dd0eb5b4..3077fd6563 100644 --- a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts +++ b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts @@ -62,6 +62,7 @@ export const mockScope = (content?: string) => { }, permissions: { comment: true, + trackedWrite: true, write: true, }, reviewPanel: {