Merge pull request #13528 from overleaf/ii-review-panel-migration-current-file-view

[web] Create initial current file container

GitOrigin-RevId: 81635922bb10b97637af296d987f28d43cf61262
This commit is contained in:
ilkin-overleaf 2023-06-27 15:50:24 +03:00 committed by Copybot
parent 78314a0ee0
commit b61ee4c4d3
23 changed files with 268 additions and 130 deletions

View file

@ -1,7 +1,7 @@
import { Project, Sort } from '../../../../../types/project/dashboard/api'
import { SortingOrder } from '../../../../../types/sorting-order'
import { getOwnerName } from './project'
import { Compare } from '../../../../../types/array/sort'
import { Compare } from '../../../../../types/helpers/array/sort'
const order = (order: SortingOrder, projects: Project[]) => {
return order === 'asc' ? [...projects] : projects.reverse()

View file

@ -18,10 +18,12 @@ function Container({ children, classNames, ...rest }: ContainerProps) {
{...rest}
data-testid="review-panel"
>
<div className="review-panel-tools">
<Toolbar />
<Nav />
</div>
<Toggler />
<Toolbar />
{children}
<Nav />
</div>
)
}

View file

@ -1,50 +1,61 @@
import Container from './container'
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'
import { useReviewPanelValueContext } from '../../context/review-panel/review-panel-context'
import useCodeMirrorContentHeight from '../../hooks/use-codemirror-content-height'
function CurrentFileContainer() {
const { entries, openDocId, permissions } = useReviewPanelValueContext()
const contentHeight = useCodeMirrorContentHeight()
console.log('Review panel got content height', contentHeight)
const currentDocEntries =
openDocId && openDocId in entries ? entries[openDocId] : undefined
return (
<Container>
<div
id="review-panel-current-file"
role="tabpanel"
tabIndex={0}
aria-labelledby="review-panel-tab-current-file"
>
<div
id="review-panel-current-file"
role="tabpanel"
tabIndex={0}
aria-labelledby="review-panel-tab-current-file"
className="rp-entry-list-inner"
style={{ height: `${contentHeight}px` }}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Et malesuada fames
ac turpis egestas integer eget aliquet nibh. Et leo duis ut diam quam
nulla porttitor massa id. Risus quis varius quam quisque id diam vel
quam elementum. Nibh venenatis cras sed felis. Sit amet commodo nulla
facilisi nullam vehicula ipsum a arcu. Dui ut ornare lectus sit amet est
placerat in. Aliquam ultrices sagittis orci a. Leo a diam sollicitudin
tempor id eu nisl nunc mi. Quis ipsum suspendisse ultrices gravida
dictum fusce. Ut etiam sit amet nisl purus in mollis nunc sed. Rhoncus
est pellentesque elit ullamcorper dignissim cras. Faucibus turpis in eu
mi bibendum. Proin libero nunc consequat interdum. Ac placerat
vestibulum lectus mauris ultrices eros in cursus turpis. Ac felis donec
et odio. Nullam ac tortor vitae purus faucibus. Consectetur lorem donec
massa sapien faucibus et molestie. Praesent elementum facilisis leo vel
fringilla est ullamcorper eget nulla. Adipiscing vitae proin sagittis
nisl rhoncus mattis rhoncus urna. Cursus metus aliquam eleifend mi in
nulla posuere sollicitudin aliquam. Eget nullam non nisi est sit amet
facilisis magna. Donec adipiscing tristique risus nec feugiat in
fermentum posuere. Gravida rutrum quisque non tellus orci ac auctor
augue. Euismod in pellentesque massa placerat duis ultricies lacus.
Pellentesque diam volutpat commodo sed egestas. Tempus iaculis urna id
volutpat lacus laoreet. Lorem ipsum dolor sit amet consectetur.
Tincidunt id aliquet risus feugiat in ante metus. Risus ultricies
tristique nulla aliquet enim tortor at auctor urna. Purus in mollis nunc
sed. In ante metus dictum at. Magna eget est lorem ipsum dolor sit.
Fusce id velit ut tortor pretium viverra. Augue neque gravida in
fermentum et sollicitudin ac. Et malesuada fames ac turpis. Felis
bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Varius
vel pharetra vel turpis nunc eget.
{currentDocEntries &&
Object.entries(currentDocEntries).map(([id, entry]) => {
if (!entry.visible) {
return null
}
if (entry.type === 'insert' || entry.type === 'delete') {
return <ChangeEntry key={id} />
}
if (entry.type === 'aggregate-change') {
return <AggregateChangeEntry key={id} />
}
if (entry.type === 'comment') {
return <CommentEntry key={id} />
}
if (entry.type === 'add-comment' && permissions.comment) {
return <AddCommentEntry key={id} />
}
if (entry.type === 'bulk-actions') {
return <BulkActionsEntry key={id} />
}
return null
})}
</div>
</Container>
</div>
)
}

