mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-09-13 12:15:29 +02:00
UI changes to update and support auto updating (#6075)
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
919f0ade99
commit
256d1a86d2
@@ -25,6 +25,8 @@ import { SaaSTeamProvider } from "@app/contexts/SaaSTeamContext";
|
||||
import { SaasBillingProvider } from "@app/contexts/SaasBillingContext";
|
||||
import { SaaSCheckoutProvider } from "@app/contexts/SaaSCheckoutContext";
|
||||
import { CreditModalBootstrap } from "@app/components/shared/modals/CreditModalBootstrap";
|
||||
import UpdateModal from "@core/components/shared/UpdateModal";
|
||||
import { useDesktopUpdatePopup } from "@app/hooks/useDesktopUpdatePopup";
|
||||
|
||||
// Common tool endpoints to preload for faster first-use
|
||||
const COMMON_TOOL_ENDPOINTS = [
|
||||
@@ -48,6 +50,7 @@ const COMMON_TOOL_ENDPOINTS = [
|
||||
*/
|
||||
export function AppProviders({ children }: { children: ReactNode }) {
|
||||
const { isFirstLaunch, setupComplete } = useFirstLaunchCheck();
|
||||
const updatePopup = useDesktopUpdatePopup();
|
||||
const [connectionMode, setConnectionMode] = useState<
|
||||
"saas" | "selfhosted" | "local" | null
|
||||
>(null);
|
||||
@@ -264,6 +267,38 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
||||
.catch(() => {});
|
||||
}, [authChecked]);
|
||||
|
||||
// Desktop auto-update popup (shown on startup if update available)
|
||||
const { state: popupState, actions: popupActions } = updatePopup;
|
||||
const updatePopupModal = popupState.updateSummary && (
|
||||
<UpdateModal
|
||||
opened={popupState.showModal}
|
||||
onClose={popupActions.dismissModal}
|
||||
onRemindLater={popupActions.remindLater}
|
||||
currentVersion={popupState.currentVersion}
|
||||
updateSummary={popupState.updateSummary}
|
||||
machineInfo={{
|
||||
machineType: navigator.platform?.toLowerCase().includes("mac")
|
||||
? "Client-mac"
|
||||
: navigator.platform?.toLowerCase().includes("linux")
|
||||
? "Client-unix"
|
||||
: "Client-win",
|
||||
activeSecurity: false,
|
||||
licenseType: "NORMAL",
|
||||
}}
|
||||
desktopInstall={
|
||||
popupState.tauriInstallReady
|
||||
? {
|
||||
state: popupState.state,
|
||||
progress: popupState.progress,
|
||||
errorMessage: popupState.errorMessage,
|
||||
canInstall: popupState.canInstall,
|
||||
actions: popupActions,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!authChecked) {
|
||||
return (
|
||||
<ProprietaryAppProviders
|
||||
@@ -278,6 +313,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
||||
}}
|
||||
>
|
||||
<div style={{ minHeight: "100vh" }} />
|
||||
{updatePopupModal}
|
||||
</ProprietaryAppProviders>
|
||||
);
|
||||
}
|
||||
@@ -313,6 +349,8 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
||||
<DesktopOnboardingModal />
|
||||
{/* Global sign-in modal, opened via stirling:open-sign-in event */}
|
||||
<SignInModal />
|
||||
{/* Desktop auto-update popup */}
|
||||
{updatePopupModal}
|
||||
</SaaSCheckoutProvider>
|
||||
</SaasBillingProvider>
|
||||
</SaaSTeamProvider>
|
||||
|
||||
+102
-4
@@ -1,16 +1,114 @@
|
||||
import React from "react";
|
||||
import { Stack } from "@mantine/core";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Stack, Alert } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CoreGeneralSection from "@core/components/shared/config/configSections/GeneralSection";
|
||||
import { DefaultAppSettings } from "@app/components/shared/config/configSections/DefaultAppSettings";
|
||||
import { useDesktopInstall } from "@app/hooks/useDesktopInstall";
|
||||
import {
|
||||
desktopUpdateService,
|
||||
type UpdateMode,
|
||||
type UpdateModeInfo,
|
||||
} from "@app/services/desktopUpdateService";
|
||||
|
||||
/**
|
||||
* Desktop extension of GeneralSection that adds default PDF editor settings
|
||||
* Desktop extension of GeneralSection.
|
||||
*
|
||||
* Adds default PDF editor settings, wires up the Tauri auto-updater install
|
||||
* flow, and exposes the user-facing update-mode control (prompt / auto /
|
||||
* disabled). When the mode is locked by a provisioning file the control is
|
||||
* still rendered but disabled, with a "Managed by administrator" hint, so
|
||||
* managed-deployment users can see what policy is in effect.
|
||||
*/
|
||||
const GeneralSection: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const install = useDesktopInstall();
|
||||
const [updateModeInfo, setUpdateModeInfo] = useState<UpdateModeInfo>({
|
||||
mode: "prompt",
|
||||
locked: false,
|
||||
});
|
||||
const [updateModeError, setUpdateModeError] = useState<string | null>(null);
|
||||
|
||||
// Check for Tauri updater availability on mount
|
||||
useEffect(() => {
|
||||
void install.checkTauriUpdate();
|
||||
}, [install.checkTauriUpdate]);
|
||||
|
||||
// Load the current update mode + lock status on mount. We intentionally
|
||||
// re-fetch on every mount so that a provisioning file dropped while the
|
||||
// app is running (admin re-pushes config via MDM) is reflected the next
|
||||
// time the user opens Settings — the Rust side re-reads the store on
|
||||
// every call, so this is essentially a fresh read.
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
desktopUpdateService.getUpdateModeInfo().then((info) => {
|
||||
if (!cancelled) setUpdateModeInfo(info);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleUpdateModeChange = useCallback(
|
||||
async (mode: UpdateMode) => {
|
||||
setUpdateModeError(null);
|
||||
try {
|
||||
await desktopUpdateService.setUpdateMode(mode);
|
||||
// Refresh rather than optimistically updating — the Rust command
|
||||
// can refuse the change (locked) and we want the UI to reflect
|
||||
// the authoritative stored value.
|
||||
const fresh = await desktopUpdateService.getUpdateModeInfo();
|
||||
setUpdateModeInfo(fresh);
|
||||
} catch (err) {
|
||||
console.error("[GeneralSection] setUpdateMode failed:", err);
|
||||
const msg =
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: typeof err === "string"
|
||||
? err
|
||||
: t(
|
||||
"settings.general.updates.updateBehaviorErrorLocked",
|
||||
"This setting is locked by your administrator.",
|
||||
);
|
||||
setUpdateModeError(msg);
|
||||
}
|
||||
},
|
||||
[t],
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
<DefaultAppSettings />
|
||||
<CoreGeneralSection />
|
||||
{updateModeError && (
|
||||
<Alert
|
||||
color="red"
|
||||
title={t(
|
||||
"settings.general.updates.updateBehaviorError",
|
||||
"Could not change update behavior",
|
||||
)}
|
||||
withCloseButton
|
||||
onClose={() => setUpdateModeError(null)}
|
||||
>
|
||||
{updateModeError}
|
||||
</Alert>
|
||||
)}
|
||||
<CoreGeneralSection
|
||||
hideUpdateSection={
|
||||
updateModeInfo.mode === "disabled" && updateModeInfo.locked
|
||||
}
|
||||
desktopInstall={{
|
||||
state: install.state,
|
||||
progress: install.progress,
|
||||
errorMessage: install.errorMessage,
|
||||
tauriInstallReady: install.tauriInstallReady,
|
||||
canInstall: install.canInstall,
|
||||
actions: install.actions,
|
||||
}}
|
||||
desktopUpdateMode={{
|
||||
mode: updateModeInfo.mode,
|
||||
locked: updateModeInfo.locked,
|
||||
onChange: handleUpdateModeChange,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user