diff --git a/app/(main)/[locale]/layout.tsx b/app/(main)/[locale]/layout.tsx index 238de5b7..60944ea5 100644 --- a/app/(main)/[locale]/layout.tsx +++ b/app/(main)/[locale]/layout.tsx @@ -6,6 +6,7 @@ import { EmbeddedBridgeProvider } from "@/components/providers/embedded-bridge-p import { RateLimitToastProvider } from "@/components/providers/rate-limit-toast-provider"; import { TourProvider } from "@/components/tour/tour-provider"; import { ProtocolLaunchHandlerProvider } from "@/components/protocol/protocol-launch-handler-provider"; +import { ProInterfaceRedirect } from "@/components/pro/pro-interface-redirect"; import { PluginDialogHost } from "@/components/plugins/plugin-dialog-host"; import { PluginConsentDialog } from "@/components/plugins/plugin-consent-dialog"; import { locales } from "@/i18n/routing"; @@ -36,6 +37,7 @@ export default async function LocaleLayout({ + {children} diff --git a/app/(main)/[locale]/pro/page.tsx b/app/(main)/[locale]/pro/page.tsx index 30cfda7c..c4c9b51e 100644 --- a/app/(main)/[locale]/pro/page.tsx +++ b/app/(main)/[locale]/pro/page.tsx @@ -9,6 +9,7 @@ import { InlineAppView } from "@/components/layout/inline-app-view"; import { useSidebarApps } from "@/hooks/use-sidebar-apps"; import { useAuthStore, redirectToLogin } from "@/stores/auth-store"; import { useEmailStore } from "@/stores/email-store"; +import { useSettingsStore } from "@/stores/settings-store"; import { useDeviceDetection } from "@/hooks/use-media-query"; import { EmbeddedContext } from "@/hooks/use-is-embedded"; import { PaneSizeContext } from "@/hooks/use-pane-size"; @@ -128,6 +129,7 @@ export default function ProHome() { const authLoading = useAuthStore((s) => s.isLoading); const quota = useEmailStore((s) => s.quota); const isPushConnected = useEmailStore((s) => s.isPushConnected); + const proInterface = useSettingsStore((s) => s.proInterface); const tabs = useProTabStore((s) => s.tabs); const activeMainTabId = useProTabStore((s) => s.activeTabId); @@ -165,10 +167,14 @@ export default function ProHome() { }, [initialCheckDone, isAuthenticated, authLoading]); useEffect(() => { - if (initialCheckDone && (isMobile || isTablet) && typeof window !== "undefined") { + if (!initialCheckDone || typeof window === "undefined") return; + // Pro is desktop-only, and only used when the user has explicitly + // enabled it. If either precondition stops holding, hand the user back + // to the standard shell. + if (isMobile || isTablet || !proInterface) { window.location.replace("/"); } - }, [initialCheckDone, isMobile, isTablet]); + }, [initialCheckDone, isMobile, isTablet, proInterface]); const mainTabs = useMemo(() => tabs.filter((t) => t.paneId === 'main'), [tabs]); const splitTabs = useMemo(() => tabs.filter((t) => t.paneId === 'split'), [tabs]); diff --git a/components/pro/pro-interface-redirect.tsx b/components/pro/pro-interface-redirect.tsx new file mode 100644 index 00000000..098fe219 --- /dev/null +++ b/components/pro/pro-interface-redirect.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { useEffect } from "react"; +import { usePathname, useRouter } from "@/i18n/navigation"; +import { useSettingsStore } from "@/stores/settings-store"; +import { useIsDesktop } from "@/hooks/use-media-query"; +import { useProTabStore, type ProTabKind } from "@/stores/pro-tab-store"; + +const STANDARD_PATH_TO_TAB: Record> = { + '/': 'mail', + '/calendar': 'calendar', + '/contacts': 'contacts', + '/files': 'files', + '/settings': 'settings', +}; + +/** + * When the Pro interface is enabled, the standard mail/calendar/contacts/ + * files/settings routes are taken over by the Pro shell — the user shouldn't + * have to click "Open" in settings to land there. Mobile/tablet keeps the + * standard layout because Pro is desktop-only (see pro/page.tsx). + */ +export function ProInterfaceRedirect() { + const router = useRouter(); + const pathname = usePathname(); + const proInterface = useSettingsStore((s) => s.proInterface); + const isDesktop = useIsDesktop(); + + useEffect(() => { + if (!proInterface || !isDesktop) return; + const tabKind = STANDARD_PATH_TO_TAB[pathname]; + if (!tabKind) return; + useProTabStore.getState().openTab(tabKind); + router.replace('/pro'); + }, [proInterface, isDesktop, pathname, router]); + + return null; +} diff --git a/components/settings/layout-settings.tsx b/components/settings/layout-settings.tsx index 213cf1a0..a3ac41c6 100644 --- a/components/settings/layout-settings.tsx +++ b/components/settings/layout-settings.tsx @@ -1,13 +1,11 @@ "use client"; import { useTranslations } from 'next-intl'; -import { Link } from '@/i18n/navigation'; import { useSettingsStore, type ToolbarPosition, type MailLayout } from '@/stores/settings-store'; import { SettingsSection, SettingItem, RadioGroup, ToggleSwitch } from './settings-section'; import { cn } from '@/lib/utils'; import { usePolicyStore } from '@/stores/policy-store'; import { useAccountStore } from '@/stores/account-store'; -import { useMediaQuery } from '@/hooks/use-media-query'; const MAIL_LAYOUT_PREVIEW_ROWS = [ { sender: 'Alice', subject: 'Quarterly roadmap', preview: 'The draft is ready for review.', selected: false }, @@ -120,7 +118,6 @@ export function LayoutSettings() { const { toolbarPosition, showToolbarLabels, hideAccountSwitcher, showRailAccountList, enableUnifiedMailbox, colorfulSidebarIcons, mailLayout, proInterface, updateSetting } = useSettingsStore(); const { isSettingLocked, isSettingHidden } = usePolicyStore(); const accounts = useAccountStore(s => s.accounts); - const isDesktop = useMediaQuery('(min-width: 1024px)'); return ( @@ -193,20 +190,10 @@ export function LayoutSettings() { )} -
- {proInterface && isDesktop && ( - - {t('pro_interface.open_label')} - - )} - updateSetting('proInterface', v)} - /> -
+ updateSetting('proInterface', v)} + />
);