All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
321 lines
7.6 KiB
JavaScript
321 lines
7.6 KiB
JavaScript
import { BrowserWindow, ipcMain, Menu } from 'electron'
|
|
import path, { dirname } from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = dirname(__filename)
|
|
|
|
let win
|
|
let sidebarViewMenuSections = []
|
|
|
|
const PROTOCOL_PREFIX = 'farmcontrol://'
|
|
|
|
function findProtocolUrl(args) {
|
|
return args.find(
|
|
(arg) => typeof arg === 'string' && arg.startsWith(PROTOCOL_PREFIX)
|
|
)
|
|
}
|
|
|
|
function sendNavigateToRenderer(redirectPath) {
|
|
const deliver = () => {
|
|
win.webContents.send('navigate', redirectPath)
|
|
win.show()
|
|
win.focus()
|
|
}
|
|
|
|
if (!win || win.isDestroyed()) {
|
|
createWindow()
|
|
win.webContents.once('did-finish-load', () => {
|
|
setTimeout(deliver, 100)
|
|
})
|
|
return
|
|
}
|
|
|
|
if (win.webContents.isLoading()) {
|
|
win.webContents.once('did-finish-load', () => {
|
|
setTimeout(deliver, 100)
|
|
})
|
|
return
|
|
}
|
|
|
|
deliver()
|
|
}
|
|
|
|
function toElectronSidebarMenuItems(items = []) {
|
|
return items
|
|
.map((item) => {
|
|
if (item?.type === 'divider') {
|
|
return { type: 'separator' }
|
|
}
|
|
|
|
const menuItem = {
|
|
label: item.label
|
|
}
|
|
|
|
if (item?.children && Array.isArray(item.children) && item.children.length) {
|
|
menuItem.submenu = toElectronSidebarMenuItems(item.children)
|
|
} else if (item?.path) {
|
|
menuItem.click = () => sendNavigateToRenderer(item.path)
|
|
} else {
|
|
menuItem.enabled = false
|
|
}
|
|
|
|
return menuItem
|
|
})
|
|
.filter(Boolean)
|
|
}
|
|
|
|
function buildApplicationMenuTemplate() {
|
|
const env = (process.env.NODE_ENV || 'development').trim()
|
|
const viewSubmenu = sidebarViewMenuSections.map((section) => ({
|
|
label: section.label,
|
|
submenu: toElectronSidebarMenuItems(section.items || [])
|
|
}))
|
|
|
|
if (viewSubmenu.length === 0) {
|
|
viewSubmenu.push({ label: 'No sidebar items available', enabled: false })
|
|
}
|
|
|
|
if (env === 'development') {
|
|
viewSubmenu.push(
|
|
{ type: 'separator' },
|
|
{
|
|
label: 'Toggle Developer Tools',
|
|
accelerator:
|
|
process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
|
click: () => {
|
|
if (win && !win.isDestroyed()) {
|
|
win.webContents.toggleDevTools()
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
const template = [
|
|
{ role: 'fileMenu' },
|
|
{ role: 'editMenu' },
|
|
{ label: 'View', submenu: viewSubmenu },
|
|
{ role: 'windowMenu' }
|
|
]
|
|
|
|
if (process.platform === 'darwin') {
|
|
template.unshift({ role: 'appMenu' })
|
|
}
|
|
|
|
return template
|
|
}
|
|
|
|
function applyApplicationMenu() {
|
|
const menu = Menu.buildFromTemplate(buildApplicationMenuTemplate())
|
|
Menu.setApplicationMenu(menu)
|
|
}
|
|
|
|
export function handleDeepLink(url) {
|
|
if (!url?.startsWith(`${PROTOCOL_PREFIX}app`)) return
|
|
const redirectPath = url.replace(`${PROTOCOL_PREFIX}app`, '') || '/'
|
|
sendNavigateToRenderer(redirectPath)
|
|
}
|
|
|
|
export function handleDeepLinkFromArgv() {
|
|
if (process.platform === 'darwin') return
|
|
const url = findProtocolUrl(process.argv)
|
|
if (url) handleDeepLink(url)
|
|
}
|
|
|
|
export function setupSingleInstanceLock(app) {
|
|
const gotTheLock = app.requestSingleInstanceLock()
|
|
if (!gotTheLock) {
|
|
app.quit()
|
|
return false
|
|
}
|
|
|
|
app.on('second-instance', (_event, commandLine) => {
|
|
const url = findProtocolUrl(commandLine)
|
|
if (url) {
|
|
handleDeepLink(url)
|
|
} else if (win && !win.isDestroyed()) {
|
|
if (win.isMinimized()) win.restore()
|
|
win.show()
|
|
win.focus()
|
|
}
|
|
})
|
|
|
|
return true
|
|
}
|
|
|
|
function attachKeyboardShortcuts(browserWindow) {
|
|
if (!browserWindow) return
|
|
// Keyboard shortcuts for the main window can be added here if needed
|
|
}
|
|
|
|
function setupWindowEvents() {
|
|
if (!win) return
|
|
win.on('maximize', () => {
|
|
win.webContents.send('window-state', {
|
|
isMaximized: true
|
|
})
|
|
})
|
|
win.on('enter-full-screen', () => {
|
|
console.log('Entered fullscreen')
|
|
win.webContents.send('window-state', {
|
|
isFullScreen: true
|
|
})
|
|
})
|
|
win.on('leave-full-screen', () => {
|
|
win.webContents.send('window-state', {
|
|
isFullScreen: false
|
|
})
|
|
})
|
|
win.on('unmaximize', () => {
|
|
win.webContents.send('window-state', {
|
|
isMaximized: false
|
|
})
|
|
})
|
|
}
|
|
|
|
export function createWindow() {
|
|
win = new BrowserWindow({
|
|
width: 1200,
|
|
height: 800,
|
|
frame: false,
|
|
titleBarStyle: 'hiddenInset',
|
|
trafficLightPosition: { x: 14, y: 12 },
|
|
backgroundColor: '#141414',
|
|
icon: path.join(__dirname, './logo512.png'),
|
|
webPreferences: {
|
|
nodeIntegration: true,
|
|
contextIsolation: false
|
|
}
|
|
})
|
|
|
|
applyApplicationMenu()
|
|
|
|
// For development, load from localhost; for production, load the built index.html
|
|
if (process.env.ELECTRON_START_URL) {
|
|
win.loadURL(process.env.ELECTRON_START_URL)
|
|
} else {
|
|
win.loadFile(path.join(__dirname, '../build/index.html'))
|
|
}
|
|
|
|
setupWindowEvents()
|
|
attachKeyboardShortcuts(win)
|
|
}
|
|
|
|
export function getWindow() {
|
|
return win
|
|
}
|
|
|
|
export function getElectronVersion() {
|
|
return process.versions.electron
|
|
}
|
|
|
|
export function setupMainWindowIPC() {
|
|
// IPC handler to get window state
|
|
ipcMain.handle('window-state', () => {
|
|
if (!win || win.isDestroyed())
|
|
return { isFullScreen: false, isMaximized: false }
|
|
return {
|
|
isFullScreen: win ? win.isFullScreen() : false,
|
|
isMaximized: win ? win.isMaximized() : false
|
|
}
|
|
})
|
|
|
|
// IPC handlers for window controls
|
|
ipcMain.on('window-control', (event, action) => {
|
|
if (!win) return
|
|
switch (action) {
|
|
case 'minimize':
|
|
win.minimize()
|
|
break
|
|
case 'maximize':
|
|
if (win.isMaximized()) {
|
|
win.unmaximize()
|
|
} else {
|
|
win.maximize()
|
|
}
|
|
break
|
|
case 'close':
|
|
win.close()
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
})
|
|
|
|
ipcMain.handle('open-internal-url', (event, url) => {
|
|
if (!win || win.isDestroyed()) {
|
|
createWindow()
|
|
// Wait for window to finish loading before sending navigate event
|
|
win.webContents.once('did-finish-load', () => {
|
|
setTimeout(() => {
|
|
win.webContents.send('navigate', url)
|
|
win.show()
|
|
win.focus()
|
|
}, 100)
|
|
})
|
|
} else {
|
|
win.webContents.send('navigate', url)
|
|
win.show()
|
|
win.focus()
|
|
}
|
|
return true
|
|
})
|
|
|
|
ipcMain.handle('set-sidebar-view-menu', (event, sidebarSections) => {
|
|
if (!Array.isArray(sidebarSections)) {
|
|
return false
|
|
}
|
|
|
|
sidebarViewMenuSections = sidebarSections
|
|
applyApplicationMenu()
|
|
return true
|
|
})
|
|
|
|
ipcMain.handle('electron-version', () => getElectronVersion())
|
|
}
|
|
|
|
export function setupMainWindowAppEvents(app) {
|
|
app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') app.quit()
|
|
})
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
|
})
|
|
|
|
app.on('open-url', (event, url) => {
|
|
event.preventDefault()
|
|
handleDeepLink(url)
|
|
})
|
|
}
|
|
|
|
export function setupDevAuthServer() {
|
|
const env = (process.env.NODE_ENV || 'development').trim()
|
|
if (env == 'development') {
|
|
console.log('Starting development auth web server...')
|
|
import('express').then(({ default: express }) => {
|
|
const app = express()
|
|
const port = 3500
|
|
|
|
app.use((req, res) => {
|
|
const redirectPath = req.originalUrl
|
|
res.send(
|
|
`Open Farmcontrol to continue... (Redirect path: ${redirectPath})`
|
|
)
|
|
if (win && win.webContents) {
|
|
win.webContents.send('navigate', redirectPath)
|
|
win.show()
|
|
win.focus()
|
|
}
|
|
})
|
|
|
|
app.listen(port, () => {
|
|
console.log(`Dev auth server running on http://localhost:${port}`)
|
|
})
|
|
})
|
|
} else {
|
|
console.log('Will use url scheme instead of auth server.')
|
|
}
|
|
}
|