diff --git a/web-app/src/hooks/useAppUpdater.ts b/web-app/src/hooks/useAppUpdater.ts
index 7b4245f74..22e860f6b 100644
--- a/web-app/src/hooks/useAppUpdater.ts
+++ b/web-app/src/hooks/useAppUpdater.ts
@@ -1,6 +1,6 @@
import { isDev } from '@/lib/utils'
import { check, Update } from '@tauri-apps/plugin-updater'
-import { useState, useCallback } from 'react'
+import { useState, useCallback, useEffect } from 'react'
import { events, AppEvent } from '@janhq/core'
export interface UpdateState {
@@ -24,64 +24,125 @@ export const useAppUpdater = () => {
remindMeLater: false,
})
- const checkForUpdate = useCallback(async (resetRemindMeLater = false) => {
- try {
- // Reset remindMeLater if requested (e.g., when called from settings)
- if (resetRemindMeLater) {
- setUpdateState((prev) => ({
- ...prev,
- remindMeLater: false,
- }))
- }
-
- if (!isDev()) {
- // Production mode - use actual Tauri updater
- const update = await check()
-
- if (update) {
- setUpdateState((prev) => ({
- ...prev,
- isUpdateAvailable: true,
- updateInfo: update,
- }))
- console.log('Update available:', update.version)
- return update
- } else {
- // No update available - reset state
- setUpdateState((prev) => ({
- ...prev,
- isUpdateAvailable: false,
- updateInfo: null,
- }))
-
- return null
- }
- } else {
- setUpdateState((prev) => ({
- ...prev,
- isUpdateAvailable: false,
- updateInfo: null,
- }))
- return null
- }
- } catch (error) {
- console.error('Error checking for updates:', error)
- // Reset state on error
+ // Listen for app update state sync events
+ useEffect(() => {
+ const handleUpdateStateSync = (newState: Partial
) => {
setUpdateState((prev) => ({
...prev,
- isUpdateAvailable: false,
- updateInfo: null,
+ ...newState,
}))
- return null
+ }
+
+ events.on('onAppUpdateStateSync', handleUpdateStateSync)
+
+ return () => {
+ events.off('onAppUpdateStateSync', handleUpdateStateSync)
}
}, [])
- const setRemindMeLater = useCallback((remind: boolean) => {
- setUpdateState((prev) => ({
- ...prev,
- remindMeLater: remind,
- }))
- }, [])
+ const syncStateToOtherInstances = useCallback(
+ (partialState: Partial) => {
+ // Emit event to sync state across all useAppUpdater instances
+ events.emit('onAppUpdateStateSync', partialState)
+ },
+ []
+ )
+
+ const checkForUpdate = useCallback(
+ async (resetRemindMeLater = false) => {
+ try {
+ // Reset remindMeLater if requested (e.g., when called from settings)
+ if (resetRemindMeLater) {
+ const newState = {
+ remindMeLater: false,
+ }
+ setUpdateState((prev) => ({
+ ...prev,
+ ...newState,
+ }))
+ // Sync to other instances
+ syncStateToOtherInstances(newState)
+ }
+
+ if (!isDev()) {
+ // Production mode - use actual Tauri updater
+ const update = await check()
+
+ if (update) {
+ const newState = {
+ isUpdateAvailable: true,
+ remindMeLater: false,
+ updateInfo: update,
+ }
+ setUpdateState((prev) => ({
+ ...prev,
+ ...newState,
+ }))
+ // Sync to other instances
+ syncStateToOtherInstances(newState)
+ console.log('Update available:', update.version)
+ return update
+ } else {
+ // No update available - reset state
+ const newState = {
+ isUpdateAvailable: false,
+ updateInfo: null,
+ }
+ setUpdateState((prev) => ({
+ ...prev,
+ ...newState,
+ }))
+ // Sync to other instances
+ syncStateToOtherInstances(newState)
+ return null
+ }
+ } else {
+ const newState = {
+ isUpdateAvailable: false,
+ updateInfo: null,
+ ...(resetRemindMeLater && { remindMeLater: false }),
+ }
+ setUpdateState((prev) => ({
+ ...prev,
+ ...newState,
+ }))
+ // Sync to other instances
+ syncStateToOtherInstances(newState)
+ return null
+ }
+ } catch (error) {
+ console.error('Error checking for updates:', error)
+ // Reset state on error
+ const newState = {
+ isUpdateAvailable: false,
+ updateInfo: null,
+ }
+ setUpdateState((prev) => ({
+ ...prev,
+ ...newState,
+ }))
+ // Sync to other instances
+ syncStateToOtherInstances(newState)
+ return null
+ }
+ },
+ [syncStateToOtherInstances]
+ )
+
+ const setRemindMeLater = useCallback(
+ (remind: boolean) => {
+ const newState = {
+ remindMeLater: remind,
+ }
+ setUpdateState((prev) => ({
+ ...prev,
+ ...newState,
+ }))
+ // Sync to other instances
+ syncStateToOtherInstances(newState)
+ },
+ [syncStateToOtherInstances]
+ )
const downloadAndInstallUpdate = useCallback(async () => {
if (!updateState.updateInfo) return
diff --git a/web-app/src/routes/settings/general.tsx b/web-app/src/routes/settings/general.tsx
index 62ee09edd..ae7f45576 100644
--- a/web-app/src/routes/settings/general.tsx
+++ b/web-app/src/routes/settings/general.tsx
@@ -9,7 +9,7 @@ import LanguageSwitcher from '@/containers/LanguageSwitcher'
import { useTranslation } from 'react-i18next'
import { useGeneralSetting } from '@/hooks/useGeneralSetting'
import { useAppUpdater } from '@/hooks/useAppUpdater'
-import { useEffect, useState } from 'react'
+import { useEffect, useState, useCallback } from 'react'
import { open } from '@tauri-apps/plugin-dialog'
import { revealItemInDir } from '@tauri-apps/plugin-opener'
import ChangeDataFolderLocation from '@/containers/dialogs/ChangeDataFolderLocation'
@@ -63,7 +63,7 @@ const openFileTitle = (): string => {
function General() {
const { t } = useTranslation()
const { spellCheckChatInput, setSpellCheckChatInput } = useGeneralSetting()
- const { checkForUpdate, setRemindMeLater } = useAppUpdater()
+ const { checkForUpdate } = useAppUpdater()
const [janDataFolder, setJanDataFolder] = useState()
const [isCopied, setIsCopied] = useState(false)
const [selectedNewPath, setSelectedNewPath] = useState(null)
@@ -179,9 +179,8 @@ function General() {
}
}
- const handleCheckForUpdate = async () => {
+ const handleCheckForUpdate = useCallback(async () => {
setIsCheckingUpdate(true)
- setRemindMeLater(false)
try {
if (isDev())
return toast.info('You are running a development version of Jan!')
@@ -196,7 +195,7 @@ function General() {
} finally {
setIsCheckingUpdate(false)
}
- }
+ }, [setIsCheckingUpdate, checkForUpdate])
return (