Refactor ElectronContext for improved Electron detection and streamlined URL handling
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

- Simplified the `isElectron` function to directly return the result of `isElectrobunBridgeReady` or `isElectrobunDesktop`.
- Removed redundant checks and consolidated URL opening logic to enhance clarity and reduce code duplication.
- Updated event handling for Electron-specific features to utilize the desktopBridge API, improving maintainability and performance.
This commit is contained in:
Tom Butcher 2026-08-09 12:09:45 +01:00
parent 84a6a86bf3
commit 35ce3c39af

View File

@ -6,32 +6,9 @@ import desktopBridge, {
isElectrobunDesktop isElectrobunDesktop
} from '../../../electrobun-bridge.js' } from '../../../electrobun-bridge.js'
const electron = window.require ? window.require('electron') : null
const ipcRenderer = electron ? electron.ipcRenderer : null
// eslint-disable-next-line react-refresh/only-export-components // eslint-disable-next-line react-refresh/only-export-components
export function isElectron() { export function isElectron() {
if (isElectrobunDesktop()) { return isElectrobunBridgeReady() || isElectrobunDesktop()
return true
}
if (
typeof window !== 'undefined' &&
window.process &&
window.process.type === 'renderer'
) {
return true
}
if (
typeof navigator === 'object' &&
typeof navigator.userAgent === 'string' &&
navigator.userAgent.indexOf('Electron') >= 0
) {
return true
}
return false
} }
const ElectronContext = createContext() const ElectronContext = createContext()
@ -56,7 +33,6 @@ const ElectronProvider = ({ children }) => {
const [isMaximized, setIsMaximized] = useState(false) const [isMaximized, setIsMaximized] = useState(false)
const [isFullScreen, setIsFullScreen] = useState(false) const [isFullScreen, setIsFullScreen] = useState(false)
const [electronAvailable] = useState(isElectron()) const [electronAvailable] = useState(isElectron())
const useElectrobun = isElectrobunBridgeReady() || isElectrobunDesktop()
const navigate = useNavigate() const navigate = useNavigate()
const lastNavigationAtRef = useRef(0) const lastNavigationAtRef = useRef(0)
@ -85,74 +61,34 @@ const ElectronProvider = ({ children }) => {
}, []) }, [])
const openExternalUrl = (url) => { const openExternalUrl = (url) => {
if (useElectrobun) { if (!electronAvailable) return false
void desktopBridge.openExternalUrl(url).catch((error) => { void desktopBridge.openExternalUrl(url).catch((error) => {
console.warn('[ElectronContext] Failed to open external url:', error) console.warn('[ElectronContext] Failed to open external url:', error)
}) })
return true return true
}
if (electronAvailable && ipcRenderer) {
ipcRenderer.invoke('open-external-url', url)
return true
}
return false
} }
const openInternalUrl = (url) => { const openInternalUrl = (url) => {
if (useElectrobun) { if (!electronAvailable) return false
void desktopBridge.openInternalUrl(url).catch((error) => { void desktopBridge.openInternalUrl(url).catch((error) => {
console.warn('[ElectronContext] Failed to open internal url:', error) console.warn('[ElectronContext] Failed to open internal url:', error)
}) })
return true return true
}
if (electronAvailable && ipcRenderer) {
ipcRenderer.invoke('open-internal-url', url)
return true
}
return false
} }
useEffect(() => { useEffect(() => {
if (!electronAvailable) return if (!electronAvailable) return
if (useElectrobun) { document.body.classList.add('electron-body')
desktopBridge.getOsInfo().then((info) => { return () => {
if (info?.platform) { document.body.classList.remove('electron-body')
setPlatform(info.platform)
if (info.platform === 'darwin') {
document.documentElement.classList.add('macos-vibrancy')
}
}
})
desktopBridge.getWindowState().then(applyWindowState)
const unsubWindowState = desktopBridge.onMessage(
'windowState',
applyWindowState
)
const unsubNavigate = desktopBridge.onMessage('navigate', (url) => {
if (url.toLowerCase() == '/favicon.ico') {
return
}
console.log('[ElectronContext] Navigating to:', url)
navigate(url)
})
const unsubNavigationGesture = desktopBridge.onMessage(
'navigationGesture',
navigateHistory
)
return () => {
unsubWindowState()
unsubNavigate()
unsubNavigationGesture()
}
} }
}, [electronAvailable])
if (!ipcRenderer) return useEffect(() => {
if (!electronAvailable) return
ipcRenderer.invoke('os-info').then((info) => { desktopBridge.getOsInfo().then((info) => {
if (info?.platform) { if (info?.platform) {
setPlatform(info.platform) setPlatform(info.platform)
if (info.platform === 'darwin') { if (info.platform === 'darwin') {
@ -161,39 +97,30 @@ const ElectronProvider = ({ children }) => {
} }
}) })
ipcRenderer.invoke('window-state').then(applyWindowState) desktopBridge.getWindowState().then(applyWindowState)
const windowStateHandler = (_event, state) => { const unsubWindowState = desktopBridge.onMessage(
applyWindowState(state) 'windowState',
} applyWindowState
ipcRenderer.on('window-state', windowStateHandler) )
const unsubNavigate = desktopBridge.onMessage('navigate', (url) => {
const navigateHandler = (_event, url) => {
if (url.toLowerCase() == '/favicon.ico') { if (url.toLowerCase() == '/favicon.ico') {
return return
} }
console.log('[ElectronContext] Navigating to:', url) console.log('[ElectronContext] Navigating to:', url)
navigate(url) navigate(url)
} })
ipcRenderer.on('navigate', navigateHandler) const unsubNavigationGesture = desktopBridge.onMessage(
'navigationGesture',
const navigationGestureHandler = (_event, direction) => { navigateHistory
navigateHistory(direction) )
}
ipcRenderer.on('navigation-gesture', navigationGestureHandler)
return () => { return () => {
ipcRenderer.removeListener('navigate', navigateHandler) unsubWindowState()
ipcRenderer.removeListener('navigation-gesture', navigationGestureHandler) unsubNavigate()
ipcRenderer.removeListener('window-state', windowStateHandler) unsubNavigationGesture()
} }
}, [ }, [applyWindowState, electronAvailable, navigate, navigateHistory])
applyWindowState,
electronAvailable,
navigate,
navigateHistory,
useElectrobun
])
useEffect(() => { useEffect(() => {
if (!electronAvailable || platform !== 'darwin') return if (!electronAvailable || platform !== 'darwin') return
@ -227,103 +154,74 @@ const ElectronProvider = ({ children }) => {
}, [electronAvailable, navigateHistory, platform]) }, [electronAvailable, navigateHistory, platform])
const handleWindowControl = (action) => { const handleWindowControl = (action) => {
if (useElectrobun) { if (!electronAvailable) return
void desktopBridge.windowControl(action) void desktopBridge.windowControl(action)
return
}
if (electronAvailable && ipcRenderer) {
ipcRenderer.send('window-control', action)
}
} }
const getAuthSession = async () => { const getAuthSession = async () => {
if (!electronAvailable) return null if (!electronAvailable) return null
if (useElectrobun) return await desktopBridge.getAuthSession() return await desktopBridge.getAuthSession()
if (!ipcRenderer) return null
return await ipcRenderer.invoke('auth-session-get')
} }
const setAuthSession = async (session) => { const setAuthSession = async (session) => {
if (!electronAvailable) return false if (!electronAvailable) return false
if (useElectrobun) { const result = await desktopBridge.setAuthSession(session)
const result = await desktopBridge.setAuthSession(session) return result?.ok ?? false
return result?.ok ?? false
}
if (!ipcRenderer) return false
return await ipcRenderer.invoke('auth-session-set', session)
} }
const clearAuthSession = async () => { const clearAuthSession = async () => {
if (!electronAvailable) return false if (!electronAvailable) return false
if (useElectrobun) { const result = await desktopBridge.clearAuthSession()
const result = await desktopBridge.clearAuthSession() return result?.ok ?? false
return result?.ok ?? false
}
if (!ipcRenderer) return false
return await ipcRenderer.invoke('auth-session-clear')
} }
const getAppSettings = useCallback(async () => { const getAppSettings = useCallback(async () => {
if (!electronAvailable) return {} if (!electronAvailable) return {}
if (useElectrobun) return await desktopBridge.getAppSettings() return await desktopBridge.getAppSettings()
if (!ipcRenderer) return {} }, [electronAvailable])
return await ipcRenderer.invoke('app-settings-get')
}, [electronAvailable, useElectrobun])
const setAppSettings = useCallback( const setAppSettings = useCallback(
async (settings) => { async (settings) => {
if (!electronAvailable) return false if (!electronAvailable) return false
if (useElectrobun) { const result = await desktopBridge.setAppSettings(settings)
const result = await desktopBridge.setAppSettings(settings) return result?.ok ?? false
return result?.ok ?? false
}
if (!ipcRenderer) return false
return await ipcRenderer.invoke('app-settings-set', settings)
}, },
[electronAvailable, useElectrobun] [electronAvailable]
) )
const startAppUpdate = useCallback( const startAppUpdate = useCallback(
async (update) => { async (update) => {
if (!electronAvailable) return false if (!electronAvailable) return false
if (useElectrobun) { const result = await desktopBridge.startAppUpdate(update)
const result = await desktopBridge.startAppUpdate(update) return result?.ok ?? false
return result?.ok ?? false
}
if (!ipcRenderer) return false
return await ipcRenderer.invoke('app-update-start', update)
}, },
[electronAvailable, useElectrobun] [electronAvailable]
) )
const checkAppUpdateResult = useCallback(async () => { const checkAppUpdateResult = useCallback(async () => {
if (!electronAvailable || !useElectrobun) return null if (!electronAvailable) return null
return await desktopBridge.checkAppUpdateResult() return await desktopBridge.checkAppUpdateResult()
}, [electronAvailable, useElectrobun]) }, [electronAvailable])
const checkDuplicateInstallations = useCallback(async () => { const checkDuplicateInstallations = useCallback(async () => {
if (!electronAvailable || !useElectrobun) return null if (!electronAvailable) return null
return await desktopBridge.checkDuplicateInstallations() return await desktopBridge.checkDuplicateInstallations()
}, [electronAvailable, useElectrobun]) }, [electronAvailable])
const removeDuplicateInstallations = useCallback(async () => { const removeDuplicateInstallations = useCallback(async () => {
if (!electronAvailable || !useElectrobun) return false if (!electronAvailable) return false
const result = await desktopBridge.removeDuplicateInstallations() const result = await desktopBridge.removeDuplicateInstallations()
return result?.ok ?? false return result?.ok ?? false
}, [electronAvailable, useElectrobun]) }, [electronAvailable])
const onDuplicateInstallationsRemoved = useCallback( const onDuplicateInstallationsRemoved = useCallback(
(handler) => { (handler) => {
if ( if (!electronAvailable || typeof handler !== 'function') {
!electronAvailable ||
!useElectrobun ||
typeof handler !== 'function'
) {
return () => {} return () => {}
} }
return desktopBridge.onMessage('duplicateInstallationsRemoved', handler) return desktopBridge.onMessage('duplicateInstallationsRemoved', handler)
}, },
[electronAvailable, useElectrobun] [electronAvailable]
) )
const onAppUpdateProgress = useCallback( const onAppUpdateProgress = useCallback(
@ -331,24 +229,9 @@ const ElectronProvider = ({ children }) => {
if (!electronAvailable || typeof handler !== 'function') { if (!electronAvailable || typeof handler !== 'function') {
return () => {} return () => {}
} }
return desktopBridge.onMessage('appUpdateProgress', handler)
if (useElectrobun) {
return desktopBridge.onMessage('appUpdateProgress', handler)
}
if (!ipcRenderer) return () => {}
const progressHandler = (_event, progress) => {
handler(progress)
}
ipcRenderer.on('app-update-progress', progressHandler)
return () => {
ipcRenderer.removeListener('app-update-progress', progressHandler)
}
}, },
[electronAvailable, useElectrobun] [electronAvailable]
) )
const onCheckForUpdatesRequest = useCallback( const onCheckForUpdatesRequest = useCallback(
@ -356,24 +239,9 @@ const ElectronProvider = ({ children }) => {
if (!electronAvailable || typeof handler !== 'function') { if (!electronAvailable || typeof handler !== 'function') {
return () => {} return () => {}
} }
return desktopBridge.onMessage('checkForUpdates', handler)
if (useElectrobun) {
return desktopBridge.onMessage('checkForUpdates', handler)
}
if (!ipcRenderer) return () => {}
const checkHandler = () => {
handler()
}
ipcRenderer.on('check-for-updates', checkHandler)
return () => {
ipcRenderer.removeListener('check-for-updates', checkHandler)
}
}, },
[electronAvailable, useElectrobun] [electronAvailable]
) )
const getToken = async () => { const getToken = async () => {
@ -389,12 +257,8 @@ const ElectronProvider = ({ children }) => {
const resizeSpotlightWindow = async (height) => { const resizeSpotlightWindow = async (height) => {
if (!electronAvailable) return false if (!electronAvailable) return false
try { try {
if (useElectrobun) { const result = await desktopBridge.resizeSpotlightWindow(height)
const result = await desktopBridge.resizeSpotlightWindow(height) return result?.ok ?? false
return result?.ok ?? false
}
if (!ipcRenderer) return false
return await ipcRenderer.invoke('spotlight-window-resize', height)
} catch (error) { } catch (error) {
console.warn( console.warn(
'[ElectronContext] Failed to resize spotlight window:', '[ElectronContext] Failed to resize spotlight window:',
@ -407,31 +271,22 @@ const ElectronProvider = ({ children }) => {
const setSidebarViewMenu = useCallback( const setSidebarViewMenu = useCallback(
async (sections) => { async (sections) => {
if (!electronAvailable) return false if (!electronAvailable) return false
if (useElectrobun) { const result = await desktopBridge.setSidebarViewMenu(sections)
const result = await desktopBridge.setSidebarViewMenu(sections) return result?.ok ?? false
return result?.ok ?? false
}
if (!ipcRenderer) return false
return await ipcRenderer.invoke('set-sidebar-view-menu', sections)
}, },
[electronAvailable, useElectrobun] [electronAvailable]
) )
const getElectronVersion = useCallback(async () => { const getElectronVersion = useCallback(async () => {
if (!electronAvailable) return null if (!electronAvailable) return null
if (useElectrobun) return await desktopBridge.getAppVersion() return await desktopBridge.getAppVersion()
if (!ipcRenderer) return null }, [electronAvailable])
return await ipcRenderer.invoke('electron-version')
}, [electronAvailable, useElectrobun])
const getAppEngine = useCallback(async () => { const getAppEngine = useCallback(async () => {
if (!electronAvailable) return 'native' if (!electronAvailable) return 'native'
if (useElectrobun) { const engine = await desktopBridge.getAppEngine()
const engine = await desktopBridge.getAppEngine() return engine === 'chromium' ? 'chromium' : 'native'
return engine === 'chromium' ? 'chromium' : 'native' }, [electronAvailable])
}
return 'native'
}, [electronAvailable, useElectrobun])
return ( return (
<ElectronContext.Provider <ElectronContext.Provider