View file

@ -0,0 +1,7 @@
import EntryContainer from './entry-container'
function AddCommentEntry() {
return <EntryContainer>Add comment entry</EntryContainer>
}
export default AddCommentEntry

View file

@ -0,0 +1,7 @@
import EntryContainer from './entry-container'
function AggregateChangeEntry() {
return <EntryContainer>Aggregate change entry</EntryContainer>
}
export default AggregateChangeEntry

View file

@ -0,0 +1,7 @@
import EntryContainer from './entry-container'
function BulkActionsEntry() {
return <EntryContainer>Bulk actions entry</EntryContainer>
}
export default BulkActionsEntry

View file

@ -0,0 +1,7 @@
import EntryContainer from './entry-container'
function ChangeEntry() {
return <EntryContainer>Change entry</EntryContainer>
}
export default ChangeEntry

View file

@ -0,0 +1,7 @@
import EntryContainer from './entry-container'
function CommentEntry() {
return <EntryContainer>Comment entry</EntryContainer>
}
export default CommentEntry

View file

@ -0,0 +1,9 @@
type EntryContainerProps = {
children: React.ReactNode
}
function EntryContainer({ children }: EntryContainerProps) {
return <div className="rp-entry-wrapper">{children}</div>
}
export default EntryContainer

View file

