overleaf/services/web/frontend/js/features/review-panel-new/components/review-panel-change.tsx
Alf Eaton 2304536844 Add review panel context providers and components (#19490)
* Tidy up review panel components

* Add ReviewPanel providers

* [web] new design for review panel track change (#19544)

* [web] new design for review panel track change

* fixed mini view

* mini icon style change

* fix icon size

* format date

* useRangesUserContext hook

* remove useRangesUserContext hook

* using full class names

* fix action icons hover

* change wording for tooltips

* added ReviewPanelChangeUser component

* Update header in new review panel

* Extract ReviewPanelTrackChangesMenuButton as a separate component

* Remove wrapper div

* Replace h2 with div for review panel label

* Rename ReviewPanelTools to ReviewPanelHeader

* Rename trackChangesExpanded -> trackChangesMenuExpanded

* Dont break memoisation of ReviewPanelTrackChangesMenuButton

* Fix the width of the track changes arrow icon

* Update how prop types are declared

* Remove new empty state from old review panel

* Add empty state to new review panel

* Add project members and owner to ChangesUsers context (#19624)

---------

Co-authored-by: Alf Eaton <alf.eaton@overleaf.com>

* Redesign comment entry in review panel (#19678)

* Redesign comment entry in review panel

* ReviewPanelCommentOptions component

* remove unused prop

* Tidying

* Add conditional import

* Optional changeManager

* Add more split test compatibility

* More split test compatibility

* Fixes

* Improve overview scrolling

* Fix overview scrolling

* Fix & simplify track changes toggle

* Fix overview scrolling

* Fix current file container

* ExpandableContent component for messages in review panel (#19738)

* ExpandableContent component for messages in review panel

* remove isExpanded dependancy

* Delete comment option for new review panel (#19772)

* Delete comment option for new review panel

* dont show thread warning if there are no replies

* fix hasReplies issue

* Implement initial collapsing overview files

* Fix positioning of overview panel

* Small styling changes

* Add count of unresolved comments and tracked chanegs

* More style adjustments

* Move review-panel-overview styles into css file

* Remove unused var

---------

Co-authored-by: Domagoj Kriskovic <dom.kriskovic@overleaf.com>
Co-authored-by: David Powell <david.powell@overleaf.com>
Co-authored-by: David <33458145+davidmcpowell@users.noreply.github.com>
GitOrigin-RevId: e67463443d541f88445a86eed5e2b6ec6040f9c7
2024-08-13 08:04:20 +00:00

172 lines
5.7 KiB
TypeScript

import { memo } from 'react'
import { useRangesActionsContext } from '../context/ranges-context'
import {
Change,
DeleteOperation,
EditOperation,
} from '../../../../../types/change'
import { useTranslation } from 'react-i18next'
import classnames from 'classnames'
import { useCodeMirrorStateContext } from '@/features/source-editor/components/codemirror-editor'
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
import { isFocused } from '../utils/is-focused'
import { Button } from 'react-bootstrap'
import Tooltip from '@/shared/components/tooltip'
import MaterialIcon from '@/shared/components/material-icon'
import { formatTimeBasedOnYear } from '@/features/utils/format-date'
import { useChangesUsersContext } from '../context/changes-users-context'
import { ReviewPanelChangeUser } from './review-panel-change-user'
export const ReviewPanelChange = memo<{
change: Change<EditOperation>
aggregate?: Change<DeleteOperation>
top?: number
}>(({ change, aggregate, top }) => {
const state = useCodeMirrorStateContext()
const { t } = useTranslation()
const { acceptChanges, rejectChanges } = useRangesActionsContext()
const permissions = usePermissionsContext()
const changesUsers = useChangesUsersContext()
if (!changesUsers) {
// if users are not loaded yet, do not show "Unknown" user
return null
}
const focused = isFocused(change.op, state.selection.main)
return (
<div
className={classnames('review-panel-entry', 'review-panel-entry-change', {
'review-panel-entry-focused': focused,
'review-panel-entry-insert': 'i' in change.op,
'review-panel-entry-delete': 'd' in change.op,
// TODO: aggregate
})}
data-top={top}
data-pos={change.op.p}
style={{
position: top === undefined ? 'relative' : 'absolute',
visibility: top === undefined ? 'visible' : 'hidden',
transition: 'top .3s, left .1s, right .1s',
}}
>
<div className="review-panel-entry-indicator">
<MaterialIcon type="edit" className="review-panel-entry-icon" />
</div>
<div className="review-panel-entry-content">
<div className="review-panel-entry-header">
<div>
<div className="review-panel-entry-user">
<ReviewPanelChangeUser change={change} />
</div>
<div className="review-panel-entry-time">
{formatTimeBasedOnYear(change.metadata?.ts)}
</div>
</div>
{permissions.write && (
<div className="review-panel-entry-actions">
<Tooltip
id="accept-change"
overlayProps={{ placement: 'bottom' }}
description={t('accept_change')}
>
<Button
onClick={() =>
aggregate
? acceptChanges(change.id, aggregate.id)
: acceptChanges(change.id)
}
bsStyle={null}
>
<MaterialIcon
type="check"
className="review-panel-entry-actions-icon"
accessibilityLabel={t('accept_change')}
/>
</Button>
</Tooltip>
<Tooltip
id="reject-change"
description={t('reject_change')}
overlayProps={{ placement: 'bottom' }}
>
<Button
bsStyle={null}
onClick={() =>
aggregate
? rejectChanges(change.id, aggregate.id)
: rejectChanges(change.id)
}
>
<MaterialIcon
className="review-panel-entry-actions-icon"
accessibilityLabel={t('reject_change')}
type="close"
/>
</Button>
</Tooltip>
</div>
)}
</div>
<div className="review-panel-change-body">
{'i' in change.op && (
<>
{aggregate ? (
<MaterialIcon
className="review-panel-entry-icon review-panel-entry-icon-changed"
type="edit"
/>
) : (
<MaterialIcon
className="review-panel-entry-icon review-panel-entry-icon-accept"
type="add_circle"
/>
)}
{aggregate ? (
<span>
{t('aggregate_changed')}:{' '}
<del className="review-panel-content-highlight">
{aggregate.op.d}
</del>{' '}
{t('aggregate_to')}{' '}
<ins className="review-panel-content-highlight">
{change.op.i}
</ins>
</span>
) : (
<span>
{t('tracked_change_added')}:&nbsp;
<ins className="review-panel-content-highlight">
{change.op.i}
</ins>
</span>
)}
</>
)}
{'d' in change.op && (
<>
<MaterialIcon
className="review-panel-entry-icon review-panel-entry-icon-reject"
type="delete"
/>
<span>
{t('tracked_change_deleted')}:&nbsp;
<del className="review-panel-content-highlight">
{change.op.d}
</del>
</span>
</>
)}
</div>
</div>
</div>
)
})
ReviewPanelChange.displayName = 'ReviewPanelChange'