Refactor single instance and deep link handling
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

- Removed deprecated single instance handlers and streamlined deep link processing.
- Updated `ensureSingleInstanceLock` to initiate signal watching for better instance management.
- Enhanced deep link handling by introducing `setupWindowsDeepLinkHandling` for improved navigation and focus management.
- Refactored URL parsing in `findProtocolUrl` for better reliability in deep link resolution.
This commit is contained in:
Tom Butcher 2026-08-02 19:45:54 +01:00
parent 37113b1b41
commit 4caa7a4bd1
3 changed files with 57 additions and 24 deletions

View File

@ -1,7 +1,6 @@
import {
closeSingleInstanceServer,
ensureSingleInstanceLock,
setSingleInstanceHandlers
ensureSingleInstanceLock
} from '../desktop/single-instance.js'
const gotSingleInstanceLock = await ensureSingleInstanceLock()
@ -17,21 +16,16 @@ const {
} = await import('../desktop/spotlight.js')
const {
createMainWindow,
handleDeepLink,
handleDeepLinkFromArgv,
setupDevAuthServer,
setupNavigationGestures,
showMainWindow
setupWindowsDeepLinkHandling
} = await import('../desktop/window.js')
setSingleInstanceHandlers({
onDeepLink: handleDeepLink,
onFocus: showMainWindow
})
const rpc = createAppRpc()
const mainWindow = await createMainWindow(rpc)
setupWindowsDeepLinkHandling()
setupNavigationGestures(mainWindow)
registerGlobalShortcuts(rpc)
setupDevAuthServer()

View File

@ -114,10 +114,11 @@ export function findProtocolUrl(args) {
const trimmed = arg.trim().replace(/^['"]+|['"]+$/g, '')
const match = trimmed.match(/farmcontrol:\/\/\S+/i)
if (match) {
const rawUrl = match[0].replace(/['"]+$/g, '')
try {
return decodeURI(match[0])
return decodeURI(rawUrl)
} catch {
return match[0]
return rawUrl
}
}
}
@ -125,10 +126,11 @@ export function findProtocolUrl(args) {
const combined = sources.join(' ')
const combinedMatch = combined.match(/farmcontrol:\/\/\S+/i)
if (combinedMatch) {
const rawUrl = combinedMatch[0].replace(/['"]+$/g, '')
try {
return decodeURI(combinedMatch[0])
return decodeURI(rawUrl)
} catch {
return combinedMatch[0]
return rawUrl
}
}
@ -212,7 +214,6 @@ function dispatchMessage(message) {
if (resolved.type === 'deeplink' && resolved.url) {
handlers.onDeepLink?.(resolved.url)
handlers.onFocus?.()
return
}
@ -243,7 +244,7 @@ function startInstanceSignalWatcher() {
// Polling below covers filesystems without reliable watch support.
}
pollInterval = setInterval(processInstanceSignal, 250)
pollInterval = setInterval(processInstanceSignal, 100)
}
export async function ensureSingleInstanceLock() {
@ -252,6 +253,7 @@ export async function ensureSingleInstanceLock() {
}
if (tryAcquirePrimaryLock()) {
startInstanceSignalWatcher()
return true
}
@ -261,7 +263,6 @@ export async function ensureSingleInstanceLock() {
export function setSingleInstanceHandlers({ onDeepLink, onFocus }) {
handlers = { onDeepLink, onFocus }
startInstanceSignalWatcher()
while (pendingMessages.length > 0) {
dispatchMessage(pendingMessages.shift())

View File

@ -10,7 +10,7 @@ const isMacOS = process.platform === 'darwin'
const DEV_SERVER_PORT = 5780
const DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}`
import { findProtocolUrl } from './single-instance.js'
import { findProtocolUrl, setSingleInstanceHandlers } from './single-instance.js'
import { getWindowsLaunchSources } from './windows-launch-args.js'
let mainWindow = null
@ -86,14 +86,46 @@ function sendNavigateToRenderer(redirectPath) {
setTimeout(() => deliverNavigation(redirectPath), 100)
}
export function handleDeepLink(url) {
if (!url || typeof url !== 'string') return
export function openInternalUrl(url) {
sendNavigateToRenderer(url)
return true
}
function parseDeepLinkPath(url) {
if (!url || typeof url !== 'string') {
return null
}
if (url.startsWith('/')) {
return url
}
const match = url.match(/^farmcontrol:\/\/app(.*)$/i)
if (!match) return
if (!match) {
return null
}
const redirectPath = match[1] || '/'
sendNavigateToRenderer(redirectPath.startsWith('/') ? redirectPath : `/${redirectPath}`)
const normalizedPath = redirectPath.startsWith('/')
? redirectPath
: `/${redirectPath}`
try {
return decodeURI(normalizedPath)
} catch {
return normalizedPath
}
}
export function handleDeepLink(url) {
const path = parseDeepLinkPath(url)
if (!path) {
showMainWindow()
return false
}
openInternalUrl(path)
return true
}
export function handleDeepLinkFromArgv() {
@ -262,9 +294,15 @@ export async function setupDevAuthServer() {
app.listen(port, () => {})
}
export function openInternalUrl(url) {
sendNavigateToRenderer(url)
return true
export function setupWindowsDeepLinkHandling() {
if (process.platform === 'darwin') {
return
}
setSingleInstanceHandlers({
onDeepLink: handleDeepLink,
onFocus: showMainWindow
})
}
export function getWindowState() {