farmcontrol-ui/public/mainWindow.js

315 lines
7.4 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 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
})
}
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.')
}
}