diff --git a/services/web/frontend/js/features/source-editor/components/editor-switch-beginner-popover.tsx b/services/web/frontend/js/features/source-editor/components/editor-switch-beginner-popover.tsx new file mode 100644 index 0000000000..40fea785bf --- /dev/null +++ b/services/web/frontend/js/features/source-editor/components/editor-switch-beginner-popover.tsx @@ -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 +}) => { + 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} + setPopoverShown(false)} + target={targetRef.current} + > + +
+ { + setPopoverShown(false) + completeTutorial({ event: 'promo-click', action: 'complete' }) + }} + /> +
+ {t('code_editor_tooltip_title')} +
+
{t('code_editor_tooltip_message')}
+
+
+
+ + ) +} diff --git a/services/web/frontend/js/features/source-editor/components/editor-switch-beginner-tooltip.tsx b/services/web/frontend/js/features/source-editor/components/editor-switch-beginner-tooltip.tsx deleted file mode 100644 index 02da586eef..0000000000 --- a/services/web/frontend/js/features/source-editor/components/editor-switch-beginner-tooltip.tsx +++ /dev/null @@ -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(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 ( - - { - hideCodeEditorTooltip() - completeTutorial({ event: 'promo-click', action: 'complete' }) - }} - /> -
{t('code_editor_tooltip_title')}
-
{t('code_editor_tooltip_message')}
- - } - 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} -
- ) -} diff --git a/services/web/frontend/js/features/source-editor/components/editor-switch.tsx b/services/web/frontend/js/features/source-editor/components/editor-switch.tsx index bdeecfad79..525f979e1c 100644 --- a/services/web/frontend/js/features/source-editor/components/editor-switch.tsx +++ b/services/web/frontend/js/features/source-editor/components/editor-switch.tsx @@ -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(null) + const richTextAvailable = openDocName ? isValidTeXFile(openDocName) : false const { completeTutorial } = useTutorial(codeEditorModePrompt, { location: 'logs', @@ -62,11 +64,15 @@ function EditorSwitch() { checked={!richTextAvailable || !visual} onChange={handleChange} /> - - +