mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 11:01:56 +02:00
Merge pull request #24688 from overleaf/td-bs5-editor-beginner-switch-popover
Migrate beginner editor switch popover to BS5 GitOrigin-RevId: c470df46989de7ad6477ee23ff13fc95dd580ea8
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { ReactElement, useCallback, useEffect, useState } from 'react'
|
||||
import Close from '@/shared/components/close'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
import useTutorial from '@/shared/hooks/promotions/use-tutorial'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import useScopeValue from '@/shared/hooks/use-scope-value'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import getMeta from '@/utils/meta'
|
||||
import OLPopover from '@/features/ui/components/ol/ol-popover'
|
||||
import OLOverlay from '@/features/ui/components/ol/ol-overlay'
|
||||
|
||||
const CODE_EDITOR_POPOVER_TIMEOUT = 1000
|
||||
export const codeEditorModePrompt = 'code-editor-mode-prompt'
|
||||
|
||||
export const EditorSwitchBeginnerPopover = ({
|
||||
children,
|
||||
targetRef,
|
||||
}: {
|
||||
children: ReactElement
|
||||
targetRef: React.RefObject<HTMLElement>
|
||||
}) => {
|
||||
const user = useUserContext()
|
||||
const { inactiveTutorials } = useEditorContext()
|
||||
const { t } = useTranslation()
|
||||
const [codeEditorOpened] = useScopeValue('editor.codeEditorOpened')
|
||||
const { completeTutorial } = useTutorial(codeEditorModePrompt, {
|
||||
location: 'logs',
|
||||
name: codeEditorModePrompt,
|
||||
})
|
||||
const [popoverShown, setPopoverShown] = useState(false)
|
||||
|
||||
const shouldShowCodeEditorPopover = useCallback(() => {
|
||||
if (inactiveTutorials.includes(codeEditorModePrompt)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (getMeta('ol-usedLatex') !== 'never') {
|
||||
// only show popover to the users that never used LaTeX (submitted in onboarding data collection)
|
||||
return false
|
||||
}
|
||||
|
||||
if (codeEditorOpened) {
|
||||
// dont show popover if code editor was opened at some point
|
||||
return false
|
||||
}
|
||||
|
||||
const msSinceSignedUp =
|
||||
user.signUpDate && Date.now() - new Date(user.signUpDate).getTime()
|
||||
|
||||
if (msSinceSignedUp && msSinceSignedUp < 24 * 60 * 60 * 1000) {
|
||||
// dont show popover if user has signed up is less than 24 hours
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, [codeEditorOpened, inactiveTutorials, user.signUpDate])
|
||||
|
||||
useEffect(() => {
|
||||
if (popoverShown && codeEditorOpened) {
|
||||
setPopoverShown(false)
|
||||
}
|
||||
}, [codeEditorOpened, popoverShown])
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
if (shouldShowCodeEditorPopover()) {
|
||||
setPopoverShown(true)
|
||||
}
|
||||
}, CODE_EDITOR_POPOVER_TIMEOUT)
|
||||
|
||||
return () => clearTimeout(timeout)
|
||||
}, [shouldShowCodeEditorPopover])
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<OLOverlay
|
||||
placement="bottom"
|
||||
show={popoverShown}
|
||||
rootClose
|
||||
onHide={() => setPopoverShown(false)}
|
||||
target={targetRef.current}
|
||||
>
|
||||
<OLPopover id="editor-switch-popover">
|
||||
<div>
|
||||
<Close
|
||||
variant="dark"
|
||||
onDismiss={() => {
|
||||
setPopoverShown(false)
|
||||
completeTutorial({ event: 'promo-click', action: 'complete' })
|
||||
}}
|
||||
/>
|
||||
<div className="tooltip-title">
|
||||
{t('code_editor_tooltip_title')}
|
||||
</div>
|
||||
<div>{t('code_editor_tooltip_message')}</div>
|
||||
</div>
|
||||
</OLPopover>
|
||||
</OLOverlay>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
import { ReactElement, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import Tooltip from '../../../shared/components/tooltip'
|
||||
import Close from '@/shared/components/close'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
import useTutorial from '@/shared/hooks/promotions/use-tutorial'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import useScopeValue from '@/shared/hooks/use-scope-value'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
const CODE_EDITOR_TOOLTIP_TIMEOUT = 1000
|
||||
export const codeEditorModePrompt = 'code-editor-mode-prompt'
|
||||
|
||||
export const EditorSwitchBeginnerTooltip = ({
|
||||
children,
|
||||
}: {
|
||||
children: ReactElement
|
||||
}) => {
|
||||
const toolbarRef = useRef<any>(null)
|
||||
const user = useUserContext()
|
||||
const { inactiveTutorials } = useEditorContext()
|
||||
const { t } = useTranslation()
|
||||
const [codeEditorOpened] = useScopeValue('editor.codeEditorOpened')
|
||||
const { completeTutorial } = useTutorial(codeEditorModePrompt, {
|
||||
location: 'logs',
|
||||
name: codeEditorModePrompt,
|
||||
})
|
||||
const [tooltipShown, setTooltipShown] = useState(false)
|
||||
|
||||
const shouldShowCodeEditorTooltip = useCallback(() => {
|
||||
if (inactiveTutorials.includes(codeEditorModePrompt)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (getMeta('ol-usedLatex') !== 'never') {
|
||||
// only show tooltip to the users that never used LaTeX (submitted in onboarding data collection)
|
||||
return false
|
||||
}
|
||||
|
||||
if (codeEditorOpened) {
|
||||
// dont show tooltip if code editor was opened at some point
|
||||
return false
|
||||
}
|
||||
|
||||
const msSinceSignedUp =
|
||||
user.signUpDate && Date.now() - new Date(user.signUpDate).getTime()
|
||||
|
||||
if (msSinceSignedUp && msSinceSignedUp < 24 * 60 * 60 * 1000) {
|
||||
// dont show tooltip if user has signed up is less than 24 hours
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, [codeEditorOpened, inactiveTutorials, user.signUpDate])
|
||||
|
||||
const showCodeEditorTooltip = useCallback(() => {
|
||||
if (toolbarRef.current && 'show' in toolbarRef.current) {
|
||||
toolbarRef.current.show()
|
||||
setTooltipShown(true)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const hideCodeEditorTooltip = useCallback(() => {
|
||||
if (toolbarRef.current && 'hide' in toolbarRef.current) {
|
||||
toolbarRef.current.hide()
|
||||
setTooltipShown(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (tooltipShown && codeEditorOpened) {
|
||||
hideCodeEditorTooltip()
|
||||
}
|
||||
}, [codeEditorOpened, hideCodeEditorTooltip, tooltipShown])
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
if (shouldShowCodeEditorTooltip()) {
|
||||
showCodeEditorTooltip()
|
||||
}
|
||||
}, CODE_EDITOR_TOOLTIP_TIMEOUT)
|
||||
|
||||
return () => clearTimeout(timeout)
|
||||
}, [showCodeEditorTooltip, shouldShowCodeEditorTooltip])
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
id="editor-switch-tooltip"
|
||||
description={
|
||||
<div>
|
||||
<Close
|
||||
variant="dark"
|
||||
onDismiss={() => {
|
||||
hideCodeEditorTooltip()
|
||||
completeTutorial({ event: 'promo-click', action: 'complete' })
|
||||
}}
|
||||
/>
|
||||
<div className="tooltip-title">{t('code_editor_tooltip_title')}</div>
|
||||
<div>{t('code_editor_tooltip_message')}</div>
|
||||
</div>
|
||||
}
|
||||
tooltipProps={{
|
||||
className: 'editor-switch-tooltip',
|
||||
}}
|
||||
overlayProps={{
|
||||
ref: toolbarRef,
|
||||
placement: 'bottom',
|
||||
shouldUpdatePosition: true,
|
||||
// @ts-ignore
|
||||
// trigger: null is used to prevent the tooltip from showing on hover
|
||||
// but it is not allowed in the type definition
|
||||
trigger: null,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeEvent, FC, memo, useCallback } from 'react'
|
||||
import { ChangeEvent, FC, memo, useCallback, useRef } from 'react'
|
||||
import useScopeValue from '@/shared/hooks/use-scope-value'
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import useTutorial from '@/shared/hooks/promotions/use-tutorial'
|
||||
@@ -6,9 +6,9 @@ import { sendMB } from '../../../infrastructure/event-tracking'
|
||||
import { isValidTeXFile } from '../../../main/is-valid-tex-file'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
EditorSwitchBeginnerTooltip,
|
||||
EditorSwitchBeginnerPopover,
|
||||
codeEditorModePrompt,
|
||||
} from './editor-switch-beginner-tooltip'
|
||||
} from './editor-switch-beginner-popover'
|
||||
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
||||
|
||||
function EditorSwitch() {
|
||||
@@ -17,6 +17,8 @@ function EditorSwitch() {
|
||||
const [codeEditorOpened] = useScopeValue('editor.codeEditorOpened')
|
||||
const { openDocName } = useEditorManagerContext()
|
||||
|
||||
const codeEditorRef = useRef<HTMLLabelElement>(null)
|
||||
|
||||
const richTextAvailable = openDocName ? isValidTeXFile(openDocName) : false
|
||||
const { completeTutorial } = useTutorial(codeEditorModePrompt, {
|
||||
location: 'logs',
|
||||
@@ -62,11 +64,15 @@ function EditorSwitch() {
|
||||
checked={!richTextAvailable || !visual}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<EditorSwitchBeginnerTooltip>
|
||||
<label htmlFor="editor-switch-cm6" className="toggle-switch-label">
|
||||
<EditorSwitchBeginnerPopover targetRef={codeEditorRef}>
|
||||
<label
|
||||
ref={codeEditorRef}
|
||||
htmlFor="editor-switch-cm6"
|
||||
className="toggle-switch-label"
|
||||
>
|
||||
<span>{t('code_editor')}</span>
|
||||
</label>
|
||||
</EditorSwitchBeginnerTooltip>
|
||||
</EditorSwitchBeginnerPopover>
|
||||
|
||||
<RichTextToggle
|
||||
checked={richTextAvailable && visual}
|
||||
|
||||
Reference in New Issue
Block a user