From a9d7f994462c9f92ebba1ca70d9af55704f423f6 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween <5454374+emcsween@users.noreply.github.com> Date: Wed, 13 Dec 2023 07:31:57 -0500 Subject: [PATCH] Merge pull request #16156 from overleaf/jdt-writefull-notif Writefull editor notification GitOrigin-RevId: 1a5077164682dbec67738af0684d364571802816 --- services/web/config/settings.defaults.js | 1 + .../web/frontend/extracted-translations.json | 5 +++- .../components/linking/enable-widget.tsx | 5 +++- .../components/grammarly-advert.tsx | 13 ++------- .../components/source-editor.tsx | 13 ++++++++- .../table-generator/promotion/popover.tsx | 6 +--- .../hooks/use-wait-for-grammarly-check.ts | 29 +++++++++++++++++++ .../js/shared/svgs/writefull-logo.tsx | 4 +-- .../web/frontend/stylesheets/app/editor.less | 6 ++++ .../web/frontend/stylesheets/main-style.less | 1 + .../stylesheets/modules/writefull.less | 3 ++ services/web/locales/en.json | 7 +++-- services/web/types/tutorial.ts | 5 ++++ services/web/types/window.ts | 2 +- 14 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 services/web/frontend/js/shared/hooks/use-wait-for-grammarly-check.ts create mode 100644 services/web/frontend/stylesheets/modules/writefull.less create mode 100644 services/web/types/tutorial.ts diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index da08ac3c4f..90993d71f6 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -851,6 +851,7 @@ module.exports = { sourceEditorComponents: [], sourceEditorCompletionSources: [], sourceEditorSymbolPalette: [], + writefullEditorPromotion: [], langFeedbackLinkingWidgets: [], integrationLinkingWidgets: [], referenceLinkingWidgets: [], diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 684d15a157..ca3a3eea75 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -1448,8 +1448,11 @@ "work_offline": "", "work_with_non_overleaf_users": "", "writefull": "", - "writefull_how_to": "", + "writefull_learn_more": "", + "writefull_prompt_body": "", + "writefull_prompt_title": "", "writefull_settings_description": "", + "writefull_settings_instructions": "", "x_changes_in": "", "x_changes_in_plural": "", "x_price_for_first_month": "", diff --git a/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx b/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx index 998d7feda0..cfbfa1fcc0 100644 --- a/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx +++ b/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx @@ -12,6 +12,7 @@ type EnableWidgetProps = { title: string description: string helpPath: string + helpTextOverride?: string hasFeature?: boolean isPremiumFeature?: boolean statusIndicator?: ReactNode @@ -27,6 +28,7 @@ export function EnableWidget({ title, description, helpPath, + helpTextOverride, hasFeature, isPremiumFeature, statusIndicator, @@ -37,6 +39,7 @@ export function EnableWidget({ disabled, }: EnableWidgetProps) { const { t } = useTranslation() + const helpText = helpTextOverride || t('learn_more') return (
@@ -51,7 +54,7 @@ export function EnableWidget({

{description}{' '} - {t('learn_more')} + {helpText}

{children} diff --git a/services/web/frontend/js/features/source-editor/components/grammarly-advert.tsx b/services/web/frontend/js/features/source-editor/components/grammarly-advert.tsx index 003ccadbe7..c66cac0827 100644 --- a/services/web/frontend/js/features/source-editor/components/grammarly-advert.tsx +++ b/services/web/frontend/js/features/source-editor/components/grammarly-advert.tsx @@ -2,19 +2,12 @@ import { useCallback, useEffect, useState } from 'react' import MaterialIcon from '@/shared/components/material-icon' import * as eventTracking from '../../../infrastructure/event-tracking' import customLocalStorage from '../../../infrastructure/local-storage' -import grammarlyExtensionPresent from '../../../shared/utils/grammarly' +import useWaitForGrammarlyCheck from '@/shared/hooks/use-wait-for-grammarly-check' export default function GrammarlyAdvert() { const [show, setShow] = useState(false) - // grammarly can take some time to load, and wont tell us when they do... so we need to run the check after a bit of time - const [grammarlyInstalled, setGrammarlyInstalled] = useState(false) - useEffect(() => { - const timer = setTimeout( - () => setGrammarlyInstalled(grammarlyExtensionPresent()), - 5000 - ) - return () => clearTimeout(timer) - }, []) + // grammarly can take some time to load, we should assume its installed and hide until we know for sure + const grammarlyInstalled = useWaitForGrammarlyCheck({ initialState: false }) useEffect(() => { const hasDismissedGrammarlyAdvert = customLocalStorage.getItem( diff --git a/services/web/frontend/js/features/source-editor/components/source-editor.tsx b/services/web/frontend/js/features/source-editor/components/source-editor.tsx index d71f896687..84d60cdab6 100644 --- a/services/web/frontend/js/features/source-editor/components/source-editor.tsx +++ b/services/web/frontend/js/features/source-editor/components/source-editor.tsx @@ -1,7 +1,15 @@ -import { lazy, memo, Suspense } from 'react' +import { lazy, memo, Suspense, ElementType } from 'react' import { FullSizeLoadingSpinner } from '../../../shared/components/loading-spinner' import withErrorBoundary from '../../../infrastructure/error-boundary' import { ErrorBoundaryFallback } from '../../../shared/components/error-boundary-fallback' +import importOverleafModules from '../../../../macros/import-overleaf-module.macro' + +const writefullPromotion = importOverleafModules( + 'writefullEditorPromotion' +) as { + import: { default: ElementType } + path: string +}[] const CodeMirrorEditor = lazy( () => @@ -11,6 +19,9 @@ const CodeMirrorEditor = lazy( function SourceEditor() { return ( }> + {writefullPromotion.map(({ import: { default: Component }, path }) => ( + + ))} ) diff --git a/services/web/frontend/js/features/source-editor/components/table-generator/promotion/popover.tsx b/services/web/frontend/js/features/source-editor/components/table-generator/promotion/popover.tsx index 95a9ef673a..2da929ee81 100644 --- a/services/web/frontend/js/features/source-editor/components/table-generator/promotion/popover.tsx +++ b/services/web/frontend/js/features/source-editor/components/table-generator/promotion/popover.tsx @@ -19,6 +19,7 @@ import { useSplitTestContext } from '../../../../../shared/context/split-test-co import { User } from '../../../../../../../types/user' import { useUserContext } from '../../../../../shared/context/user-context' import grammarlyExtensionPresent from '../../../../../shared/utils/grammarly' +import { EditorTutorials } from '../../../../../../../types/tutorial' import { debugConsole } from '../../../../../utils/debugging' const DELAY_BEFORE_SHOWING_PROMOTION = 1000 @@ -26,11 +27,6 @@ const NEW_USER_CUTOFF_TIME = new Date(2023, 8, 20).getTime() const NOW_TIME = new Date().getTime() const GRAMMARLY_CUTOFF_TIME = new Date(2023, 9, 10).getTime() -type EditorTutorials = { - inactiveTutorials: [string] - deactivateTutorial: (key: string) => void -} - const editorContextPropTypes = { inactiveTutorials: PropTypes.arrayOf(PropTypes.string).isRequired, deactivateTutorial: PropTypes.func.isRequired, diff --git a/services/web/frontend/js/shared/hooks/use-wait-for-grammarly-check.ts b/services/web/frontend/js/shared/hooks/use-wait-for-grammarly-check.ts new file mode 100644 index 0000000000..57499ef803 --- /dev/null +++ b/services/web/frontend/js/shared/hooks/use-wait-for-grammarly-check.ts @@ -0,0 +1,29 @@ +import { useEffect, useState } from 'react' + +/** + * + * @param {number} delay how long to wait before checking for grammarly in ms + * @param {boolean} initialState the initial state we should set grammarlyInstalled to before checking after the delay + * @returns {boolean} a stateful boolean which is initially false, then updates to reflect whether grammarly is installed after the delay to check + */ +export default function useWaitForGrammarlyCheck({ + delay = 3000, + initialState = false, +}) { + const [grammarlyInstalled, setGrammarlyInstalled] = useState(() => { + return initialState + }) + + useEffect(() => { + const timer = setTimeout( + () => setGrammarlyInstalled(grammarlyExtensionPresent()), + delay + ) + return () => clearTimeout(timer) + }, [delay]) + return grammarlyInstalled +} + +function grammarlyExtensionPresent() { + return !!document.querySelector('grammarly-desktop-integration') +} diff --git a/services/web/frontend/js/shared/svgs/writefull-logo.tsx b/services/web/frontend/js/shared/svgs/writefull-logo.tsx index 67bfd9f8b6..4c8ff02b56 100644 --- a/services/web/frontend/js/shared/svgs/writefull-logo.tsx +++ b/services/web/frontend/js/shared/svgs/writefull-logo.tsx @@ -1,10 +1,10 @@ -function WritefullLogo({ width = '40', height = '40' }) { +function WritefullLogo({ width = '40', height = '40', background = 'none' }) { return ( void +} diff --git a/services/web/types/window.ts b/services/web/types/window.ts index 79b514ddf8..c4bfe6bb7a 100644 --- a/services/web/types/window.ts +++ b/services/web/types/window.ts @@ -42,6 +42,6 @@ declare global { useRecaptchaNet?: boolean } expectingLinkedFileRefreshedSocketFor?: string | null - writefull?: Map + writefull?: any } }