Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions web-app/src/containers/dialogs/AppUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button'
import { useState, useEffect } from 'react'
import { useReleaseNotes } from '@/hooks/useReleaseNotes'
import { RenderMarkdown } from '../RenderMarkdown'
import { isDev } from '@/lib/utils'
import { cn, isDev } from '@/lib/utils'

const DialogAppUpdater = () => {
const {
Expand Down Expand Up @@ -38,12 +38,31 @@ const DialogAppUpdater = () => {
checkForUpdate()
}, [checkForUpdate])

if (updateState.remindMeLater) return null
const [appUpdateState, setAppUpdateState] = useState({
remindMeLater: false,
isUpdateAvailable: false,
})

useEffect(() => {
setAppUpdateState({
remindMeLater: updateState.remindMeLater,
isUpdateAvailable: updateState.isUpdateAvailable,
})
}, [updateState])

if (appUpdateState.remindMeLater) return null

console.log(appUpdateState)
console.log(updateState)

return (
<>
{updateState.isUpdateAvailable && (
<div className="fixed z-50 w-[400px] bottom-3 right-3 bg-main-view text-main-view-fg flex items-center justify-center border border-main-view-fg/10 rounded-lg shadow-md">
{appUpdateState.isUpdateAvailable && (
<div
className={cn(
'fixed z-50 w-[400px] bottom-3 right-3 bg-main-view text-main-view-fg flex items-center justify-center border border-main-view-fg/10 rounded-lg shadow-md'
)}
>
<div className="px-0 py-4">
<div className="px-4">
<div className="flex items-start gap-2">
Expand Down
141 changes: 101 additions & 40 deletions web-app/src/hooks/useAppUpdater.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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,
}))
}
// Listen for app update state sync events
useEffect(() => {
const handleUpdateStateSync = (newState: Partial<UpdateState>) => {
setUpdateState((prev) => ({
...prev,
...newState,
}))
}

events.on('onAppUpdateStateSync', handleUpdateStateSync)

if (!isDev()) {
// Production mode - use actual Tauri updater
const update = await check()
return () => {
events.off('onAppUpdateStateSync', handleUpdateStateSync)
}
}, [])

if (update) {
const syncStateToOtherInstances = useCallback(
(partialState: Partial<UpdateState>) => {
// 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,
isUpdateAvailable: true,
updateInfo: update,
...newState,
}))
console.log('Update available:', update.version)
return update
// 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 {
// No update available - reset state
setUpdateState((prev) => ({
...prev,
const newState = {
isUpdateAvailable: false,
updateInfo: null,
...(resetRemindMeLater && { remindMeLater: false }),
}
setUpdateState((prev) => ({
...prev,
...newState,
}))

// Sync to other instances
syncStateToOtherInstances(newState)
return null
}
} else {
setUpdateState((prev) => ({
...prev,
} 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
}
} catch (error) {
console.error('Error checking for updates:', error)
// Reset state on error
},
[syncStateToOtherInstances]
)

const setRemindMeLater = useCallback(
(remind: boolean) => {
const newState = {
remindMeLater: remind,
}
setUpdateState((prev) => ({
...prev,
isUpdateAvailable: false,
updateInfo: null,
...newState,
}))
return null
}
}, [])

const setRemindMeLater = useCallback((remind: boolean) => {
setUpdateState((prev) => ({
...prev,
remindMeLater: remind,
}))
}, [])
// Sync to other instances
syncStateToOtherInstances(newState)
},
[syncStateToOtherInstances]
)

const downloadAndInstallUpdate = useCallback(async () => {
if (!updateState.updateInfo) return
Expand Down
9 changes: 4 additions & 5 deletions web-app/src/routes/settings/general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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<string | undefined>()
const [isCopied, setIsCopied] = useState(false)
const [selectedNewPath, setSelectedNewPath] = useState<string | null>(null)
Expand Down Expand Up @@ -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!')
Expand All @@ -196,7 +195,7 @@ function General() {
} finally {
setIsCheckingUpdate(false)
}
}
}, [setIsCheckingUpdate, checkForUpdate])

return (
<div className="flex flex-col h-full">
Expand Down
Loading