fix: auto-install updates in background and use updateService in AboutSection#46
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enhances the update mechanism by implementing automatic background updates with progress feedback. The changes streamline the update workflow to automatically download, install, and relaunch when updates are detected in the background, while manual checks continue to show a dialog for user confirmation.
Key Changes:
- Background update checks now automatically download and install updates with toast notifications for progress tracking
- AboutSection refactored to use the centralized
updateService.checkForUpdatesManually()method instead of duplicated update logic - Documentation updated to reflect the new auto-install behavior
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/services/updateService.ts | Added autoInstallUpdate() method for silent background updates with progress toasts; updated doc comments to describe auto-install behavior |
| src/components/sections/AboutSection.tsx | Removed duplicate update check logic and tauri-apps/plugin-updater import; now delegates to updateService.checkForUpdatesManually() |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/services/updateService.ts
Outdated
| toast.info(`Downloading update ${update.version}...`, { | ||
| id: toastId, | ||
| duration: Infinity | ||
| }); | ||
|
|
||
| await update.downloadAndInstall((event) => { | ||
| if (event.event === 'Started' && event.data.contentLength) { | ||
| toast.info(`Downloading update ${update.version}...`, { | ||
| id: toastId, | ||
| duration: Infinity | ||
| }); | ||
| } else if (event.event === 'Finished') { | ||
| toast.info('Installing update, app will restart...', { | ||
| id: toastId, | ||
| duration: Infinity | ||
| }); | ||
| } | ||
| }); |
There was a problem hiding this comment.
The initial toast at line 138 is immediately replaced by the identical toast at line 145 when the 'Started' event fires. This creates redundant toast notifications. Consider removing the initial toast and relying solely on the event handler to show the first download notification.
src/services/updateService.ts
Outdated
| } | ||
| }); | ||
|
|
||
| await relaunch(); |
There was a problem hiding this comment.
If relaunch() fails, the error will be caught by the catch block but the error message 'Update failed' may be misleading since the update itself succeeded but only the relaunch failed. Consider adding more specific error handling around the relaunch call to provide clearer feedback to users.
PR Review: Auto-install Updates in Background✅ Overall AssessmentThis is a solid improvement that enhances the user experience by automating the update process. The code is well-structured and follows the project's conventions. 🎯 Code Quality & Best PracticesStrengths:
Suggestions:
const handleCheckUpdate = async () => {
setIsCheckingUpdate(true);
await updateService.checkForUpdatesManually();
setIsCheckingUpdate(false);
};Consider wrapping in try-catch to ensure const handleCheckUpdate = async () => {
setIsCheckingUpdate(true);
try {
await updateService.checkForUpdatesManually();
} finally {
setIsCheckingUpdate(false);
}
};
await update.downloadAndInstall((event) => {
if (event.event === 'Started' && event.data.contentLength) {
toast.info(`Downloading update ${update.version}...`, {
id: toastId,
duration: Infinity
});
} else if (event.event === 'Progress') {
const percent = Math.round((event.data.chunkLength / event.data.contentLength) * 100);
toast.info(`Downloading update ${update.version}... ${percent}%`, {
id: toastId,
duration: Infinity
});
} else if (event.event === 'Finished') {
toast.info('Installing update, app will restart...', {
id: toastId,
duration: Infinity
});
}
});🐛 Potential IssuesCritical:
🔒 Security ConcernsLow severity:
⚡ Performance ConsiderationsMinor:
🧪 Test CoverageConcerns:
Recommendations:
📝 DocumentationGood:
Suggestions:
🎯 Recommendations SummaryBefore merge:
Future improvements:
✨ Positive Notes
Status: ✅ Approve with minor suggestions - the concerns about auto-install behavior should be addressed, but the code quality is good overall. |
- Add tauri-plugin-notification for system notifications - Defer auto-updates during recording/transcribing sessions - Retry up to 3 times at 30s intervals when session is active - Show 'Updated to latest version' toast after relaunch - Send system notification when update waits for session to end - Verify backend state before relaunch for safety
Pull Request Review: Auto-Install Updates in BackgroundSummaryThis PR adds automatic update downloading and installation with smart session detection to prevent interrupting active recordings. The implementation is well-thought-out with good error handling and user experience considerations. ✅ Strengths1. Excellent User Experience Design
2. Good Error Handling
3. Clean Architecture
🐛 Potential Issues1. Race Condition in Session State Tracking (Medium Priority)Location: Issue: The session state is tracked via frontend state, but there's a timing gap: // In useRecording.ts
useEffect(() => {
const isActive = state !== 'idle' && state !== 'error';
updateService.setSessionActive(isActive);
}, [state]);Problem:
Recommendation: // Add session state change listener in updateService
private sessionChangeCallbacks: Array<() => void> = [];
setSessionActive(active: boolean): void {
const wasActive = this.isSessionActive;
this.isSessionActive = active;
// Only trigger callbacks on actual state change
if (wasActive && !active && this.pendingRelaunch) {
this.pendingRelaunch = false;
this.performRelaunch();
}
}2. Missing Tauri Command Registration (Critical)Location: Issue: The code calls Found in: Verification needed: # Check if command is registered
grep -r "get_current_recording_state" src-tauri/src/lib.rsIf not registered, add to invoke handler list. 3. Unhandled Promise in setSessionActive (Low Priority)Location: Issue: if (!active && this.pendingRelaunch) {
this.pendingRelaunch = false;
this.performRelaunch(); // async function not awaited
}This is in a synchronous function calling an async function without error handling. Recommendation: if (!active && this.pendingRelaunch) {
this.pendingRelaunch = false;
this.performRelaunch().catch(error => {
console.error('Failed to relaunch after session ended:', error);
toast.error('Update installed. Please restart manually.');
});
}4. localStorage Dependency (Low Priority)Location: Issue: Using localStorage for cross-session state may fail in some environments or if storage is cleared. Recommendation: Consider using Tauri's store plugin (already in use elsewhere) for persistence: // Use app.store instead of localStorage for cross-session state
const store = app.store('update-state');
store.set('just_updated', 'true');🔒 Security Considerations1. Update Verification ✅
2. Notification Permissions ✅
3. No User Data Exposure ✅
⚡ Performance Considerations1. Memory Leak Risk (Low Priority)Location: Issue: Multiple setTimeout calls could accumulate if session stays active: setTimeout(() => this.autoInstallUpdate(update, retryCount + 1), RETRY_DELAY);Recommendation: Store timeout IDs and clear on cleanup: private pendingTimeouts: Set<number> = new Set();
private scheduleRetry(update: Update, retryCount: number): void {
const timeoutId = window.setTimeout(() => {
this.pendingTimeouts.delete(timeoutId);
this.autoInstallUpdate(update, retryCount + 1);
}, RETRY_DELAY);
this.pendingTimeouts.add(timeoutId);
}
dispose(): void {
// Clear pending retries
this.pendingTimeouts.forEach(clearTimeout);
this.pendingTimeouts.clear();
// ... existing cleanup
}2. Toast Duration: Infinity (Low Priority)Location: Issue: Toasts with infinite duration could confuse users if app doesn't relaunch quickly. Recommendation: Use reasonable max duration (30s) and update message: toast.info('Downloading update (this may take a minute)...', {
id: toastId,
duration: 30000
});🧪 Test CoverageMissing TestsThe
Recommendation: Add describe('UpdateService', () => {
describe('Session State Management', () => {
it('should defer relaunch when session is active', async () => {
// Test implementation
});
it('should relaunch when session ends with pending update', async () => {
// Test implementation
});
});
// ... more test suites
});📋 Code QualityPositive
Improvements Needed
Suggested refactor: private static readonly MAX_SESSION_RETRIES = 3;
private static readonly SESSION_RETRY_DELAY_MS = 30000;
private static readonly TOAST_MAX_DURATION_MS = 30000;🎯 Recommendations SummaryMust Fix (Before Merge)
Should Fix (Soon)
Consider for Future
✅ Final VerdictRecommendation: Approve with Minor Changes This is a solid implementation with good UX design and error handling. The main concerns are:
The code quality is high and follows project conventions well. Once the test coverage is added and the unhandled promise is fixed, this is ready to merge. Great work on the session-aware update system! 🚀 |
Changes
updateService.checkForUpdatesManually()instead of duplicate logicTesting