From 123da516996ed1902c80ea04bb52cb831cff2560 Mon Sep 17 00:00:00 2001 From: David <33458145+davidmcpowell@users.noreply.github.com> Date: Thu, 11 Jul 2024 14:22:28 +0100 Subject: [PATCH] Merge pull request #19346 from overleaf/dp-review-panel-empty-state Add empty state to review panel GitOrigin-RevId: 47d7b676e9868942567fc02db234b0827ac86ba3 --- .../src/Features/Project/ProjectController.js | 1 + .../web/frontend/extracted-translations.json | 2 + .../review-panel/current-file-container.tsx | 120 +++--------------- .../components/review-panel/empty-state.tsx | 22 ++++ .../components/review-panel/entry.tsx | 109 ++++++++++++++++ .../review-panel/overview-container.tsx | 20 ++- .../stylesheets/app/editor/review-panel.less | 42 ++++++ services/web/locales/en.json | 2 + 8 files changed, 212 insertions(+), 106 deletions(-) create mode 100644 services/web/frontend/js/features/source-editor/components/review-panel/empty-state.tsx create mode 100644 services/web/frontend/js/features/source-editor/components/review-panel/entry.tsx diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index e37e1a9eac..49876778a0 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -335,6 +335,7 @@ const _ProjectController = { 'pdfjs-40', 'personal-access-token', 'revert-file', + 'review-panel-redesign', 'track-pdf-download', !anonymous && 'writefull-oauth-promotion', 'ieee-stylesheet', diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 4a1d50664a..3d505b16fc 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -849,12 +849,14 @@ "no_borders": "", "no_caption": "", "no_comments": "", + "no_comments_or_suggestions": "", "no_existing_password": "", "no_folder": "", "no_image_files_found": "", "no_members": "", "no_messages": "", "no_new_commits_in_github": "", + "no_one_has_commented_or_left_any_suggestions_yet": "", "no_other_projects_found": "", "no_pdf_error_explanation": "", "no_pdf_error_reason_no_content": "", diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/current-file-container.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/current-file-container.tsx index ea30787f27..8d34c2aa41 100644 --- a/services/web/frontend/js/features/source-editor/components/review-panel/current-file-container.tsx +++ b/services/web/frontend/js/features/source-editor/components/review-panel/current-file-container.tsx @@ -3,37 +3,18 @@ import Container from './container' import Toolbar from './toolbar/toolbar' import Nav from './nav' import Toggler from './toggler' -import ChangeEntry from './entries/change-entry' -import AggregateChangeEntry from './entries/aggregate-change-entry' -import CommentEntry from './entries/comment-entry' -import AddCommentEntry from './entries/add-comment-entry' -import BulkActionsEntry from './entries/bulk-actions-entry/bulk-actions-entry' import PositionedEntries from './positioned-entries' import { useReviewPanelValueContext } from '../../context/review-panel/review-panel-context' -import { useEditorContext } from '../../../../shared/context/editor-context' import useCodeMirrorContentHeight from '../../hooks/use-codemirror-content-height' import { ReviewPanelEntry } from '../../../../../../types/review-panel/entry' -import { - ReviewPanelDocEntries, - ThreadId, -} from '../../../../../../types/review-panel/review-panel' - -const isEntryAThreadId = ( - entry: keyof ReviewPanelDocEntries -): entry is ThreadId => entry !== 'add-comment' && entry !== 'bulk-actions' +import { ReviewPanelDocEntries } from '../../../../../../types/review-panel/review-panel' +import Entry from './entry' +import EmptyState from './empty-state' +import { useFeatureFlag } from '@/shared/context/split-test-context' function CurrentFileContainer() { - const { - commentThreads, - entries, - openDocId, - permissions, - loadingThreads, - users, - nVisibleSelectedChanges: nChanges, - } = useReviewPanelValueContext() + const { entries, openDocId } = useReviewPanelValueContext() const contentHeight = useCodeMirrorContentHeight() - const { isRestrictedTokenMember } = useEditorContext() const currentDocEntries = openDocId && openDocId in entries ? entries[openDocId] : undefined @@ -44,10 +25,19 @@ function CurrentFileContainer() { > }, [currentDocEntries]) + const enableEmptyState = useFeatureFlag('review-panel-redesign') + + const showEmptyState = + enableEmptyState && + objectEntries.filter( + ([key]) => key !== 'add-comment' && key !== 'bulk-actions' + ).length === 0 + return (
+ {showEmptyState && }
@@ -63,87 +53,7 @@ function CurrentFileContainer() { > {openDocId && objectEntries.map(([id, entry]) => { - if (!entry.visible) { - return null - } - - if ( - isEntryAThreadId(id) && - (entry.type === 'insert' || entry.type === 'delete') - ) { - return ( - - ) - } - - if (isEntryAThreadId(id) && entry.type === 'aggregate-change') { - return ( - - ) - } - - if ( - isEntryAThreadId(id) && - entry.type === 'comment' && - !loadingThreads - ) { - return ( - - ) - } - - if ( - entry.type === 'add-comment' && - permissions.comment && - !isRestrictedTokenMember - ) { - return - } - - if (entry.type === 'bulk-actions' && permissions.write) { - return ( - - ) - } - - return null + return })} diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/empty-state.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/empty-state.tsx new file mode 100644 index 0000000000..73c093605b --- /dev/null +++ b/services/web/frontend/js/features/source-editor/components/review-panel/empty-state.tsx @@ -0,0 +1,22 @@ +import MaterialIcon from '@/shared/components/material-icon' +import { useTranslation } from 'react-i18next' + +function EmptyState() { + const { t } = useTranslation() + + return ( +
+
+
+ +
+

+ {t('no_comments_or_suggestions')} +

+

{t('no_one_has_commented_or_left_any_suggestions_yet')}

+
+
+ ) +} + +export default EmptyState diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/entry.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/entry.tsx new file mode 100644 index 0000000000..1e68ca4c77 --- /dev/null +++ b/services/web/frontend/js/features/source-editor/components/review-panel/entry.tsx @@ -0,0 +1,109 @@ +import { memo } from 'react' +import ChangeEntry from './entries/change-entry' +import AggregateChangeEntry from './entries/aggregate-change-entry' +import CommentEntry from './entries/comment-entry' +import AddCommentEntry from './entries/add-comment-entry' +import BulkActionsEntry from './entries/bulk-actions-entry/bulk-actions-entry' +import { + ReviewPanelDocEntries, + ThreadId, +} from '../../../../../../types/review-panel/review-panel' +import { useReviewPanelValueContext } from '../../context/review-panel/review-panel-context' +import { useEditorContext } from '../../../../shared/context/editor-context' + +type Props = { + entry: ReviewPanelDocEntries[keyof ReviewPanelDocEntries] + id: ThreadId | 'add-comment' | 'bulk-actions' +} + +const isEntryAThreadId = ( + entry: keyof ReviewPanelDocEntries +): entry is ThreadId => entry !== 'add-comment' && entry !== 'bulk-actions' + +function Entry({ entry, id }: Props) { + const { + commentThreads, + openDocId, + permissions, + loadingThreads, + users, + nVisibleSelectedChanges: nChanges, + } = useReviewPanelValueContext() + const { isRestrictedTokenMember } = useEditorContext() + + if (!entry.visible || !openDocId) { + return null + } + + if ( + isEntryAThreadId(id) && + (entry.type === 'insert' || entry.type === 'delete') + ) { + return ( + + ) + } + + if (isEntryAThreadId(id) && entry.type === 'aggregate-change') { + return ( + + ) + } + + if (isEntryAThreadId(id) && entry.type === 'comment' && !loadingThreads) { + return ( + + ) + } + + if ( + entry.type === 'add-comment' && + permissions.comment && + !isRestrictedTokenMember + ) { + return + } + + if (entry.type === 'bulk-actions' && permissions.write) { + return ( + + ) + } + + return null +} + +export default memo(Entry) diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/overview-container.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/overview-container.tsx index d45d2eb8e8..5a20903e55 100644 --- a/services/web/frontend/js/features/source-editor/components/review-panel/overview-container.tsx +++ b/services/web/frontend/js/features/source-editor/components/review-panel/overview-container.tsx @@ -6,15 +6,33 @@ import Icon from '../../../../shared/components/icon' import OverviewFile from './overview-file' import { useReviewPanelValueContext } from '../../context/review-panel/review-panel-context' import { useFileTreeData } from '@/shared/context/file-tree-data-context' -import { memo } from 'react' +import { memo, useMemo } from 'react' +import EmptyState from './empty-state' +import { useFeatureFlag } from '@/shared/context/split-test-context' function OverviewContainer() { + const { entries } = useReviewPanelValueContext() + const { isOverviewLoading } = useReviewPanelValueContext() const { docs } = useFileTreeData() + const entryCount = useMemo(() => { + return docs + ?.map(doc => { + const docEntries = entries[doc.doc.id] ?? {} + return Object.keys(docEntries).filter( + key => key !== 'add-comment' && key !== 'bulk-actions' + ).length + }) + .reduce((acc, curr) => acc + curr, 0) + }, [docs, entries]) + + const enableEmptyState = useFeatureFlag('review-panel-redesign') + return ( + {enableEmptyState && entryCount === 0 && }