mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
Add review mode tutorial popover (#25709)
GitOrigin-RevId: bf2a365b21da780786f2736efb0770cea5f5b656
This commit is contained in:
@@ -14,6 +14,7 @@ const VALID_KEYS = [
|
||||
'full-project-search-promo',
|
||||
'editor-popup-ux-survey',
|
||||
'wf-features-moved',
|
||||
'review-mode',
|
||||
]
|
||||
|
||||
async function completeTutorial(req, res, next) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { FC, RefObject, useCallback, useEffect } from 'react'
|
||||
import { Button, Overlay, Popover } from 'react-bootstrap'
|
||||
import Close from '@/shared/components/close'
|
||||
|
||||
export const ReviewModePromo: FC<{
|
||||
target: RefObject<HTMLSpanElement>
|
||||
showPopup: boolean
|
||||
tryShowingPopup: () => void
|
||||
hideUntilReload: () => void
|
||||
completeTutorial: (props: {
|
||||
action: 'complete'
|
||||
event: 'promo-click' | 'promo-dismiss'
|
||||
}) => void
|
||||
}> = ({
|
||||
showPopup,
|
||||
tryShowingPopup,
|
||||
hideUntilReload,
|
||||
completeTutorial,
|
||||
target,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
tryShowingPopup()
|
||||
}, [tryShowingPopup])
|
||||
|
||||
const handleHide = useCallback(() => {
|
||||
hideUntilReload()
|
||||
}, [hideUntilReload])
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
completeTutorial({
|
||||
action: 'complete',
|
||||
event: 'promo-dismiss',
|
||||
})
|
||||
}, [completeTutorial])
|
||||
|
||||
const handleAccept = useCallback(() => {
|
||||
completeTutorial({
|
||||
action: 'complete',
|
||||
event: 'promo-click',
|
||||
})
|
||||
}, [completeTutorial])
|
||||
|
||||
if (!showPopup) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Overlay
|
||||
target={target.current}
|
||||
placement="bottom"
|
||||
show
|
||||
onHide={handleHide}
|
||||
>
|
||||
<Popover>
|
||||
<Popover.Body style={{ width: 246 }}>
|
||||
<Close variant="dark" onDismiss={handleClose} />
|
||||
<p style={{ fontWeight: 'bold' }}>Track changes have moved</p>
|
||||
<p>
|
||||
Choose <b>Reviewing</b> mode in the dropdown to turn on track
|
||||
changes.
|
||||
</p>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
href="/learn/how-to/Reviewing_and_reviewers_on_Overleaf"
|
||||
target="_blank"
|
||||
size="sm"
|
||||
variant="link"
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
Learn more
|
||||
</Button>
|
||||
<Button onClick={handleAccept} size="sm" variant="secondary">
|
||||
OK
|
||||
</Button>
|
||||
</div>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
</Overlay>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { forwardRef, memo, MouseEventHandler, useState } from 'react'
|
||||
import { forwardRef, memo, MouseEventHandler, useRef, useState } from 'react'
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownMenu,
|
||||
@@ -19,6 +19,9 @@ import { sendMB } from '@/infrastructure/event-tracking'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import UpgradeTrackChangesModal from './upgrade-track-changes-modal'
|
||||
import { ReviewModePromo } from '@/features/review-panel-new/components/review-mode-promo'
|
||||
import useTutorial from '@/shared/hooks/promotions/use-tutorial'
|
||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||
|
||||
type Mode = 'view' | 'review' | 'edit'
|
||||
|
||||
@@ -141,6 +144,7 @@ const ModeSwitcherToggleButton = forwardRef<
|
||||
iconType="edit"
|
||||
label={t('editing')}
|
||||
ariaExpanded={ariaExpanded}
|
||||
currentMode={mode}
|
||||
/>
|
||||
)
|
||||
} else if (mode === 'review') {
|
||||
@@ -152,6 +156,7 @@ const ModeSwitcherToggleButton = forwardRef<
|
||||
iconType="rate_review"
|
||||
label={t('reviewing')}
|
||||
ariaExpanded={ariaExpanded}
|
||||
currentMode={mode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -164,6 +169,7 @@ const ModeSwitcherToggleButton = forwardRef<
|
||||
iconType="visibility"
|
||||
label={t('viewing')}
|
||||
ariaExpanded={ariaExpanded}
|
||||
currentMode={mode}
|
||||
/>
|
||||
)
|
||||
})
|
||||
@@ -176,31 +182,72 @@ const ModeSwitcherToggleButtonContent = forwardRef<
|
||||
iconType: string
|
||||
label: string
|
||||
ariaExpanded: boolean
|
||||
currentMode: string
|
||||
}
|
||||
>(({ onClick, className, iconType, label, ariaExpanded }, ref) => {
|
||||
>(({ onClick, className, iconType, label, ariaExpanded, currentMode }, ref) => {
|
||||
const [isFirstTimeUsed, setIsFirstTimeUsed] = usePersistedState(
|
||||
`modeSwitcherFirstTimeUsed`,
|
||||
true
|
||||
)
|
||||
|
||||
const tutorialProps = useTutorial('review-mode', {
|
||||
name: 'review-mode-notification',
|
||||
})
|
||||
|
||||
const user = useUserContext()
|
||||
const project = useProjectContext()
|
||||
const { reviewPanelOpen } = useLayoutContext()
|
||||
const { inactiveTutorials } = useEditorContext()
|
||||
|
||||
const hasCompletedReviewModeTutorial =
|
||||
inactiveTutorials.includes('review-mode')
|
||||
|
||||
const canShowReviewModePromo =
|
||||
reviewPanelOpen &&
|
||||
currentMode !== 'review' &&
|
||||
project.features.trackChanges &&
|
||||
user.signUpDate &&
|
||||
user.signUpDate < '2025-03-15' &&
|
||||
!hasCompletedReviewModeTutorial
|
||||
|
||||
const containerRef = useRef<HTMLSpanElement | null>(null)
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames('review-mode-switcher-toggle-button', className, {
|
||||
'review-mode-switcher-toggle-button-expanded': isFirstTimeUsed,
|
||||
})}
|
||||
ref={ref}
|
||||
onClick={event => {
|
||||
setIsFirstTimeUsed(false)
|
||||
onClick(event)
|
||||
}}
|
||||
aria-expanded={ariaExpanded}
|
||||
>
|
||||
<MaterialIcon className="material-symbols-outlined" type={iconType} />
|
||||
<div className="review-mode-switcher-toggle-label" aria-label={label}>
|
||||
{label}
|
||||
</div>
|
||||
<MaterialIcon type="keyboard_arrow_down" />
|
||||
</button>
|
||||
<>
|
||||
<span ref={containerRef}>
|
||||
<button
|
||||
className={classNames(
|
||||
'review-mode-switcher-toggle-button',
|
||||
className,
|
||||
{
|
||||
'review-mode-switcher-toggle-button-expanded': isFirstTimeUsed,
|
||||
}
|
||||
)}
|
||||
ref={ref}
|
||||
onClick={event => {
|
||||
setIsFirstTimeUsed(false)
|
||||
if (!hasCompletedReviewModeTutorial) {
|
||||
tutorialProps.completeTutorial({
|
||||
action: 'complete',
|
||||
event: 'promo-click',
|
||||
})
|
||||
}
|
||||
onClick(event)
|
||||
}}
|
||||
aria-expanded={ariaExpanded}
|
||||
>
|
||||
<MaterialIcon className="material-symbols-outlined" type={iconType} />
|
||||
<div className="review-mode-switcher-toggle-label" aria-label={label}>
|
||||
{label}
|
||||
</div>
|
||||
<MaterialIcon type="keyboard_arrow_down" />
|
||||
</button>
|
||||
</span>
|
||||
|
||||
{canShowReviewModePromo && (
|
||||
<ReviewModePromo target={containerRef} {...tutorialProps} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user