@ -1,32 +1,41 @@
import Container from './container'
function OverviewContainer() {
return (
<Container>
<div
id="review-panel-overview"
role="tabpanel"
tabIndex={0}
aria-labelledby="review-panel-tab-overview"
>
Faucibus turpis in eu mi bibendum. Proin libero nunc consequat interdum.
Ac placerat vestibulum lectus mauris ultrices eros in cursus turpis. Ac
felis donec et odio. Nullam ac tortor vitae purus faucibus. Consectetur
lorem donec massa sapien faucibus et molestie. Praesent elementum
facilisis leo vel fringilla est ullamcorper eget nulla. Adipiscing vitae
proin sagittis nisl rhoncus mattis rhoncus urna. Cursus metus aliquam
eleifend mi in nulla posuere sollicitudin aliquam. Eget nullam non nisi
est sit amet facilisis magna. Donec adipiscing tristique risus nec
feugiat in fermentum posuere. Gravida rutrum quisque non tellus orci ac
auctor augue. Euismod in pellentesque massa placerat duis ultricies
lacus. Pellentesque diam volutpat commodo sed egestas. Tempus iaculis
urna id volutpat lacus laoreet. Lorem ipsum dolor sit amet consectetur.
Tincidunt id aliquet risus feugiat in ante metus. Risus ultricies
tristique nulla aliquet enim tortor at auctor urna. Purus in mollis nunc
sed. In ante metus dictum at. Magna eget est lorem ipsum dolor sit.
Fusce id velit ut tortor pretium viverra.
</div>
</Container>
<div
className="rp-entry-list"
id="review-panel-overview"
role="tabpanel"
tabIndex={0}
aria-labelledby="review-panel-tab-overview"
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Et malesuada fames ac
turpis egestas integer eget aliquet nibh. Et leo duis ut diam quam nulla
porttitor massa id. Risus quis varius quam quisque id diam vel quam
elementum. Nibh venenatis cras sed felis. Sit amet commodo nulla facilisi
nullam vehicula ipsum a arcu. Dui ut ornare lectus sit amet est placerat
in. Aliquam ultrices sagittis orci a. Leo a diam sollicitudin tempor id eu
nisl nunc mi. Quis ipsum suspendisse ultrices gravida dictum fusce. Ut
etiam sit amet nisl purus in mollis nunc sed. Rhoncus est pellentesque
elit ullamcorper dignissim cras. Faucibus turpis in eu mi bibendum. Proin
libero nunc consequat interdum. Ac placerat vestibulum lectus mauris
ultrices eros in cursus turpis. Ac felis donec et odio. Nullam ac tortor
vitae purus faucibus. Consectetur lorem donec massa sapien faucibus et
molestie. Praesent elementum facilisis leo vel fringilla est ullamcorper
eget nulla. Adipiscing vitae proin sagittis nisl rhoncus mattis rhoncus
urna. Cursus metus aliquam eleifend mi in nulla posuere sollicitudin
aliquam. Eget nullam non nisi est sit amet facilisis magna. Donec
adipiscing tristique risus nec feugiat in fermentum posuere. Gravida
rutrum quisque non tellus orci ac auctor augue. Euismod in pellentesque
massa placerat duis ultricies lacus. Pellentesque diam volutpat commodo
sed egestas. Tempus iaculis urna id volutpat lacus laoreet. Lorem ipsum
dolor sit amet consectetur. Tincidunt id aliquet risus feugiat in ante
metus. Risus ultricies tristique nulla aliquet enim tortor at auctor urna.
Purus in mollis nunc sed. In ante metus dictum at. Magna eget est lorem
ipsum dolor sit. Fusce id velit ut tortor pretium viverra. Augue neque
gravida in fermentum et sollicitudin ac. Et malesuada fames ac turpis.
Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices.
Varius vel pharetra vel turpis nunc eget.
</div>
)
}

View file

