From ec89b77e953fe7155283e781511b873418460c6e Mon Sep 17 00:00:00 2001 From: James Brunton Date: Thu, 13 Nov 2025 16:40:56 +0000 Subject: [PATCH] Move to banner --- .../public/locales/en-GB/translation.json | 13 ++- frontend/src/desktop/App.tsx | 53 +++++++++ .../src/desktop/components/AppProviders.tsx | 10 -- .../desktop/components/DefaultAppPrompt.tsx | 78 ------------- .../components/shared/DefaultAppBanner.tsx | 110 ++++++++++++++++++ .../configSections/DefaultAppSettings.tsx | 86 ++++++++++++++ .../config/configSections/GeneralSection.tsx | 18 +++ .../src/desktop/hooks/useDefaultAppPrompt.ts | 82 ------------- 8 files changed, 279 insertions(+), 171 deletions(-) create mode 100644 frontend/src/desktop/App.tsx delete mode 100644 frontend/src/desktop/components/DefaultAppPrompt.tsx create mode 100644 frontend/src/desktop/components/shared/DefaultAppBanner.tsx create mode 100644 frontend/src/desktop/components/shared/config/configSections/DefaultAppSettings.tsx create mode 100644 frontend/src/desktop/components/shared/config/configSections/GeneralSection.tsx delete mode 100644 frontend/src/desktop/hooks/useDefaultAppPrompt.ts diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index 689166047..96335fa23 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -47,6 +47,11 @@ "description": "You can change this later in your system settings.", "notNow": "Not Now", "setDefault": "Set Default", + "dismiss": "Dismiss", + "prompt": { + "title": "Set as Default PDF Editor", + "message": "Make Stirling PDF your default application for opening PDF files." + }, "success": { "title": "Default App Set", "message": "Stirling PDF is now your default PDF editor" @@ -351,7 +356,13 @@ "mode": { "fullscreen": "Fullscreen", "sidebar": "Sidebar" - } + }, + "defaultPdfEditor": "Default PDF editor", + "defaultPdfEditorActive": "Stirling PDF is your default PDF editor", + "defaultPdfEditorInactive": "Another application is set as default", + "defaultPdfEditorChecking": "Checking...", + "defaultPdfEditorSet": "Already Default", + "setAsDefault": "Set as Default" }, "hotkeys": { "title": "Keyboard Shortcuts", diff --git a/frontend/src/desktop/App.tsx b/frontend/src/desktop/App.tsx new file mode 100644 index 000000000..89e092aa3 --- /dev/null +++ b/frontend/src/desktop/App.tsx @@ -0,0 +1,53 @@ +import { Suspense } from "react"; +import { Routes, Route } from "react-router-dom"; +import { AppProviders } from "@app/components/AppProviders"; +import { LoadingFallback } from "@app/components/shared/LoadingFallback"; +import Landing from "@app/routes/Landing"; +import Login from "@app/routes/Login"; +import Signup from "@app/routes/Signup"; +import AuthCallback from "@app/routes/AuthCallback"; +import InviteAccept from "@app/routes/InviteAccept"; +import OnboardingTour from "@app/components/onboarding/OnboardingTour"; +import { DefaultAppBanner } from "@app/components/shared/DefaultAppBanner"; + +// Import global styles +import "@app/styles/tailwind.css"; +import "@app/styles/cookieconsent.css"; +import "@app/styles/index.css"; +import "@app/styles/auth-theme.css"; + +// Import file ID debugging helpers (development only) +import "@app/utils/fileIdSafety"; + +export default function App() { + return ( + }> + +
+ +
+ + + {/* Auth routes - no nested providers needed */} + } /> + } /> + } /> + } /> + + {/* Main app routes - Landing handles auth logic */} + } /> + + +
+
+
+
+ ); +} diff --git a/frontend/src/desktop/components/AppProviders.tsx b/frontend/src/desktop/components/AppProviders.tsx index 225b5f6f5..c04ede4f9 100644 --- a/frontend/src/desktop/components/AppProviders.tsx +++ b/frontend/src/desktop/components/AppProviders.tsx @@ -2,18 +2,13 @@ import { ReactNode } from "react"; import { AppProviders as ProprietaryAppProviders } from "@proprietary/components/AppProviders"; import { DesktopConfigSync } from '@app/components/DesktopConfigSync'; import { DESKTOP_DEFAULT_APP_CONFIG } from '@app/config/defaultAppConfig'; -import { DefaultAppPrompt } from '@app/components/DefaultAppPrompt'; -import { useDefaultAppPrompt } from '@app/hooks/useDefaultAppPrompt'; /** * Desktop application providers * Wraps proprietary providers and adds desktop-specific configuration * - Enables retry logic for app config (needed for Tauri mode when backend is starting) - * - Shows default PDF handler prompt on first launch */ export function AppProviders({ children }: { children: ReactNode }) { - const { promptOpened, handleSetDefault, handleDismiss } = useDefaultAppPrompt(); - return ( - {children} ); diff --git a/frontend/src/desktop/components/DefaultAppPrompt.tsx b/frontend/src/desktop/components/DefaultAppPrompt.tsx deleted file mode 100644 index 3d3cc4c14..000000000 --- a/frontend/src/desktop/components/DefaultAppPrompt.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Modal, Text, Button, Stack, Flex } from '@mantine/core'; -import { useTranslation } from 'react-i18next'; -import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; -import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; -import CancelIcon from '@mui/icons-material/Cancel'; -import { CSSProperties } from 'react'; - -interface DefaultAppPromptProps { - opened: boolean; - onSetDefault: () => void; - onDismiss: () => void; -} - -const ICON_STYLE: CSSProperties = { - fontSize: 48, - display: 'block', - margin: '0 auto 12px', - color: 'var(--mantine-color-blue-6)', -}; - -export const DefaultAppPrompt = ({ opened, onSetDefault, onDismiss }: DefaultAppPromptProps) => { - const { t } = useTranslation(); - - return ( - - - - - {t( - 'defaultApp.message', - 'Would you like to set Stirling PDF as your default PDF editor?' - )} - - - {t( - 'defaultApp.description', - 'You can change this later in your system settings.' - )} - - - - - - - - - ); -}; diff --git a/frontend/src/desktop/components/shared/DefaultAppBanner.tsx b/frontend/src/desktop/components/shared/DefaultAppBanner.tsx new file mode 100644 index 000000000..1e75faa01 --- /dev/null +++ b/frontend/src/desktop/components/shared/DefaultAppBanner.tsx @@ -0,0 +1,110 @@ +import React, { useState, useEffect } from 'react'; +import { Paper, Group, Text, Button, ActionIcon } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import LocalIcon from '@app/components/shared/LocalIcon'; +import { defaultAppService } from '@app/services/defaultAppService'; +import { alert } from '@app/components/toast'; + +const PROMPT_DISMISSED_KEY = 'stirlingpdf_default_app_prompt_dismissed'; + +export const DefaultAppBanner: React.FC = () => { + const { t } = useTranslation(); + const [promptDismissed, setPromptDismissed] = useState(() => { + return localStorage.getItem(PROMPT_DISMISSED_KEY) === 'true'; + }); + const [isDefault, setIsDefault] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + checkDefaultStatus(); + }, []); + + const checkDefaultStatus = async () => { + try { + const status = await defaultAppService.isDefaultPdfHandler(); + setIsDefault(status); + } catch (error) { + console.error('Failed to check default status:', error); + } + }; + + const handleSetDefault = async () => { + setIsLoading(true); + try { + const result = await defaultAppService.setAsDefaultPdfHandler(); + + if (result === 'set_successfully') { + alert({ + alertType: 'success', + title: t('defaultApp.success.title', 'Default App Set'), + body: t('defaultApp.success.message', 'Stirling PDF is now your default PDF editor'), + }); + setIsDefault(true); + } else if (result === 'opened_settings') { + alert({ + alertType: 'neutral', + title: t('defaultApp.settingsOpened.title', 'Settings Opened'), + body: t('defaultApp.settingsOpened.message', 'Please select Stirling PDF in your system settings'), + }); + } + } catch (error) { + console.error('Failed to set default:', error); + alert({ + alertType: 'error', + title: t('defaultApp.error.title', 'Error'), + body: t('defaultApp.error.message', 'Failed to set default PDF handler'), + }); + } finally { + setIsLoading(false); + } + }; + + const handleDismissPrompt = () => { + setPromptDismissed(true); + localStorage.setItem(PROMPT_DISMISSED_KEY, 'true'); + }; + + if (promptDismissed || isDefault !== false) { + return null; + } + + return ( + + + + + {t('defaultApp.prompt.message', 'Make Stirling PDF your default application for opening PDF files.')} + + + + + + + + ); +}; diff --git a/frontend/src/desktop/components/shared/config/configSections/DefaultAppSettings.tsx b/frontend/src/desktop/components/shared/config/configSections/DefaultAppSettings.tsx new file mode 100644 index 000000000..621f672d1 --- /dev/null +++ b/frontend/src/desktop/components/shared/config/configSections/DefaultAppSettings.tsx @@ -0,0 +1,86 @@ +import React, { useState, useEffect } from 'react'; +import { Paper, Text, Button, Group } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import { defaultAppService } from '@app/services/defaultAppService'; +import { alert } from '@app/components/toast'; + +export const DefaultAppSettings: React.FC = () => { + const { t } = useTranslation(); + const [isDefault, setIsDefault] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + checkDefaultStatus(); + }, []); + + const checkDefaultStatus = async () => { + try { + const status = await defaultAppService.isDefaultPdfHandler(); + setIsDefault(status); + } catch (error) { + console.error('Failed to check default status:', error); + } + }; + + const handleSetDefault = async () => { + setIsLoading(true); + try { + const result = await defaultAppService.setAsDefaultPdfHandler(); + + if (result === 'set_successfully') { + alert({ + alertType: 'success', + title: t('defaultApp.success.title', 'Default App Set'), + body: t('defaultApp.success.message', 'Stirling PDF is now your default PDF editor'), + }); + setIsDefault(true); + } else if (result === 'opened_settings') { + alert({ + alertType: 'neutral', + title: t('defaultApp.settingsOpened.title', 'Settings Opened'), + body: t('defaultApp.settingsOpened.message', 'Please select Stirling PDF in your system settings'), + }); + } + } catch (error) { + console.error('Failed to set default:', error); + alert({ + alertType: 'error', + title: t('defaultApp.error.title', 'Error'), + body: t('defaultApp.error.message', 'Failed to set default PDF handler'), + }); + } finally { + setIsLoading(false); + } + }; + + return ( + + +
+ + {t('settings.general.defaultPdfEditor', 'Default PDF editor')} + + + {isDefault === true + ? t('settings.general.defaultPdfEditorActive', 'Stirling PDF is your default PDF editor') + : isDefault === false + ? t('settings.general.defaultPdfEditorInactive', 'Another application is set as default') + : t('settings.general.defaultPdfEditorChecking', 'Checking...')} + +
+ +
+
+ ); +}; diff --git a/frontend/src/desktop/components/shared/config/configSections/GeneralSection.tsx b/frontend/src/desktop/components/shared/config/configSections/GeneralSection.tsx new file mode 100644 index 000000000..8dfec2ee9 --- /dev/null +++ b/frontend/src/desktop/components/shared/config/configSections/GeneralSection.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Stack } from '@mantine/core'; +import CoreGeneralSection from '@core/components/shared/config/configSections/GeneralSection'; +import { DefaultAppSettings } from '@app/components/shared/config/configSections/DefaultAppSettings'; + +/** + * Desktop extension of GeneralSection that adds default PDF editor settings + */ +const GeneralSection: React.FC = () => { + return ( + + + + + ); +}; + +export default GeneralSection; diff --git a/frontend/src/desktop/hooks/useDefaultAppPrompt.ts b/frontend/src/desktop/hooks/useDefaultAppPrompt.ts deleted file mode 100644 index fe5933f6f..000000000 --- a/frontend/src/desktop/hooks/useDefaultAppPrompt.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { useState, useEffect } from 'react'; -import { defaultAppService } from '@app/services/defaultAppService'; -import { alert } from '@app/components/toast'; -import { useTranslation } from 'react-i18next'; - -export function useDefaultAppPrompt() { - const { t } = useTranslation(); - const [promptOpened, setPromptOpened] = useState(false); - const [isSettingDefault, setIsSettingDefault] = useState(false); - - // Check on mount if we should show the prompt - useEffect(() => { - const checkShouldPrompt = async () => { - try { - const shouldShow = await defaultAppService.shouldShowPrompt(); - if (shouldShow) { - // Small delay so it doesn't show immediately on app launch - setTimeout(() => setPromptOpened(true), 2000); - } - } catch (error) { - console.error('[DefaultAppPrompt] Failed to check prompt status:', error); - } - }; - - checkShouldPrompt(); - }, []); - - const handleSetDefault = async () => { - setIsSettingDefault(true); - try { - const result = await defaultAppService.setAsDefaultPdfHandler(); - - if (result === 'set_successfully') { - alert({ - alertType: 'success', - title: t('defaultApp.success.title', 'Default App Set'), - body: t( - 'defaultApp.success.message', - 'Stirling PDF is now your default PDF editor' - ), - }); - } else if (result === 'opened_settings') { - alert({ - alertType: 'neutral', - title: t('defaultApp.settingsOpened.title', 'Settings Opened'), - body: t( - 'defaultApp.settingsOpened.message', - 'Please select Stirling PDF in your system settings' - ), - }); - } - - // Mark as dismissed regardless of outcome - defaultAppService.setPromptDismissed(true); - setPromptOpened(false); - } catch (error) { - console.error('[DefaultAppPrompt] Failed to set default handler:', error); - alert({ - alertType: 'error', - title: t('defaultApp.error.title', 'Error'), - body: t( - 'defaultApp.error.message', - 'Failed to set default PDF handler' - ), - }); - } finally { - setIsSettingDefault(false); - } - }; - - const handleDismiss = () => { - defaultAppService.setPromptDismissed(true); - setPromptOpened(false); - }; - - return { - promptOpened, - isSettingDefault, - handleSetDefault, - handleDismiss, - }; -}