From d89b62e96561e7eee29ac22c99c2573a54d686ad Mon Sep 17 00:00:00 2001 From: Eric Mc Sween <5454374+emcsween@users.noreply.github.com> Date: Thu, 6 Jul 2023 07:19:04 -0400 Subject: [PATCH] Merge pull request #13671 from overleaf/ii-review-panel-migration-aggregate-change-entry [web] Create aggregate change entries GitOrigin-RevId: 685ac40739f3c39665d84bd402ada21e00db5146 --- .../web/frontend/extracted-translations.json | 2 + .../review-panel/current-file-container.tsx | 13 +- .../entries/aggregate-change-entry.tsx | 174 +++++++++++++++++- .../review-panel/entries/change-entry.tsx | 2 +- .../components/review-panel/overview-file.tsx | 10 +- .../review-panel/review-panel.spec.tsx | 18 +- services/web/types/review-panel/entry.ts | 60 +++--- 7 files changed, 244 insertions(+), 35 deletions(-) diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index bdc0d57913..14290768b6 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -42,6 +42,8 @@ "additional_licenses": "", "address_line_1": "", "address_second_line_optional": "", + "aggregate_changed": "", + "aggregate_to": "", "all_premium_features": "", "all_premium_features_including": "", "all_projects": "", 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 c2de44f5a7..73b4d51fbd 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 @@ -79,7 +79,18 @@ function CurrentFileContainer() { } if (entry.type === 'aggregate-change') { - return + return ( + + ) } if (entry.type === 'comment' && !loadingThreads) { diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/entries/aggregate-change-entry.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/entries/aggregate-change-entry.tsx index a8301f8415..9ddd9451b9 100644 --- a/services/web/frontend/js/features/source-editor/components/review-panel/entries/aggregate-change-entry.tsx +++ b/services/web/frontend/js/features/source-editor/components/review-panel/entries/aggregate-change-entry.tsx @@ -1,7 +1,177 @@ +import { useTranslation } from 'react-i18next' +import { useState } from 'react' import EntryContainer from './entry-container' +import EntryActions from './entry-actions' +import Icon from '../../../../../shared/components/icon' +import { useReviewPanelValueContext } from '../../../context/review-panel/review-panel-context' +import { formatTime } from '../../../../utils/format-date' +import classnames from 'classnames' +import { ReviewPanelAggregateChangeEntry } from '../../../../../../../types/review-panel/entry' +import { + ReviewPanelPermissions, + ReviewPanelUser, +} from '../../../../../../../types/review-panel/review-panel' +import { DocId } from '../../../../../../../types/project-settings' -function AggregateChangeEntry() { - return Aggregate change entry +type AggregateChangeEntryProps = { + docId: DocId + entry: ReviewPanelAggregateChangeEntry + permissions: ReviewPanelPermissions + user: ReviewPanelUser | undefined + contentLimit?: number + onMouseEnter?: () => void + onMouseLeave?: () => void + onIndicatorClick?: () => void +} + +function AggregateChangeEntry({ + docId, + entry, + permissions, + user, + contentLimit = 17, + onMouseEnter, + onMouseLeave, + onIndicatorClick, +}: AggregateChangeEntryProps) { + const { t } = useTranslation() + const { acceptChanges, rejectChanges, handleLayoutChange, gotoEntry } = + useReviewPanelValueContext() + const [isDeletionCollapsed, setIsDeletionCollapsed] = useState(true) + const [isInsertionCollapsed, setIsInsertionCollapsed] = useState(true) + + const replacedContent = entry.metadata.replaced_content + const content = entry.content + const deletionNeedsCollapsing = replacedContent.length > contentLimit + const insertionNeedsCollapsing = content.length > contentLimit + + const deletionContent = isDeletionCollapsed + ? replacedContent.substring(0, contentLimit) + : replacedContent + + const insertionContent = isInsertionCollapsed + ? content.substring(0, contentLimit) + : content + + const handleEntryClick = (e: React.MouseEvent) => { + const target = e.target as Element + + for (const selector of [ + '.rp-entry', + '.rp-entry-description', + '.rp-entry-body', + '.rp-entry-action-icon i', + ]) { + if (target.matches(selector)) { + gotoEntry(docId, entry.offset) + break + } + } + } + + const handleDeletionToggleCollapse = () => { + setIsDeletionCollapsed(value => !value) + handleLayoutChange() + } + + const handleInsertionToggleCollapse = () => { + setIsInsertionCollapsed(value => !value) + handleLayoutChange() + } + + return ( + +
+ {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} +
+ +
+
+
+
+ +
+
+
+ {t('aggregate_changed')}  + {deletionContent} + {deletionNeedsCollapsing && ( + + )} +  {t('aggregate_to')}  + {insertionContent} + {insertionNeedsCollapsing && ( + + )} +
+
+ {formatTime(entry.metadata.ts, 'MMM d, y h:mm a')} +  •  + {user && ( + + {user.name ?? t('anonymous')} + + )} +
+
+
+ {permissions.write && ( + + rejectChanges(entry.entry_ids)}> + {t('reject')} + + acceptChanges(entry.entry_ids)}> + {t('accept')} + + + )} +
+ + ) } export default AggregateChangeEntry diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/entries/change-entry.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/entries/change-entry.tsx index 25f8f993d6..40b080efa9 100644 --- a/services/web/frontend/js/features/source-editor/components/review-panel/entries/change-entry.tsx +++ b/services/web/frontend/js/features/source-editor/components/review-panel/entries/change-entry.tsx @@ -40,7 +40,7 @@ function ChangeEntry({ const { t } = useTranslation() const { acceptChanges, rejectChanges, handleLayoutChange, gotoEntry } = useReviewPanelValueContext() - const [isCollapsed, setIsCollapsed] = useState(false) + const [isCollapsed, setIsCollapsed] = useState(true) const content = isCollapsed ? entry.content.substring(0, contentLimit) diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/overview-file.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/overview-file.tsx index 08533f09ad..23f96db15b 100644 --- a/services/web/frontend/js/features/source-editor/components/review-panel/overview-file.tsx +++ b/services/web/frontend/js/features/source-editor/components/review-panel/overview-file.tsx @@ -89,7 +89,15 @@ function OverviewFile({ docId, docPath }: OverviewFileProps) { } if (entry.type === 'aggregate-change') { - return + return ( + + ) } if (entry.type === 'comment') { diff --git a/services/web/test/frontend/features/review-panel/review-panel.spec.tsx b/services/web/test/frontend/features/review-panel/review-panel.spec.tsx index 1c822086bc..571c4c0911 100644 --- a/services/web/test/frontend/features/review-panel/review-panel.spec.tsx +++ b/services/web/test/frontend/features/review-panel/review-panel.spec.tsx @@ -195,10 +195,16 @@ describe('', function () { describe('change entries', function () { // eslint-disable-next-line mocha/no-skipped-tests - it.skip('renders inserted entries', function () {}) + it.skip('renders inserted entries in current file mode', function () {}) // eslint-disable-next-line mocha/no-skipped-tests - it.skip('renders deleted entries', function () {}) + it.skip('renders deleted entries in current file mode', function () {}) + + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('renders inserted entries in overview mode', function () {}) + + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('renders deleted entries in overview mode', function () {}) // eslint-disable-next-line mocha/no-skipped-tests it.skip('accepts change', function () {}) @@ -207,6 +213,14 @@ describe('', function () { it.skip('rejects change', function () {}) }) + describe('aggregate change entries', function () { + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('renders changed entries in current file mode', function () {}) + + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('renders changed entries in overview mode', function () {}) + }) + describe('overview mode', function () { // eslint-disable-next-line mocha/no-skipped-tests it.skip('shows list of files changed', function () {}) diff --git a/services/web/types/review-panel/entry.ts b/services/web/types/review-panel/entry.ts index 2f2a5fb45a..f046d9adeb 100644 --- a/services/web/types/review-panel/entry.ts +++ b/services/web/types/review-panel/entry.ts @@ -7,46 +7,50 @@ interface ReviewPanelEntryScreenPos { } interface ReviewPanelBaseEntry { - visible: boolean + focused: boolean offset: number + screenPos: ReviewPanelEntryScreenPos + visible: boolean +} + +interface ReviewPanelInsertOrDeleteEntry { + content: string + entry_ids: ThreadId[] + metadata: { + ts: Date + user_id: UserId + } +} + +export interface ReviewPanelInsertEntry + extends ReviewPanelBaseEntry, + ReviewPanelInsertOrDeleteEntry { + type: 'insert' +} + +export interface ReviewPanelDeleteEntry + extends ReviewPanelBaseEntry, + ReviewPanelInsertOrDeleteEntry { + type: 'delete' } export interface ReviewPanelCommentEntry extends ReviewPanelBaseEntry { type: 'comment' content: string entry_ids: ThreadId[] - focused: boolean - screenPos: ReviewPanelEntryScreenPos thread_id: ThreadId replyContent?: string // angular specific } -export interface ReviewPanelInsertEntry extends ReviewPanelBaseEntry { - type: 'insert' - content: string - entry_ids: ThreadId[] - metadata: { - ts: Date - user_id: UserId - } - screenPos: ReviewPanelEntryScreenPos - focused?: boolean -} - -export interface ReviewPanelDeleteEntry extends ReviewPanelBaseEntry { - type: 'delete' - content: string - entry_ids: ThreadId[] - metadata: { - ts: Date - user_id: UserId - } - screenPos: ReviewPanelEntryScreenPos - focused?: boolean -} - -interface ReviewPanelAggregateChangeEntry extends ReviewPanelBaseEntry { +export interface ReviewPanelAggregateChangeEntry extends ReviewPanelBaseEntry { type: 'aggregate-change' + content: string + entry_ids: ThreadId[] + metadata: { + replaced_content: string + ts: Date + user_id: UserId + } } interface ReviewPanelAddCommentEntry extends ReviewPanelBaseEntry {