Skip to content

Commit

Permalink
refactor hostd, do not show window on startup if configured
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Jan 25, 2024
1 parent 89b3e1b commit 6511858
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 218 deletions.
37 changes: 26 additions & 11 deletions hostd/electron-src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { spawn } from 'child_process'
import { state } from './state'
import { getBinaryFilePath, getConfig, getConfigFilePath } from './config'
import axios from 'axios'
import { Octokit } from '@octokit/rest'

export function startDaemon(): Promise<void> {
return new Promise(async (resolve, reject) => {
Expand All @@ -10,52 +11,52 @@ export function startDaemon(): Promise<void> {
try {
const config = getConfig()
const binaryFilePath = getBinaryFilePath()
state.process = spawn(binaryFilePath, ['-env'], {
state.daemon = spawn(binaryFilePath, ['-env'], {
env: { ...process.env, HOSTD_CONFIG_FILE: getConfigFilePath() },
cwd: config.directory,
})

state.process.stdout?.on('data', (data) => {
state.daemon.stdout?.on('data', (data) => {
console.log(`stdout: ${data}`)
// Emit events or log data as needed
})

state.process.stderr?.on('data', (data) => {
state.daemon.stderr?.on('data', (data) => {
console.error(`stderr: ${data}`)
// Emit events or log data as needed
})

state.process.on('close', (code) => {
state.daemon.on('close', (code) => {
console.log(`child process exited with code ${code}`)
state.process = null
state.daemon = null
})

resolve()
} catch (err) {
state.process = null
state.daemon = null
reject(err)
}
})
}

export function stopDaemon(): Promise<void> {
return new Promise((resolve) => {
if (!state.process) {
if (!state.daemon) {
resolve()
return
}

state.process.on('close', () => {
state.process = null
state.daemon.on('close', () => {
state.daemon = null
resolve()
})

state.process.kill('SIGINT')
state.daemon.kill('SIGINT')
})
}

export function getIsDaemonRunning(): boolean {
return !!state.process && !state.process.killed
return !!state.daemon && !state.daemon.killed
}

export async function getInstalledVersion(): Promise<string> {
Expand Down Expand Up @@ -87,3 +88,17 @@ export async function getInstalledVersion(): Promise<string> {
return ''
}
}

export async function getLatestVersion(): Promise<string> {
try {
const octokit = new Octokit()
const response = await octokit.repos.getLatestRelease({
owner: 'SiaFoundation',
repo: 'hostd',
})
return response.data.tag_name
} catch (err) {
console.error(err)
return ''
}
}
51 changes: 18 additions & 33 deletions hostd/electron-src/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,7 @@ import { getBinaryFilePath, getConfigAndBinaryDirectoryPath } from './config'
import { promisify } from 'util'
import stream from 'stream'
import axios from 'axios'

export async function getLatestVersion(): Promise<string> {
try {
const octokit = new Octokit()
const response = await octokit.repos.getLatestRelease({
owner: 'SiaFoundation',
repo: 'hostd',
})
return response.data.tag_name
} catch (err) {
console.error(err)
return ''
}
}
import { system } from './state'

export async function downloadRelease(): Promise<void> {
try {
Expand Down Expand Up @@ -95,11 +82,11 @@ function getTempDownloadsPath(): string {
}

function getBinaryZipStagingPath(): string {
const binaryName = process.platform === 'win32' ? `hostd.exe` : `hostd`
const binaryName = system.isWindows ? `hostd.exe` : `hostd`
return path.join(getTempDownloadsPath(), binaryName + '.zip')
}

function releaseAsset(): string {
function releaseAsset() {
let goos
switch (process.platform) {
case 'win32':
Expand All @@ -111,28 +98,26 @@ function releaseAsset(): string {
case 'linux':
goos = 'linux'
break
// Add additional mappings as needed
default:
throw new Error(`Unsupported platform: ${process.platform}`)
}

let goarch
switch (process.arch) {
case 'x64':
goarch = 'amd64'
break
case 'ia32':
goarch = '386'
break
case 'arm':
goarch = 'arm'
break
case 'arm64':
goarch = 'arm64'
break
// Add additional mappings as needed
default:
throw new Error(`Unsupported architecture: ${process.arch}`)
if (process.platform === 'win32') {
// Windows only supports amd64
goarch = 'amd64'
} else {
// For Darwin and Linux, consider both amd64 and arm64
switch (process.arch) {
case 'x64':
goarch = 'amd64'
break
case 'arm64':
goarch = 'arm64'
break
default:
throw new Error(`Unsupported architecture: ${process.arch}`)
}
}

return `hostd_${goos}_${goarch}.zip`
Expand Down
195 changes: 23 additions & 172 deletions hostd/electron-src/index.ts
Original file line number Diff line number Diff line change
@@ -1,152 +1,36 @@
import path, { join } from 'path'
import fs from 'fs'
import {
BrowserWindow,
app,
ipcMain,
shell,
Tray,
Menu,
globalShortcut,
} from 'electron'
import isDev from 'electron-is-dev'
import { app, globalShortcut } from 'electron'
import prepareNext from 'electron-next'
import {
getInstalledVersion,
getIsDaemonRunning,
startDaemon,
stopDaemon,
} from './daemon'
import { downloadRelease, getLatestVersion } from './download'
import {
Config,
doesBinaryExist,
getConfig,
getDefaultDataPath,
getIsConfigured,
saveConfig,
} from './config'
import { format } from 'url'

let mainWindow: BrowserWindow | null = null
let appIcon = null
const isDarwin = process.platform === 'darwin'
const isLinux = process.platform === 'linux'
let isQuitting = false
// const isWindows = process.platform === 'win32'
import { startDaemon, stopDaemon } from './daemon'
import { downloadRelease } from './download'
import { doesBinaryExist, getIsConfigured } from './config'
import { initTray } from './tray'
import { state } from './state'
import { initWindow } from './window'
import { initShortcuts } from './shortcuts'
import { initIpc } from './ipc'

app.on('ready', async () => {
await prepareNext('./renderer')
initWindow()
initTray()
initShortcuts()
initIpc()

mainWindow = new BrowserWindow({
width: 500,
height: 700,
minWidth: 500,
minHeight: 600,
maxWidth: 500,
maxHeight: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: join(__dirname, 'preload.js'),
},
})

// Hide the main window instead of closing it, to keep the app running in the tray
mainWindow.on('close', (event) => {
if (!isQuitting) {
event.preventDefault()
if (isDarwin) {
app.dock.hide()
}
mainWindow?.hide()
}
})

// Setup close to tray settings for both minimize and close events
mainWindow.on('minimize', () => {
if (isDarwin) {
app.dock.hide()
}
// TODO: Does Linux support tray?
// https://electronjs.org/docs/api/tray
// minimize instead of attempting to go to system tray.
if (isLinux) {
return true
}
mainWindow?.hide()
return false
})

const iconName = isDarwin ? 'tray.png' : 'tray-win.png'
const iconPath = isDev
? path.join(process.cwd(), 'assets', iconName)
: path.join(__dirname, '../assets', iconName)

appIcon = new Tray(iconPath)
const trayContextMenu = Menu.buildFromTemplate([
{
label: 'Configure',
click: function () {
if (isDarwin) {
app.dock.show()
}
mainWindow?.show()
},
},
{
label: 'Quit',
click: function () {
app.quit()
},
},
])
appIcon.setToolTip('hostd')
appIcon.setContextMenu(trayContextMenu)
const url = isDev
? 'http://localhost:8000/'
: format({
pathname: path.join(__dirname, '../renderer/out/index.html'),
protocol: 'file:',
slashes: true,
})
mainWindow.loadURL(url)

if (isDev) {
mainWindow.setMaximumSize(2000, 2000)
mainWindow.setSize(1000, 800)
mainWindow.webContents.openDevTools()
}

const needsDownload = !doesBinaryExist()
if (needsDownload) {
const needsInitialDownload = !doesBinaryExist()
if (needsInitialDownload) {
await downloadRelease()
}

// If the app is already configured, start the daemon and open browser
// and do not show the configuration window.
if (getIsConfigured()) {
startDaemon()
state.mainWindow?.close()
}
// Register a global shortcut listener for the developer tools
const devToolsShortcut =
process.platform === 'darwin' ? 'Cmd+Alt+I' : 'Ctrl+Shift+I'
globalShortcut.register(devToolsShortcut, () => {
// Open the DevTools
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.openDevTools()
mainWindow.setSize(1000, 800)
mainWindow.setMaximumSize(2000, 2000)
}
})
})

async function quitDaemonAndApp() {
isQuitting = true
await stopDaemon()
mainWindow = null
}

app.on('before-quit', async () => {
if (!isQuitting) {
if (!state.isQuitting) {
await quitDaemonAndApp()
}
})
Expand All @@ -160,41 +44,8 @@ app.on('will-quit', async () => {
// even though closing windows is not really possible
app.on('window-all-closed', app.quit)

ipcMain.handle('open-browser', (_, url: string) => {
shell.openExternal(url)
})
ipcMain.handle('daemon-start', async (_) => {
await startDaemon()
})
ipcMain.handle('daemon-stop', async (_) => {
async function quitDaemonAndApp() {
state.isQuitting = true
await stopDaemon()
})
ipcMain.handle('daemon-is-running', (_) => {
const isDaemonRunning = getIsDaemonRunning()
return isDaemonRunning
})
ipcMain.handle('config-get', (_) => {
const config = getConfig()
return config
})
ipcMain.handle('get-is-configured', (_) => {
return getIsConfigured()
})
ipcMain.handle('open-data-directory', (_) => {
shell.openPath(getDefaultDataPath())
return true
})
ipcMain.handle('get-default-data-directory', async (_) => {
await fs.promises.mkdir(getDefaultDataPath(), { recursive: true })
return getDefaultDataPath()
})
ipcMain.handle('get-installed-version', (_) => {
return getInstalledVersion()
})
ipcMain.handle('get-latest-version', (_) => {
return getLatestVersion()
})
ipcMain.handle('config-save', async (_, config: Config) => {
await saveConfig(config)
return true
})
state.mainWindow = null
}
Loading

0 comments on commit 6511858

Please sign in to comment.