mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
Disable update check and notification in SaaS mode (#6863)
# Description of Changes In SaaS mode the self-hosted "Update Available" notification could still appear and the update-check code (external call to `supabase.stirling.com/functions/v1/updates`) still ran, even though the cloud owns app versioning. The web `UpdateStartupPopup` was already SaaS-gated via a null override, but two other paths were not: - **Desktop app in SaaS connection mode** - `useDesktopUpdatePopup()` ran its startup check and rendered the `UpdateModal` regardless of connection mode, so a self-hosted update popup appeared while connected to SaaS. - **Settings → General** - the core `GeneralSection` fired `checkForUpdate()` on mount unconditionally, even when the update section was hidden (as SaaS does), so the external call still ran. **What changed** - `useDesktopUpdatePopup.ts` - the startup timer now bails out immediately when `connectionModeService.getCurrentMode() === "saas"`. No mode lookup, no external fetch, no modal. - `core/GeneralSection.tsx` - the mount `checkForUpdate()` now returns early when `hideUpdateSection` is set, so hiding the section (web SaaS, managed-disabled desktop) also stops the external call. - `desktop/GeneralSection.tsx` - passes `hideUpdateSection` when `useSaaSMode()` is true, which (via the above) suppresses the settings check in desktop-SaaS too. **Why** - in SaaS the update check should never be called and no update notification should be shown; the cloud handles versioning. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details.
This commit is contained in:
@@ -112,12 +112,14 @@ const GeneralSection: React.FC<GeneralSectionProps> = ({
|
||||
// falling back to the backend version
|
||||
const currentVersion = appVersion ?? config?.appVersion ?? null;
|
||||
|
||||
// Check for updates on mount
|
||||
// Check for updates on mount — skipped when the update UI is hidden (SaaS
|
||||
// build, managed-disabled desktop) so no external update call ever fires.
|
||||
useEffect(() => {
|
||||
if (hideUpdateSection) return;
|
||||
if (currentVersion) {
|
||||
checkForUpdate();
|
||||
}
|
||||
}, [currentVersion, config?.machineType]);
|
||||
}, [currentVersion, config?.machineType, hideUpdateSection]);
|
||||
|
||||
const checkForUpdate = async () => {
|
||||
if (!currentVersion) return;
|
||||
|
||||
+6
-1
@@ -4,6 +4,7 @@ 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 { useSaaSMode } from "@app/hooks/useSaaSMode";
|
||||
import {
|
||||
desktopUpdateService,
|
||||
type UpdateMode,
|
||||
@@ -22,6 +23,9 @@ import {
|
||||
const GeneralSection: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const install = useDesktopInstall();
|
||||
// In SaaS connection mode the cloud owns app versioning — hide the update
|
||||
// section (which also stops the core auto-check from firing).
|
||||
const isSaaSMode = useSaaSMode();
|
||||
const [updateModeInfo, setUpdateModeInfo] = useState<UpdateModeInfo>({
|
||||
mode: "prompt",
|
||||
locked: false,
|
||||
@@ -93,7 +97,8 @@ const GeneralSection: React.FC = () => {
|
||||
)}
|
||||
<CoreGeneralSection
|
||||
hideUpdateSection={
|
||||
updateModeInfo.mode === "disabled" && updateModeInfo.locked
|
||||
isSaaSMode ||
|
||||
(updateModeInfo.mode === "disabled" && updateModeInfo.locked)
|
||||
}
|
||||
desktopInstall={{
|
||||
state: install.state,
|
||||
|
||||
@@ -9,6 +9,7 @@ const getVersionMock = vi.fn();
|
||||
const getUpdateModeMock = vi.fn();
|
||||
const canInstallUpdatesMock = vi.fn();
|
||||
const getUpdateSummaryMock = vi.fn();
|
||||
const getCurrentModeMock = vi.fn();
|
||||
|
||||
vi.mock("@tauri-apps/api/core", () => ({
|
||||
invoke: (cmd: string, args?: unknown) => invokeMock(cmd, args),
|
||||
@@ -36,6 +37,12 @@ vi.mock("@app/services/desktopUpdateService", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@app/services/connectionModeService", () => ({
|
||||
connectionModeService: {
|
||||
getCurrentMode: () => getCurrentModeMock(),
|
||||
},
|
||||
}));
|
||||
|
||||
import { useDesktopUpdatePopup } from "@app/hooks/useDesktopUpdatePopup";
|
||||
|
||||
/** Flush pending microtasks so awaited promises settle. */
|
||||
@@ -74,8 +81,11 @@ describe("useDesktopUpdatePopup — auto mode", () => {
|
||||
getUpdateModeMock.mockReset();
|
||||
canInstallUpdatesMock.mockReset();
|
||||
getUpdateSummaryMock.mockReset();
|
||||
getCurrentModeMock.mockReset();
|
||||
|
||||
// Defaults: auto mode, update available, install permitted.
|
||||
// Defaults: local (non-SaaS) connection, auto mode, update available,
|
||||
// install permitted.
|
||||
getCurrentModeMock.mockResolvedValue("local");
|
||||
getUpdateModeMock.mockResolvedValue("auto");
|
||||
getVersionMock.mockResolvedValue("1.0.0");
|
||||
getUpdateSummaryMock.mockResolvedValue({ latest_version: "2.0.0" });
|
||||
@@ -189,4 +199,18 @@ describe("useDesktopUpdatePopup — auto mode", () => {
|
||||
expect(invocations).toContain("download_and_install_update");
|
||||
expect(invocations).toContain("restart_app");
|
||||
});
|
||||
|
||||
it("skips the update check entirely in SaaS connection mode", async () => {
|
||||
// In SaaS mode the cloud owns versioning — the self-hosted update check
|
||||
// must never run: no mode lookup, no external summary fetch, no install.
|
||||
getCurrentModeMock.mockResolvedValue("saas");
|
||||
|
||||
await runStartup();
|
||||
|
||||
expect(getUpdateModeMock).not.toHaveBeenCalled();
|
||||
expect(getUpdateSummaryMock).not.toHaveBeenCalled();
|
||||
const invocations = invokeMock.mock.calls.map((c) => c[0]);
|
||||
expect(invocations).not.toContain("download_and_install_update");
|
||||
expect(invocations).not.toContain("restart_app");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
desktopUpdateService,
|
||||
type CanInstallResult,
|
||||
} from "@app/services/desktopUpdateService";
|
||||
import { connectionModeService } from "@app/services/connectionModeService";
|
||||
|
||||
const SNOOZE_KEY = "stirling-pdf-updater:snoozedUntil";
|
||||
const STARTUP_DELAY_MS = 15_000;
|
||||
@@ -72,6 +73,10 @@ export function useDesktopUpdatePopup() {
|
||||
hasChecked.current = true;
|
||||
|
||||
const timer = setTimeout(async () => {
|
||||
// In SaaS connection mode the cloud owns app versioning — the self-hosted
|
||||
// update check + popup must never run (no external call, no modal).
|
||||
if ((await connectionModeService.getCurrentMode()) === "saas") return;
|
||||
|
||||
let mode: Awaited<ReturnType<typeof desktopUpdateService.getUpdateMode>> =
|
||||
"prompt";
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user