diff --git a/services/web/app/src/Features/Tutorial/TutorialController.mjs b/services/web/app/src/Features/Tutorial/TutorialController.mjs index 2a82642e1c..3a0ac46b16 100644 --- a/services/web/app/src/Features/Tutorial/TutorialController.mjs +++ b/services/web/app/src/Features/Tutorial/TutorialController.mjs @@ -31,6 +31,7 @@ const VALID_KEYS = [ 'old-editor-warning-tooltip', 'old-editor-warning-tooltip-2', 'workbench-rail-popover', + 'themed-dashboard-intro', ] async function completeTutorial(req, res, next) { diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 6d9ef11aed..e7e9681c46 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -14,6 +14,7 @@ "Pricing": "", "Solutions": "", "a_custom_size_has_been_used_in_the_latex_code": "", + "a_dashboard_that_follows_your_lead": "", "a_file_with_that_name_already_exists_and_will_be_overriden": "", "a_more_comprehensive_list_of_keyboard_shortcuts": "", "a_new_reference_was_added": "", @@ -601,6 +602,7 @@ "failed_to_send_managed_user_invite_to_email": "", "failed_to_send_sso_link_invite_to_email": "", "fair_usage_policy_applies": "", + "fancy_going_dark": "", "fast": "", "fast_draft": "", "feature_enabled_or_disabled": "", @@ -761,6 +763,7 @@ "go_to_writefull": "", "good_news_you_already_purchased_this_add_on": "", "good_news_you_are_already_receiving_this_add_on_via_writefull": "", + "got_it": "", "got_questions": "", "group_admin": "", "group_audit_logs": "", @@ -1091,6 +1094,7 @@ "math_inline": "", "maximum_files_uploaded_together": "", "maybe_later": "", + "meet_the_new_dark_dashboard": "", "members_added": "", "members_management": "", "mendeley": "", @@ -2219,11 +2223,15 @@ "wed_love_you_to_stay": "", "welcome_to_overleaf_opening_workspace": "", "welcome_to_sl": "", + "welcome_to_the_dark_side": "", "well_be_here_when_youre_ready": "", "were_making_some_changes_to_project_sharing_this_means_you_will_be_visible": "", "were_performing_maintenance": "", + "weve_given_your_dashboard_a_sleek_new_dark_theme_for_more_comfortable_late_night_research_prefer_the_light_switch_back_anytime_right_here": "", "weve_hit_a_problem_try_starting_a_new_chat": "", "weve_made_it_easier_to_find_and_use_the_tools_you_need_today": "", + "weve_matched_your_dashboard_theme_to_your_editor_preferences_but_you_can_change_that_here_anytime": "", + "weve_set_your_dashboard_to_dark_mode_to_help_you_stay_focused_if_youre_a_fan_of_a_lighter_look_you_can_easily_switch_themes_here": "", "what_did_you_find_most_helpful": "", "what_do_you_need_help_with": "", "what_does_this_mean_for_you": "", @@ -2308,6 +2316,7 @@ "your_current_plan_gives_you": "", "your_current_plan_supports_up_to_x_licenses": "", "your_current_project_will_revert_to_the_version_from_time": "", + "your_dashboard_is_set_to_match_your_system_theme_automatically_want_a_different_look_pick_your_favorite_theme_here": "", "your_email_is_confirmed": "", "your_feedback_matters_answer_two_quick_questions": "", "your_git_access_info": "", diff --git a/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx b/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx index bfe5d4483a..1e63090af7 100644 --- a/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx +++ b/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx @@ -7,13 +7,13 @@ import { isVersionSelected } from '../../utils/history-details' import { useUserContext } from '../../../../shared/context/user-context' import useDropdownActiveItem from '../../hooks/use-dropdown-active-item' import { useHistoryContext } from '../../context/history-context' -import { useEditorContext } from '../../../../shared/context/editor-context' import OLPopover from '@/shared/components/ol/ol-popover' import OLOverlay from '@/shared/components/ol/ol-overlay' import Close from '@/shared/components/close' import { Trans, useTranslation } from 'react-i18next' import MaterialIcon from '@/shared/components/material-icon' import useTutorial from '@/shared/hooks/promotions/use-tutorial' +import { useTutorialContext } from '@/shared/context/tutorial-context' function AllHistoryList() { const { id: currentUserId } = useUserContext() @@ -91,7 +91,7 @@ function AllHistoryList() { } }, [updatesLoadingState]) - const { inactiveTutorials } = useEditorContext() + const { inactiveTutorials } = useTutorialContext() const { showPopup: showHistoryTutorial, tryShowingPopup: tryShowingHistoryTutorial, diff --git a/services/web/frontend/js/features/ide-react/components/editor-survey.tsx b/services/web/frontend/js/features/ide-react/components/editor-survey.tsx index c12cd0b95d..50012c1c38 100644 --- a/services/web/frontend/js/features/ide-react/components/editor-survey.tsx +++ b/services/web/frontend/js/features/ide-react/components/editor-survey.tsx @@ -5,12 +5,12 @@ import OLFormGroup from '@/shared/components/ol/ol-form-group' import OLIconButton from '@/shared/components/ol/ol-icon-button' import { OLToast } from '@/shared/components/ol/ol-toast' import { OLToastContainer } from '@/shared/components/ol/ol-toast-container' -import { useEditorContext } from '@/shared/context/editor-context' import useTutorial from '@/shared/hooks/promotions/use-tutorial' import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { sendMB } from '@/infrastructure/event-tracking' import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils' import { useTranslation } from 'react-i18next' +import { useTutorialContext } from '@/shared/context/tutorial-context' type EditorSurveyPage = 'ease-of-use' | 'meets-my-needs' | 'thank-you' @@ -28,7 +28,7 @@ const EditorSurveyContent = () => { const [easeOfUse, setEaseOfUse] = useState(null) const [meetsMyNeeds, setMeetsMyNeeds] = useState(null) const [page, setPage] = useState('ease-of-use') - const { inactiveTutorials } = useEditorContext() + const { inactiveTutorials } = useTutorialContext() const hasCompletedSurvey = inactiveTutorials.includes(TUTORIAL_KEY) const newEditor = useIsNewEditorEnabled() diff --git a/services/web/frontend/js/features/ide-react/context/react-context-root.tsx b/services/web/frontend/js/features/ide-react/context/react-context-root.tsx index 0acf4efde1..ff6885d957 100644 --- a/services/web/frontend/js/features/ide-react/context/react-context-root.tsx +++ b/services/web/frontend/js/features/ide-react/context/react-context-root.tsx @@ -32,6 +32,7 @@ import { CommandRegistryProvider } from './command-registry-context' import { NewEditorTourProvider } from '@/features/ide-redesign/contexts/new-editor-tour-context' import { EditorSelectionProvider } from '@/shared/context/editor-selection-context' import importOverleafModules from '../../../../macros/import-overleaf-module.macro' +import { TutorialProvider } from '@/shared/context/tutorial-context' const rootContextProviders = importOverleafModules('rootContextProviders') as { import: { default: ElementType } @@ -76,6 +77,7 @@ export const ReactContextRoot: FC< UserFeaturesProvider, NewEditorTourProvider, EditorSelectionProvider, + TutorialProvider, ...providers, } @@ -105,47 +107,49 @@ export const ReactContextRoot: FC< - - - - - - - - - - - - - - - - - - - - { - childrenWrappedWithDynamicProviders - } - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + { + childrenWrappedWithDynamicProviders + } + + + + + + + + + + + + + + + + + + + + diff --git a/services/web/frontend/js/features/ide-redesign/components/new-editor-opt-out-intro-modal.tsx b/services/web/frontend/js/features/ide-redesign/components/new-editor-opt-out-intro-modal.tsx index 3f8c0799d9..f896d61d89 100644 --- a/services/web/frontend/js/features/ide-redesign/components/new-editor-opt-out-intro-modal.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/new-editor-opt-out-intro-modal.tsx @@ -9,15 +9,15 @@ import { useCallback, useEffect, useState } from 'react' import useTutorial from '@/shared/hooks/promotions/use-tutorial' import OLButton from '@/shared/components/ol/ol-button' import { useTranslation } from 'react-i18next' -import { useEditorContext } from '@/shared/context/editor-context' import { useIsNewToNewEditor } from '../utils/new-editor-utils' import { useNewEditorTourContext } from '../contexts/new-editor-tour-context' import promoVideo from './new-editor-promo-video.mp4' +import { useTutorialContext } from '@/shared/context/tutorial-context' const TUTORIAL_KEY = 'new-editor-intro-2' export default function NewEditorOptOutIntroModal() { - const { inactiveTutorials } = useEditorContext() + const { inactiveTutorials } = useTutorialContext() const { tryShowingPopup, showPopup: showModal, diff --git a/services/web/frontend/js/features/ide-redesign/components/old-editor-warning-tooltip.tsx b/services/web/frontend/js/features/ide-redesign/components/old-editor-warning-tooltip.tsx index 33db35e1de..cdc61b3206 100644 --- a/services/web/frontend/js/features/ide-redesign/components/old-editor-warning-tooltip.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/old-editor-warning-tooltip.tsx @@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next' import useTutorial from '@/shared/hooks/promotions/use-tutorial' import { useCallback, useEffect, useState } from 'react' import { useSwitchEnableNewEditorState } from '../hooks/use-switch-enable-new-editor-state' -import { useEditorContext } from '@/shared/context/editor-context' import { canUseNewEditor } from '../utils/new-editor-utils' +import { useTutorialContext } from '@/shared/context/tutorial-context' const TUTORIAL_KEY = 'old-editor-warning-tooltip-2' @@ -15,7 +15,7 @@ export default function OldEditorWarningTooltip({ }: { target: HTMLElement | null }) { - const { inactiveTutorials } = useEditorContext() + const { inactiveTutorials } = useTutorialContext() const { t } = useTranslation() const { loading, setEditorRedesignStatus } = useSwitchEnableNewEditorState() diff --git a/services/web/frontend/js/features/ide-redesign/components/tooltip-promo.tsx b/services/web/frontend/js/features/ide-redesign/components/tooltip-promo.tsx index b5c43c9859..124cdb9e08 100644 --- a/services/web/frontend/js/features/ide-redesign/components/tooltip-promo.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/tooltip-promo.tsx @@ -1,5 +1,5 @@ import Close from '@/shared/components/close' -import { useEditorContext } from '@/shared/context/editor-context' +import { useTutorialContext } from '@/shared/context/tutorial-context' import useTutorial from '@/shared/hooks/promotions/use-tutorial' import { isSplitTestEnabled } from '@/utils/splitTestUtils' import classNames from 'classnames' @@ -26,7 +26,7 @@ export default function TooltipPromotion({ placement?: OverlayProps['placement'] splitTestName?: string }) { - const { inactiveTutorials } = useEditorContext() + const { inactiveTutorials } = useTutorialContext() const { showPopup, tryShowingPopup, hideUntilReload, dismissTutorial } = useTutorial(tutorialKey, eventData) diff --git a/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx b/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx index a9f5bbe897..8bac5780f2 100644 --- a/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx +++ b/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx @@ -1,17 +1,17 @@ import useTutorial from '@/shared/hooks/promotions/use-tutorial' -import { useEditorContext } from '@/shared/context/editor-context' import { useProjectContext } from '@/shared/context/project-context' import OLNotification from '@/shared/components/ol/ol-notification' import { useTranslation, Trans } from 'react-i18next' import { useCallback } from 'react' import { onRollingBuild } from '@/shared/utils/rolling-build' +import { useTutorialContext } from '@/shared/context/tutorial-context' export const TUTORIAL_KEY = 'rolling-compile-image-changed' const RollingCompileImageChangedAlert = () => { const { completeTutorial } = useTutorial(TUTORIAL_KEY) const { project } = useProjectContext() - const { inactiveTutorials } = useEditorContext() + const { inactiveTutorials } = useTutorialContext() const { t } = useTranslation() diff --git a/services/web/frontend/js/features/project-list/components/project-list-root.tsx b/services/web/frontend/js/features/project-list/components/project-list-root.tsx index 3cb12533c7..52e2ba12a4 100644 --- a/services/web/frontend/js/features/project-list/components/project-list-root.tsx +++ b/services/web/frontend/js/features/project-list/components/project-list-root.tsx @@ -21,6 +21,7 @@ import { DsNavStyleProvider } from '@/features/project-list/components/use-is-ds import CookieBanner from '@/shared/components/cookie-banner' import useThemedPage from '@/shared/hooks/use-themed-page' import { UserSettingsProvider } from '@/shared/context/user-settings-context' +import { TutorialProvider } from '@/shared/context/tutorial-context' function ProjectListRoot() { const { isReady } = useWaitForI18n() @@ -37,9 +38,11 @@ export function ProjectListRootInner() { - - - + + + + + diff --git a/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx b/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx index 41c169134c..be72e16f2d 100644 --- a/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx +++ b/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx @@ -18,6 +18,8 @@ import { useScrolled } from '@/features/project-list/components/sidebar/use-scro import { useSendProjectListMB } from '@/features/project-list/components/project-list-events' import { SurveyWidgetDsNav } from '@/features/project-list/components/survey-widget-ds-nav' import { useFeatureFlag } from '@/shared/context/split-test-context' +import { ThemedProjectDashboardNotification } from './themed-project-dashboard-notification' +import { useThemedDashboardIntro } from './use-themed-dashboard-intro' function SidebarDsNav() { const { t } = useTranslation() @@ -38,6 +40,12 @@ function SidebarDsNav() { ) as NavbarDropdownItemData const { containerRef, scrolledUp, scrolledDown } = useScrolled() const themedDsNav = useFeatureFlag('themed-project-dashboard') + const { + completeThemedDashboardIntro, + dismissThemedDashboardIntro, + targetRef, + showingThemedDashboardIntro, + } = useThemedDashboardIntro() return (