Add app update engine handling and UI enhancements
- Introduced `getAppEngine` method in the Electron context to retrieve the current app update engine. - Updated the Settings component to manage and display the app update engine, allowing users to select between 'Native' and 'Chromium'. - Enhanced the app update logic to persist the selected engine and ensure proper handling during updates. - Improved UI in the NewAppUpdate component to show the engine type associated with updates. - Refactored app update settings management to include engine normalization and validation.
This commit is contained in:
parent
6b9e4bff63
commit
998084160f
@ -88,6 +88,16 @@ const NewAppUpdate = ({ update, onCancel, onUpdate }) => {
|
|||||||
<Text style={{ margin: 0 }} type='secondary'>
|
<Text style={{ margin: 0 }} type='secondary'>
|
||||||
Branch: <Text>{update?.branch || 'Unknown'}</Text>
|
Branch: <Text>{update?.branch || 'Unknown'}</Text>
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text style={{ margin: 0 }} type='secondary'>
|
||||||
|
Engine:{' '}
|
||||||
|
<Text>
|
||||||
|
{update?.engine === 'chromium'
|
||||||
|
? 'Chromium'
|
||||||
|
: update?.engine === 'native'
|
||||||
|
? 'Native'
|
||||||
|
: 'Unknown'}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Dropdown menu={actionsMenu}>
|
<Dropdown menu={actionsMenu}>
|
||||||
<Button size='small' type='text'>
|
<Button size='small' type='text'>
|
||||||
|
|||||||
@ -5,6 +5,10 @@ import { useThemeContext } from '../context/ThemeContext'
|
|||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import { ElectronContext } from '../context/ElectronContext'
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
|
import {
|
||||||
|
normalizeAppUpdateEngine,
|
||||||
|
useAppUpdateContext
|
||||||
|
} from '../context/AppUpdateContext'
|
||||||
import { useMessageContext } from '../context/MessageContext'
|
import { useMessageContext } from '../context/MessageContext'
|
||||||
import useCollapseState from '../hooks/useCollapseState'
|
import useCollapseState from '../hooks/useCollapseState'
|
||||||
import InfoCollapse from '../common/InfoCollapse'
|
import InfoCollapse from '../common/InfoCollapse'
|
||||||
@ -14,13 +18,22 @@ import EditButtons from '../common/EditButtons'
|
|||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
const { Option } = Select
|
const { Option } = Select
|
||||||
const DEFAULT_UPDATE_BRANCH = 'main'
|
const DEFAULT_UPDATE_BRANCH = 'main'
|
||||||
|
const DEFAULT_UPDATE_ENGINE = 'native'
|
||||||
|
|
||||||
|
const engineLabel = (engine) => {
|
||||||
|
const normalized = normalizeAppUpdateEngine(engine)
|
||||||
|
if (normalized === 'chromium') return 'Chromium'
|
||||||
|
if (normalized === 'native') return 'Native'
|
||||||
|
return 'Not configured'
|
||||||
|
}
|
||||||
|
|
||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
const { isDarkMode, isCompact, isSystem, setThemeMode, setDensityMode } =
|
const { isDarkMode, isCompact, isSystem, setThemeMode, setDensityMode } =
|
||||||
useThemeContext()
|
useThemeContext()
|
||||||
const { fetchAppUpdateBranches } = useContext(ApiServerContext)
|
const { fetchAppUpdateBranches } = useContext(ApiServerContext)
|
||||||
const { isElectron, getAppSettings, setAppSettings } =
|
const { isElectron, getAppSettings, setAppSettings, getAppEngine } =
|
||||||
useContext(ElectronContext)
|
useContext(ElectronContext)
|
||||||
|
const { recheckForUpdates } = useAppUpdateContext()
|
||||||
const { userProfile, setUserProfile } = useContext(AuthContext)
|
const { userProfile, setUserProfile } = useContext(AuthContext)
|
||||||
const { showSuccess, showError } = useMessageContext()
|
const { showSuccess, showError } = useMessageContext()
|
||||||
const [collapseState, updateCollapseState] = useCollapseState('Settings', {
|
const [collapseState, updateCollapseState] = useCollapseState('Settings', {
|
||||||
@ -34,20 +47,31 @@ const Settings = () => {
|
|||||||
const [draftSettings, setDraftSettings] = useState({})
|
const [draftSettings, setDraftSettings] = useState({})
|
||||||
const [branches, setBranches] = useState([])
|
const [branches, setBranches] = useState([])
|
||||||
const [branchLoading, setBranchLoading] = useState(false)
|
const [branchLoading, setBranchLoading] = useState(false)
|
||||||
|
const [runningEngine, setRunningEngine] = useState(DEFAULT_UPDATE_ENGINE)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSettings = async () => {
|
const loadSettings = async () => {
|
||||||
setSettingsLoading(true)
|
setSettingsLoading(true)
|
||||||
const storedSettings = isElectron
|
const [storedSettings, detectedEngine] = await Promise.all([
|
||||||
? await getAppSettings()
|
isElectron ? getAppSettings() : Promise.resolve(userProfile?.settings || {}),
|
||||||
: userProfile?.settings || {}
|
isElectron ? getAppEngine() : Promise.resolve(DEFAULT_UPDATE_ENGINE)
|
||||||
setAppSettingsState(storedSettings || {})
|
])
|
||||||
setDraftSettings(storedSettings || {})
|
const nextEngine =
|
||||||
|
normalizeAppUpdateEngine(detectedEngine) || DEFAULT_UPDATE_ENGINE
|
||||||
|
setRunningEngine(nextEngine)
|
||||||
|
|
||||||
|
const nextSettings = { ...(storedSettings || {}) }
|
||||||
|
if (isElectron && !normalizeAppUpdateEngine(nextSettings.appUpdateEngine)) {
|
||||||
|
nextSettings.appUpdateEngine = nextEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
setAppSettingsState(nextSettings)
|
||||||
|
setDraftSettings(nextSettings)
|
||||||
setSettingsLoading(false)
|
setSettingsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSettings()
|
loadSettings()
|
||||||
}, [getAppSettings, isElectron, userProfile?.settings])
|
}, [getAppEngine, getAppSettings, isElectron, userProfile?.settings])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (settingsLoading || isEditing) return
|
if (settingsLoading || isEditing) return
|
||||||
@ -118,12 +142,17 @@ const Settings = () => {
|
|||||||
(branches.includes(DEFAULT_UPDATE_BRANCH) ? DEFAULT_UPDATE_BRANCH : null) ||
|
(branches.includes(DEFAULT_UPDATE_BRANCH) ? DEFAULT_UPDATE_BRANCH : null) ||
|
||||||
branches[0] ||
|
branches[0] ||
|
||||||
'Not configured'
|
'Not configured'
|
||||||
|
const currentEngine =
|
||||||
|
normalizeAppUpdateEngine(appSettings.appUpdateEngine) ||
|
||||||
|
normalizeAppUpdateEngine(runningEngine) ||
|
||||||
|
DEFAULT_UPDATE_ENGINE
|
||||||
|
|
||||||
const startEditing = () => {
|
const startEditing = () => {
|
||||||
setDraftSettings({
|
setDraftSettings({
|
||||||
...appSettings,
|
...appSettings,
|
||||||
appUpdateBranch:
|
appUpdateBranch:
|
||||||
currentBranch === 'Not configured' ? undefined : currentBranch,
|
currentBranch === 'Not configured' ? undefined : currentBranch,
|
||||||
|
appUpdateEngine: currentEngine,
|
||||||
theme: currentThemeValue,
|
theme: currentThemeValue,
|
||||||
density: currentDensityValue
|
density: currentDensityValue
|
||||||
})
|
})
|
||||||
@ -139,12 +168,18 @@ const Settings = () => {
|
|||||||
setSaving(true)
|
setSaving(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const nextEngine =
|
||||||
|
normalizeAppUpdateEngine(draftSettings.appUpdateEngine) ||
|
||||||
|
currentEngine
|
||||||
const nextSettings = {
|
const nextSettings = {
|
||||||
...appSettings,
|
...appSettings,
|
||||||
theme: draftSettings.theme,
|
theme: draftSettings.theme,
|
||||||
density: draftSettings.density,
|
density: draftSettings.density,
|
||||||
...(isElectron
|
...(isElectron
|
||||||
? { appUpdateBranch: draftSettings.appUpdateBranch }
|
? {
|
||||||
|
appUpdateBranch: draftSettings.appUpdateBranch,
|
||||||
|
appUpdateEngine: nextEngine
|
||||||
|
}
|
||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
const saved = isElectron
|
const saved = isElectron
|
||||||
@ -173,6 +208,16 @@ const Settings = () => {
|
|||||||
setDraftSettings(nextSettings)
|
setDraftSettings(nextSettings)
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
showSuccess('Settings saved.')
|
showSuccess('Settings saved.')
|
||||||
|
|
||||||
|
const appUpdateSettingsChanged =
|
||||||
|
isElectron &&
|
||||||
|
(nextSettings.appUpdateBranch !== appSettings.appUpdateBranch ||
|
||||||
|
nextSettings.appUpdateEngine !==
|
||||||
|
normalizeAppUpdateEngine(appSettings.appUpdateEngine))
|
||||||
|
|
||||||
|
if (appUpdateSettingsChanged) {
|
||||||
|
void recheckForUpdates()
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
}
|
}
|
||||||
@ -196,7 +241,9 @@ const Settings = () => {
|
|||||||
startEditing={startEditing}
|
startEditing={startEditing}
|
||||||
formValid={
|
formValid={
|
||||||
Boolean(draftSettings.theme && draftSettings.density) &&
|
Boolean(draftSettings.theme && draftSettings.density) &&
|
||||||
(!isElectron || Boolean(draftSettings.appUpdateBranch))
|
(!isElectron ||
|
||||||
|
(Boolean(draftSettings.appUpdateBranch) &&
|
||||||
|
Boolean(normalizeAppUpdateEngine(draftSettings.appUpdateEngine))))
|
||||||
}
|
}
|
||||||
disabled={settingsLoading || (!isElectron && !userProfile)}
|
disabled={settingsLoading || (!isElectron && !userProfile)}
|
||||||
loading={saving}
|
loading={saving}
|
||||||
@ -297,6 +344,29 @@ const Settings = () => {
|
|||||||
<Text>{currentBranch}</Text>
|
<Text>{currentBranch}</Text>
|
||||||
)}
|
)}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Engine'>
|
||||||
|
{isEditing ? (
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
normalizeAppUpdateEngine(draftSettings.appUpdateEngine) ||
|
||||||
|
currentEngine
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
setDraftSettings((previous) => ({
|
||||||
|
...previous,
|
||||||
|
appUpdateEngine: value
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder='Select an engine'
|
||||||
|
>
|
||||||
|
<Option value='native'>Native</Option>
|
||||||
|
<Option value='chromium'>Chromium</Option>
|
||||||
|
</Select>
|
||||||
|
) : (
|
||||||
|
<Text>{engineLabel(currentEngine)}</Text>
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -20,10 +20,22 @@ import SoftwareUpdateIcon from '../../Icons/SoftwareUpdateIcon'
|
|||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1000
|
const UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1000
|
||||||
|
const DEFAULT_MODEL_WIDTH = 710
|
||||||
const DEFAULT_UPDATE_BRANCH = 'main'
|
const DEFAULT_UPDATE_BRANCH = 'main'
|
||||||
|
const DEFAULT_UPDATE_ENGINE = 'native'
|
||||||
const CURRENT_BUILD_NUMBER = import.meta.env.VITE_BUILD_NUMBER
|
const CURRENT_BUILD_NUMBER = import.meta.env.VITE_BUILD_NUMBER
|
||||||
const APP_UPDATE_DISMISSED_KEY = 'appUpdateDismissed'
|
const APP_UPDATE_DISMISSED_KEY = 'appUpdateDismissed'
|
||||||
|
|
||||||
|
export const normalizeAppUpdateEngine = (engine) => {
|
||||||
|
const value = String(engine || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
|
||||||
|
if (value === 'chromium' || value === 'cef') return 'chromium'
|
||||||
|
if (value === 'native') return 'native'
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const getDismissedUpdate = () => {
|
const getDismissedUpdate = () => {
|
||||||
try {
|
try {
|
||||||
const stored = sessionStorage.getItem(APP_UPDATE_DISMISSED_KEY)
|
const stored = sessionStorage.getItem(APP_UPDATE_DISMISSED_KEY)
|
||||||
@ -42,7 +54,9 @@ const isUpdateDismissed = (update) => {
|
|||||||
return (
|
return (
|
||||||
dismissed.version === update.version &&
|
dismissed.version === update.version &&
|
||||||
dismissed.buildNumber === update.buildNumber &&
|
dismissed.buildNumber === update.buildNumber &&
|
||||||
dismissed.branch === update.branch
|
dismissed.branch === update.branch &&
|
||||||
|
normalizeAppUpdateEngine(dismissed.engine) ===
|
||||||
|
normalizeAppUpdateEngine(update.engine)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +68,8 @@ const saveDismissedUpdate = (update) => {
|
|||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
version: update.version,
|
version: update.version,
|
||||||
buildNumber: update.buildNumber,
|
buildNumber: update.buildNumber,
|
||||||
branch: update.branch
|
branch: update.branch,
|
||||||
|
engine: normalizeAppUpdateEngine(update.engine) || DEFAULT_UPDATE_ENGINE
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -109,8 +124,14 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
const { fetchAppUpdateBranches, fetchAppUpdateCurrent } =
|
const { fetchAppUpdateBranches, fetchAppUpdateCurrent } =
|
||||||
useContext(ApiServerContext)
|
useContext(ApiServerContext)
|
||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
const { isElectron, getAppSettings, startAppUpdate, onAppUpdateProgress } =
|
const {
|
||||||
useContext(ElectronContext)
|
isElectron,
|
||||||
|
getAppSettings,
|
||||||
|
setAppSettings,
|
||||||
|
getAppEngine,
|
||||||
|
startAppUpdate,
|
||||||
|
onAppUpdateProgress
|
||||||
|
} = useContext(ElectronContext)
|
||||||
const [checking, setChecking] = useState(false)
|
const [checking, setChecking] = useState(false)
|
||||||
const [noUpdateOpen, setNoUpdateOpen] = useState(false)
|
const [noUpdateOpen, setNoUpdateOpen] = useState(false)
|
||||||
const [availableUpdate, setAvailableUpdate] = useState(null)
|
const [availableUpdate, setAvailableUpdate] = useState(null)
|
||||||
@ -120,12 +141,14 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
const runningCheckRef = useRef(null)
|
const runningCheckRef = useRef(null)
|
||||||
const updateCheckDependenciesRef = useRef({})
|
const updateCheckDependenciesRef = useRef({})
|
||||||
|
|
||||||
const [modelWidth, setModelWidth] = useState(650)
|
const [modelWidth, setModelWidth] = useState(DEFAULT_MODEL_WIDTH)
|
||||||
|
|
||||||
updateCheckDependenciesRef.current = {
|
updateCheckDependenciesRef.current = {
|
||||||
fetchAppUpdateBranches,
|
fetchAppUpdateBranches,
|
||||||
fetchAppUpdateCurrent,
|
fetchAppUpdateCurrent,
|
||||||
getAppSettings,
|
getAppSettings,
|
||||||
|
setAppSettings,
|
||||||
|
getAppEngine,
|
||||||
isElectron,
|
isElectron,
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
@ -135,6 +158,8 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
fetchAppUpdateBranches,
|
fetchAppUpdateBranches,
|
||||||
fetchAppUpdateCurrent,
|
fetchAppUpdateCurrent,
|
||||||
getAppSettings,
|
getAppSettings,
|
||||||
|
setAppSettings,
|
||||||
|
getAppEngine,
|
||||||
isElectron,
|
isElectron,
|
||||||
token
|
token
|
||||||
} = updateCheckDependenciesRef.current
|
} = updateCheckDependenciesRef.current
|
||||||
@ -143,9 +168,10 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
if (runningCheckRef.current) return runningCheckRef.current
|
if (runningCheckRef.current) return runningCheckRef.current
|
||||||
|
|
||||||
const checkPromise = (async () => {
|
const checkPromise = (async () => {
|
||||||
const [branches, appSettings] = await Promise.all([
|
const [branches, appSettings, runningEngine] = await Promise.all([
|
||||||
fetchAppUpdateBranches(),
|
fetchAppUpdateBranches(),
|
||||||
getAppSettings()
|
getAppSettings(),
|
||||||
|
getAppEngine()
|
||||||
])
|
])
|
||||||
const configuredBranch = appSettings?.appUpdateBranch
|
const configuredBranch = appSettings?.appUpdateBranch
|
||||||
const defaultBranch = branches.includes(DEFAULT_UPDATE_BRANCH)
|
const defaultBranch = branches.includes(DEFAULT_UPDATE_BRANCH)
|
||||||
@ -157,11 +183,52 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
|
|
||||||
if (!selectedBranch) return null
|
if (!selectedBranch) return null
|
||||||
|
|
||||||
const update = await fetchAppUpdateCurrent(selectedBranch)
|
const selectedEngine =
|
||||||
|
normalizeAppUpdateEngine(appSettings?.appUpdateEngine) ||
|
||||||
|
normalizeAppUpdateEngine(runningEngine) ||
|
||||||
|
DEFAULT_UPDATE_ENGINE
|
||||||
|
const currentRunningEngine =
|
||||||
|
normalizeAppUpdateEngine(runningEngine) || DEFAULT_UPDATE_ENGINE
|
||||||
|
|
||||||
return isAppUpdateAvailable(update, appVersion, CURRENT_BUILD_NUMBER)
|
const settingsUpdates = {}
|
||||||
? update
|
if (!appSettings?.appUpdateRunningBranch) {
|
||||||
: null
|
settingsUpdates.appUpdateRunningBranch = selectedBranch
|
||||||
|
}
|
||||||
|
if (!normalizeAppUpdateEngine(appSettings?.appUpdateEngine)) {
|
||||||
|
settingsUpdates.appUpdateEngine = selectedEngine
|
||||||
|
}
|
||||||
|
if (Object.keys(settingsUpdates).length > 0) {
|
||||||
|
await setAppSettings({
|
||||||
|
...appSettings,
|
||||||
|
...settingsUpdates
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const runningBranch =
|
||||||
|
appSettings?.appUpdateRunningBranch ||
|
||||||
|
settingsUpdates.appUpdateRunningBranch ||
|
||||||
|
selectedBranch
|
||||||
|
|
||||||
|
const update = await fetchAppUpdateCurrent(selectedBranch)
|
||||||
|
if (!update) return null
|
||||||
|
|
||||||
|
const newerVersionAvailable = isAppUpdateAvailable(
|
||||||
|
update,
|
||||||
|
appVersion,
|
||||||
|
CURRENT_BUILD_NUMBER
|
||||||
|
)
|
||||||
|
const engineMismatch = selectedEngine !== currentRunningEngine
|
||||||
|
const branchMismatch = selectedBranch !== runningBranch
|
||||||
|
|
||||||
|
if (!newerVersionAvailable && !engineMismatch && !branchMismatch) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...update,
|
||||||
|
branch: update.branch || selectedBranch,
|
||||||
|
engine: selectedEngine
|
||||||
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
runningCheckRef.current = checkPromise
|
runningCheckRef.current = checkPromise
|
||||||
@ -181,6 +248,7 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
setNoUpdateOpen(false)
|
setNoUpdateOpen(false)
|
||||||
setAvailableUpdate(update)
|
setAvailableUpdate(update)
|
||||||
if (forcePrompt || !isUpdateDismissed(update)) {
|
if (forcePrompt || !isUpdateDismissed(update)) {
|
||||||
|
setModelWidth(DEFAULT_MODEL_WIDTH)
|
||||||
setUpdatePromptOpen(true)
|
setUpdatePromptOpen(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,6 +273,11 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}, [isElectron, showUpdateIfAvailable])
|
}, [isElectron, showUpdateIfAvailable])
|
||||||
|
|
||||||
|
const recheckForUpdates = useCallback(async () => {
|
||||||
|
if (!isElectron) return null
|
||||||
|
return showUpdateIfAvailable({ forcePrompt: true })
|
||||||
|
}, [isElectron, showUpdateIfAvailable])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isElectron) return undefined
|
if (!isElectron) return undefined
|
||||||
|
|
||||||
@ -252,7 +325,15 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await startAppUpdate(update)
|
const appSettings = await getAppSettings()
|
||||||
|
const engine =
|
||||||
|
normalizeAppUpdateEngine(update?.engine) ||
|
||||||
|
normalizeAppUpdateEngine(appSettings?.appUpdateEngine) ||
|
||||||
|
DEFAULT_UPDATE_ENGINE
|
||||||
|
const result = await startAppUpdate({
|
||||||
|
...update,
|
||||||
|
engine
|
||||||
|
})
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -273,7 +354,9 @@ export const AppUpdateProvider = ({ children }) => {
|
|||||||
Boolean(installingUpdate) && updateProgress?.phase !== 'error'
|
Boolean(installingUpdate) && updateProgress?.phase !== 'error'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppUpdateContext.Provider value={{ availableUpdate, checkForUpdates }}>
|
<AppUpdateContext.Provider
|
||||||
|
value={{ availableUpdate, checkForUpdates, recheckForUpdates }}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
<Modal
|
<Modal
|
||||||
open={checking}
|
open={checking}
|
||||||
|
|||||||
@ -369,6 +369,15 @@ const ElectronProvider = ({ children }) => {
|
|||||||
return await ipcRenderer.invoke('electron-version')
|
return await ipcRenderer.invoke('electron-version')
|
||||||
}, [electronAvailable, useElectrobun])
|
}, [electronAvailable, useElectrobun])
|
||||||
|
|
||||||
|
const getAppEngine = useCallback(async () => {
|
||||||
|
if (!electronAvailable) return 'native'
|
||||||
|
if (useElectrobun) {
|
||||||
|
const engine = await desktopBridge.getAppEngine()
|
||||||
|
return engine === 'chromium' ? 'chromium' : 'native'
|
||||||
|
}
|
||||||
|
return 'native'
|
||||||
|
}, [electronAvailable, useElectrobun])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElectronContext.Provider
|
<ElectronContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@ -390,7 +399,8 @@ const ElectronProvider = ({ children }) => {
|
|||||||
setToken,
|
setToken,
|
||||||
resizeSpotlightWindow,
|
resizeSpotlightWindow,
|
||||||
setSidebarViewMenu,
|
setSidebarViewMenu,
|
||||||
getElectronVersion
|
getElectronVersion,
|
||||||
|
getAppEngine
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Utils } from "electrobun/bun";
|
|||||||
import { launchMacInstaller } from "./macappupdate.js";
|
import { launchMacInstaller } from "./macappupdate.js";
|
||||||
import { launchWindowsInstaller } from "./winappupdate.js";
|
import { launchWindowsInstaller } from "./winappupdate.js";
|
||||||
import { scheduleAppRestart } from "./updater-runner.js";
|
import { scheduleAppRestart } from "./updater-runner.js";
|
||||||
|
import { getAppSettings, setAppSettings } from "./store.js";
|
||||||
|
|
||||||
const SUPPORTED_TARGETS = {
|
const SUPPORTED_TARGETS = {
|
||||||
darwin: {
|
darwin: {
|
||||||
@ -20,6 +21,8 @@ const SUPPORTED_TARGETS = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_UPDATE_ENGINE = "native";
|
||||||
|
|
||||||
let runningUpdate = null;
|
let runningUpdate = null;
|
||||||
|
|
||||||
const getArtifactName = (artifact) =>
|
const getArtifactName = (artifact) =>
|
||||||
@ -31,6 +34,33 @@ const normalizeArch = (arch) => {
|
|||||||
return arch;
|
return arch;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeEngine = (engine) => {
|
||||||
|
const value = String(engine || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
if (value === "chromium" || value === "cef") return "chromium";
|
||||||
|
if (value === "native") return "native";
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const artifactIsChromium = (artifact) => {
|
||||||
|
const explicit = String(
|
||||||
|
artifact?.engine || artifact?.renderer || "",
|
||||||
|
).toLowerCase();
|
||||||
|
|
||||||
|
if (explicit === "cef" || explicit === "chromium") return true;
|
||||||
|
if (explicit === "native") return false;
|
||||||
|
|
||||||
|
const name = getArtifactName(artifact).toLowerCase();
|
||||||
|
return /[-_.]cef(?:[-_.]|$)/.test(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const artifactMatchesEngine = (artifact, engine) => {
|
||||||
|
const wantsChromium = normalizeEngine(engine) === "chromium";
|
||||||
|
return artifactIsChromium(artifact) === wantsChromium;
|
||||||
|
};
|
||||||
|
|
||||||
const artifactMatchesPlatform = (artifact, target, platform, arch) => {
|
const artifactMatchesPlatform = (artifact, target, platform, arch) => {
|
||||||
const name = getArtifactName(artifact).toLowerCase();
|
const name = getArtifactName(artifact).toLowerCase();
|
||||||
const normalizedArch = normalizeArch(arch);
|
const normalizedArch = normalizeArch(arch);
|
||||||
@ -69,22 +99,23 @@ const selectUpdateArtifact = (
|
|||||||
throw new Error(`App updates are not supported on ${platform}.`);
|
throw new Error(`App updates are not supported on ${platform}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const engine =
|
||||||
|
normalizeEngine(update?.engine) || DEFAULT_UPDATE_ENGINE;
|
||||||
const artifacts = Array.isArray(update?.artifacts) ? update.artifacts : [];
|
const artifacts = Array.isArray(update?.artifacts) ? update.artifacts : [];
|
||||||
const matchingArtifact = artifacts.find((artifact) =>
|
const matchingArtifact = artifacts.find(
|
||||||
artifactMatchesPlatform(artifact, target, platform, arch),
|
(artifact) =>
|
||||||
|
artifactMatchesPlatform(artifact, target, platform, arch) &&
|
||||||
|
artifactMatchesEngine(artifact, engine),
|
||||||
);
|
);
|
||||||
const fallbackArtifact = artifacts.find((artifact) => {
|
|
||||||
const name = getArtifactName(artifact).toLowerCase();
|
|
||||||
return artifact?.url && name.endsWith(target.extension);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!matchingArtifact && !fallbackArtifact) {
|
if (!matchingArtifact) {
|
||||||
|
const engineLabel = engine === "chromium" ? "Chromium (cef)" : "Native";
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No ${target.extension} update artifact found for ${platform}/${arch}.`,
|
`No ${target.extension} ${engineLabel} update artifact found for ${platform}/${arch}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingArtifact || fallbackArtifact;
|
return matchingArtifact;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getInstallErrorMessage = (error, output = "") => {
|
const getInstallErrorMessage = (error, output = "") => {
|
||||||
@ -181,10 +212,34 @@ const downloadArtifact = async (artifact, destinationPath, sendProgress) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const persistInstalledUpdateSettings = async (update) => {
|
||||||
|
try {
|
||||||
|
const settings = await getAppSettings();
|
||||||
|
const engine =
|
||||||
|
normalizeEngine(update?.engine) ||
|
||||||
|
normalizeEngine(settings?.appUpdateEngine) ||
|
||||||
|
DEFAULT_UPDATE_ENGINE;
|
||||||
|
|
||||||
|
await setAppSettings({
|
||||||
|
...settings,
|
||||||
|
appUpdateEngine: engine,
|
||||||
|
...(update?.branch
|
||||||
|
? { appUpdateRunningBranch: update.branch }
|
||||||
|
: {}),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
"[app-update] Failed to persist installed update settings.",
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const launchInstallerAndRestart = async (
|
const launchInstallerAndRestart = async (
|
||||||
mainWindow,
|
mainWindow,
|
||||||
installerPath,
|
installerPath,
|
||||||
sendProgress,
|
sendProgress,
|
||||||
|
update,
|
||||||
) => {
|
) => {
|
||||||
const installerHelpers = { sendProgress, getInstallErrorMessage };
|
const installerHelpers = { sendProgress, getInstallErrorMessage };
|
||||||
|
|
||||||
@ -206,6 +261,8 @@ const launchInstallerAndRestart = async (
|
|||||||
throw new Error(`App updates are not supported on ${process.platform}.`);
|
throw new Error(`App updates are not supported on ${process.platform}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await persistInstalledUpdateSettings(update);
|
||||||
|
|
||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
scheduleAppRestart();
|
scheduleAppRestart();
|
||||||
}
|
}
|
||||||
@ -241,7 +298,12 @@ const runAppUpdate = async (mainWindow, update, sendProgress) => {
|
|||||||
message: "Update downloaded",
|
message: "Update downloaded",
|
||||||
});
|
});
|
||||||
|
|
||||||
await launchInstallerAndRestart(mainWindow, installerPath, sendProgress);
|
await launchInstallerAndRestart(
|
||||||
|
mainWindow,
|
||||||
|
installerPath,
|
||||||
|
sendProgress,
|
||||||
|
update,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function startAppUpdate(mainWindow, update, sendProgress) {
|
export function startAppUpdate(mainWindow, update, sendProgress) {
|
||||||
|
|||||||
@ -72,7 +72,12 @@ export function createAppRpc() {
|
|||||||
setSidebarViewMenu: async ({ sections }) => ({
|
setSidebarViewMenu: async ({ sections }) => ({
|
||||||
ok: setSidebarViewMenu(sections)
|
ok: setSidebarViewMenu(sections)
|
||||||
}),
|
}),
|
||||||
getAppVersion: async () => process.env.ELECTROBUN_VERSION || 'desktop'
|
getAppVersion: async () => process.env.ELECTROBUN_VERSION || 'desktop',
|
||||||
|
getAppEngine: async () => {
|
||||||
|
const mainWindow = getMainWindow()
|
||||||
|
const renderer = mainWindow?.renderer || 'native'
|
||||||
|
return renderer === 'cef' ? 'chromium' : 'native'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
messages: {}
|
messages: {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -185,7 +185,8 @@ const electronAPI = {
|
|||||||
invokeRequest('resizeSpotlightWindow', { height }),
|
invokeRequest('resizeSpotlightWindow', { height }),
|
||||||
setSidebarViewMenu: (sections) =>
|
setSidebarViewMenu: (sections) =>
|
||||||
invokeRequest('setSidebarViewMenu', { sections }),
|
invokeRequest('setSidebarViewMenu', { sections }),
|
||||||
getAppVersion: () => invokeRequest('getAppVersion')
|
getAppVersion: () => invokeRequest('getAppVersion'),
|
||||||
|
getAppEngine: () => invokeRequest('getAppEngine')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user