update notif (#4937)

# Description of Changes
<img width="1521" height="1041" alt="image"
src="https://github.com/user-attachments/assets/2644bf70-0a9b-4c91-9046-02f555314608"
/>

<img width="1162" height="1598" alt="image"
src="https://github.com/user-attachments/assets/36d693f7-6fdd-4f2b-9db1-39ac336d9055"
/>

<img width="1220" height="1625" alt="image"
src="https://github.com/user-attachments/assets/4d4c19ea-0020-45fb-b15a-9f6ad377856c"
/>

<!--
Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)
-->

---

## Checklist

### General

- [ ] 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/devGuide/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)
- [ ] I have performed a self-review of my own code
- [ ] 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)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.
This commit is contained in:
Anthony Stirling
2025-11-19 11:54:33 +00:00
committed by GitHub
parent 87bf7a5b7f
commit 6177ccd333
8 changed files with 844 additions and 8 deletions
@@ -1,10 +1,12 @@
import React, { useState, useEffect } from 'react';
import { Paper, Stack, Switch, Text, Tooltip, NumberInput, SegmentedControl, Code, Group, Anchor, ActionIcon } from '@mantine/core';
import { Paper, Stack, Switch, Text, Tooltip, NumberInput, SegmentedControl, Code, Group, Anchor, ActionIcon, Button, Badge, Alert } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { usePreferences } from '@app/contexts/PreferencesContext';
import { useAppConfig } from '@app/contexts/AppConfigContext';
import type { ToolPanelMode } from '@app/constants/toolPanel';
import LocalIcon from '@app/components/shared/LocalIcon';
import { updateService, UpdateSummary } from '@app/services/updateService';
import UpdateModal from '@app/components/shared/UpdateModal';
const DEFAULT_AUTO_UNZIP_FILE_LIMIT = 4;
const BANNER_DISMISSED_KEY = 'stirlingpdf_features_banner_dismissed';
@@ -22,12 +24,44 @@ const GeneralSection: React.FC<GeneralSectionProps> = ({ hideTitle = false }) =>
// Check localStorage on mount
return localStorage.getItem(BANNER_DISMISSED_KEY) === 'true';
});
const [updateSummary, setUpdateSummary] = useState<UpdateSummary | null>(null);
const [updateModalOpened, setUpdateModalOpened] = useState(false);
const [checkingUpdate, setCheckingUpdate] = useState(false);
// Sync local state with preference changes
useEffect(() => {
setFileLimitInput(preferences.autoUnzipFileLimit);
}, [preferences.autoUnzipFileLimit]);
// Check for updates on mount
useEffect(() => {
if (config?.appVersion && config?.machineType) {
checkForUpdate();
}
}, [config?.appVersion, config?.machineType]);
const checkForUpdate = async () => {
if (!config?.appVersion || !config?.machineType) {
return;
}
setCheckingUpdate(true);
const machineInfo = {
machineType: config.machineType,
activeSecurity: config.activeSecurity ?? false,
licenseType: config.license ?? 'NORMAL',
};
const summary = await updateService.getUpdateSummary(config.appVersion, machineInfo);
if (summary) {
const isNewerVersion = updateService.compareVersions(summary.latest_version, config.appVersion) > 0;
if (isNewerVersion) {
setUpdateSummary(summary);
}
}
setCheckingUpdate(false);
};
// Check if login is disabled
const loginDisabled = !config?.enableLogin;
@@ -170,6 +204,108 @@ const GeneralSection: React.FC<GeneralSectionProps> = ({ hideTitle = false }) =>
</Tooltip>
</Stack>
</Paper>
{/* Update Check Section */}
{config?.appVersion && (
<Paper withBorder p="md" radius="md">
<Stack gap="md">
<div>
<Group justify="space-between" align="center">
<div>
<Text fw={600} size="sm">
{t('settings.general.updates.title', 'Software Updates')}
</Text>
<Text size="xs" c="dimmed" mt={4}>
{t('settings.general.updates.description', 'Check for updates and view version information')}
</Text>
</div>
{updateSummary && (
<Badge
color={updateSummary.max_priority === 'urgent' ? 'red' : 'blue'}
variant="filled"
>
{updateSummary.max_priority === 'urgent'
? t('update.urgentUpdateAvailable', 'Urgent Update')
: t('update.updateAvailable', 'Update Available')}
</Badge>
)}
</Group>
</div>
<Group justify="space-between" align="center">
<div>
<Text size="sm" c="dimmed">
{t('settings.general.updates.currentVersion', 'Current Version')}:{' '}
<Text component="span" fw={500}>
{config.appVersion}
</Text>
</Text>
{updateSummary && (
<Text size="sm" c="dimmed" mt={4}>
{t('settings.general.updates.latestVersion', 'Latest Version')}:{' '}
<Text component="span" fw={500} c="blue">
{updateSummary.latest_version}
</Text>
</Text>
)}
</div>
<Group gap="sm">
<Button
size="sm"
variant="default"
onClick={checkForUpdate}
loading={checkingUpdate}
leftSection={<LocalIcon icon="refresh-rounded" width="1rem" height="1rem" />}
>
{t('settings.general.updates.checkForUpdates', 'Check for Updates')}
</Button>
{updateSummary && (
<Button
size="sm"
color={updateSummary.max_priority === 'urgent' ? 'red' : 'blue'}
onClick={() => setUpdateModalOpened(true)}
leftSection={<LocalIcon icon="system-update-rounded" width="1rem" height="1rem" />}
>
{t('settings.general.updates.viewDetails', 'View Details')}
</Button>
)}
</Group>
</Group>
{updateSummary?.any_breaking && (
<Alert
color="orange"
title={t('update.breakingChangesDetected', 'Breaking Changes Detected')}
styles={{
title: { fontWeight: 600 }
}}
>
<Text size="sm">
{t(
'update.breakingChangesMessage',
'Some versions contain breaking changes. Please review the migration guides before updating.'
)}
</Text>
</Alert>
)}
</Stack>
</Paper>
)}
{/* Update Modal */}
{updateSummary && config?.appVersion && config?.machineType && (
<UpdateModal
opened={updateModalOpened}
onClose={() => setUpdateModalOpened(false)}
currentVersion={config.appVersion}
updateSummary={updateSummary}
machineInfo={{
machineType: config.machineType,
activeSecurity: config.activeSecurity ?? false,
licenseType: config.license ?? 'NORMAL',
}}
/>
)}
</Stack>
);
};