@ -1,11 +1,12 @@
import ReactDOM from 'react-dom'
import Container from './container'
import CurrentFileContainer from './current-file-container'
import OverviewContainer from './overview-container'
import { useCodeMirrorViewContext } from '../codemirror-editor'
import {
ReviewPanelProvider,
useReviewPanelValueContext,
} from '../../context/review-panel/review-panel-context'
import CurrentFileContainer from './current-file-container'
import OverviewContainer from './overview-container'
import { isCurrentFileView } from '../../utils/sub-view'
type ReviewPanelViewProps = {
@ -18,9 +19,13 @@ function ReviewPanelView({ parentDomNode }: ReviewPanelViewProps) {
return ReactDOM.createPortal(
<>
{isCurrentFileView(subView) ? (
<CurrentFileContainer />
<Container>
<CurrentFileContainer />
</Container>
) : (
<OverviewContainer />
<Container>
<OverviewContainer />
</Container>
)}
</>,
parentDomNode

View file

@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next'
import { useMemo } from 'react'
import ResolvedCommentEntry from './resolved-comment-entry'
import ResolvedCommentEntry from '../entries/resolved-comment-entry'
import moment from 'moment'
type ResolvedCommentsScrollerProps = {

View file

@ -3,7 +3,7 @@ import useScopeValue from '../../../../../shared/hooks/use-scope-value'
import { ReviewPanelState } from '../types/review-panel-state'
import { sendMB } from '../../../../../infrastructure/event-tracking'
import * as ReviewPanel from '../types/review-panel-state'
import { SubView } from '../../../../../../../types/review-panel'
import { SubView } from '../../../../../../../types/review-panel/review-panel'
function useAngularReviewPanelState(): ReviewPanelState {
const [subView, setSubView] = useScopeValue<ReviewPanel.Value<'subView'>>(
@ -12,12 +12,18 @@ function useAngularReviewPanelState(): ReviewPanelState {
const [collapsed, setCollapsed] = useScopeValue<
ReviewPanel.Value<'collapsed'>
>('reviewPanel.overview.docsCollapsedState')
const [entries] = useScopeValue<ReviewPanel.Value<'entries'>>(
'reviewPanel.entries'
)
const [permissions] =
useScopeValue<ReviewPanel.Value<'permissions'>>('permissions')
const [wantTrackChanges] = useScopeValue<
ReviewPanel.Value<'wantTrackChanges'>
>('editor.wantTrackChanges')
const [openDocId] =
useScopeValue<ReviewPanel.Value<'openDocId'>>('editor.open_doc_id')
const [shouldCollapse, setShouldCollapse] = useScopeValue<
ReviewPanel.Value<'shouldCollapse'>
>('reviewPanel.fullTCStateCollapsed')
@ -63,10 +69,12 @@ function useAngularReviewPanelState(): ReviewPanelState {
const values = useMemo<ReviewPanelState['values']>(
() => ({
collapsed,
entries,
permissions,
subView,
shouldCollapse,
subView,
wantTrackChanges,
openDocId,
toggleTrackChangesForEveryone,
toggleTrackChangesForUser,
toggleTrackChangesForGuests,
@ -79,10 +87,12 @@ function useAngularReviewPanelState(): ReviewPanelState {
}),
[
collapsed,
entries,
permissions,
subView,
shouldCollapse,
subView,
wantTrackChanges,
openDocId,
toggleTrackChangesForEveryone,
toggleTrackChangesForUser,
toggleTrackChangesForGuests,

View file

@ -1,15 +1,19 @@
import {
SubView,
DocId,
ReviewPanelEntries,
ReviewPanelPermissions,
} from '../../../../../../../types/review-panel'
SubView,
} from '../../../../../../../types/review-panel/review-panel'
export interface ReviewPanelState {
values: {
collapsed: Record<string, boolean>
subView: SubView
entries: ReviewPanelEntries
permissions: ReviewPanelPermissions
shouldCollapse: boolean
subView: SubView
wantTrackChanges: boolean
openDocId: DocId | null
toggleTrackChangesForEveryone: (isOn: boolean) => unknown
toggleTrackChangesForUser: (isOn: boolean, memberId: string) => unknown
toggleTrackChangesForGuests: (isOn: boolean) => unknown

View file

@ -1,4 +1,4 @@
import { SubView } from '../../../../../types/review-panel'
import { SubView } from '../../../../../types/review-panel/review-panel'
export const isCurrentFileView = (view: SubView) => view === 'cur_file'
export const isOverviewView = (view: SubView) => view === 'overview'

View file

@ -1185,8 +1185,19 @@ button when (@is-overleaf-light = true) {
// CM6-specific review panel rules
.ol-cm-review-panel {
position: relative;
z-index: 6;
display: block;
flex-shrink: 0;
flex-direction: column;
background-color: @rp-bg-blue;
border-left: solid 0 @rp-border-grey;
font-family: @font-family-base;
line-height: @line-height-base;
font-size: @rp-base-font-size;
color: @rp-type-blue;
box-sizing: content-box;
.rp-size-expanded & {
display: flex;
@ -1202,18 +1213,6 @@ button when (@is-overleaf-light = true) {
border-left-width: 1px;
}
background-color: @rp-bg-blue;
border-left: solid 0 @rp-border-grey;
font-family: @font-family-base;
line-height: @line-height-base;
font-size: @rp-base-font-size;
color: @rp-type-blue;
z-index: 6;
position: relative;
box-sizing: content-box;
flex-direction: column;
.review-panel-toggler {
display: flex;
flex-direction: column;
@ -1227,15 +1226,15 @@ button when (@is-overleaf-light = true) {
}
}
.review-panel-toolbar-collapse-button {
border: none;
background: none;
padding: 0;
}
.review-panel-toolbar {
position: sticky;
top: 0;
.review-panel-toolbar-collapse-button {
border: none;
background: none;
padding: 0;
}
}
.rp-nav {
@ -1263,6 +1262,27 @@ button when (@is-overleaf-light = true) {
flex-direction: column;
}
.rp-overview-file {
.rp-overview-file-entries {
//height: auto;
transition: height ease-in-out 0.15s; //, display 0.15s 0s;
}
.rp-overview-file-num-entries {
display: none;
}
&.rp-overview-file-collapse {
.rp-overview-file-num-entries {
display: inline;
}
.rp-overview-file-entries {
// height: 0;
}
}
}
.rp-nav-item {
border-right: 0;
border-bottom: 0;
@ -1270,32 +1290,6 @@ button when (@is-overleaf-light = true) {
background: none;
}
.rp-entry-list {
display: inline-flex;
flex-direction: column;
.rp-overview-file {
.rp-overview-file-entries {
//height: auto;
transition: height ease-in-out 0.15s; //, display 0.15s 0s;
}
.rp-overview-file-num-entries {
display: none;
}
&.rp-overview-file-collapse {
.rp-overview-file-num-entries {
display: inline;
}
.rp-overview-file-entries {
// height: 0;
}
}
}
}
.rp-tc-state {
height: 0;
transition: height 150ms;

View file

@ -78,6 +78,7 @@ export const mockScope = (content?: string) => {
subView: 'cur_file',
formattedProjectMembers: {},
fullTCStateCollapsed: true,
entries: {},
},
ui: {
reviewPanelOpen: true,

View file

@ -0,0 +1,3 @@
declare const brand: unique symbol
export type Brand<T, TBrand> = T & { [brand]: TBrand }

View file

@ -1,8 +0,0 @@
export type SubView = 'cur_file' | 'overview'
export interface ReviewPanelPermissions {
read: boolean
write: boolean
admin: boolean
comment: boolean
}

View file

@ -0,0 +1,47 @@
interface ReviewPanelEntryScreenPos {
y: number
height: number
editorPaddingTop: number
}
interface ReviewPanelBaseEntry {
visible: boolean
}
interface ReviewPanelCommentEntry extends ReviewPanelBaseEntry {
type: 'comment'
content: string
entry_ids: string[]
focused: boolean
offset: number
screenPos: ReviewPanelEntryScreenPos
thread_id: string
}
interface ReviewPanelInsertEntry extends ReviewPanelBaseEntry {
type: 'insert'
}
interface ReviewPanelDeleteEntry extends ReviewPanelBaseEntry {
type: 'delete'
}
interface ReviewPanelAggregateChangeEntry extends ReviewPanelBaseEntry {
type: 'aggregate-change'
}
interface ReviewPanelAddCommentEntry extends ReviewPanelBaseEntry {
type: 'add-comment'
}
interface ReviewPanelBulkActionsEntry extends ReviewPanelBaseEntry {
type: 'bulk-actions'
}
export type ReviewPanelEntry =
| ReviewPanelCommentEntry
| ReviewPanelInsertEntry
| ReviewPanelDeleteEntry
| ReviewPanelAggregateChangeEntry
| ReviewPanelAddCommentEntry
| ReviewPanelBulkActionsEntry

View file

@ -0,0 +1,16 @@
import { Brand } from '../helpers/brand'
import { ReviewPanelEntry } from './entry'
export type SubView = 'cur_file' | 'overview'
export interface ReviewPanelPermissions {
read: boolean
write: boolean
admin: boolean
comment: boolean
}
export type ReviewPanelDocEntries = Record<string, ReviewPanelEntry>
export type DocId = Brand<string, 'DocId'>
export type ReviewPanelEntries = Record<DocId, ReviewPanelDocEntries>