RevelaOS/src/system/BootLoader.ts
2024-01-22 16:43:40 +00:00

171 lines
6 KiB
TypeScript

import HTML from '../HTML'
import { AppClosedEvent, AppOpenedEvent, Process } from '../types'
import { getTime } from '../utils'
import nullIcon from '../assets/icons/application-default-icon.svg'
import { parse } from 'js-ini'
import { v4 as uuid } from 'uuid'
const BootLoader: Process = {
config: {
name: 'Bootloader',
type: 'process',
targetVer: '1.0.0-indev.0'
},
run: async (process) => {
const splashScreen = await process.loadLibrary('lib/SplashScreen')
const splashElement = splashScreen.getElement()
splashElement.appendTo(document.body)
const { fs } = process
const wm = await process.loadLibrary('lib/WindowManager')
const launcher = await process.loadLibrary('lib/Launcher')
const config = Buffer.from(await fs.readFile('/etc/flow')).toString()
process.kernel.setConfig(parse(config))
if ('serviceWorker' in navigator) {
const registrations = await navigator.serviceWorker.getRegistrations()
for (const registration of registrations) {
await registration.unregister()
}
try {
await navigator.serviceWorker.register(`/uv-sw.js?url=${encodeURIComponent(btoa(process.kernel.config.SERVER))}&e=${uuid()}`, {
scope: '/service/'
})
} catch (e) {
console.error(e)
}
} else {
console.warn('Service workers are not supported.')
}
const input = new HTML('input').attr({
type: 'text',
placeholder: 'Search'
}).on('keyup', () => {
apps.elm.innerHTML = ''
renderApps().catch(e => console.error(e))
}).appendTo(launcher.element)
const apps = new HTML('apps').appendTo(launcher.element)
const renderApps = async (): Promise<void> => {
apps.html('')
const files = await fs.readdir('/home/Applications/')
files
.filter((x: string) => x.endsWith('.app') && ((input.elm as HTMLInputElement) !== null ? x.toLowerCase().includes((input.elm as HTMLInputElement).value.toLowerCase()) : true))
.forEach((file: string) => {
fs.readFile(`/home/Applications/${file}`).then(async (data: Uint8Array) => {
const path = Buffer.from(data).toString()
const executable = await process.kernel.getExecutable(path) as Process
const appElement = new HTML('app').on('click', () => {
process.launch(path).catch((e: any) => console.error(e))
launcher.toggle()
}).appendTo(apps)
new HTML('img').attr({
src: executable.config.icon ?? nullIcon,
alt: `${executable.config.name} icon`
}).appendTo(appElement)
new HTML('div').text(executable.config.name).appendTo(appElement)
}).catch((e: any) => console.error(e))
})
}
await renderApps()
document.addEventListener('fs_update', () => {
renderApps().catch(e => console.error(e))
})
launcher.element.on('click', (e: Event) => {
if (e.target !== e.currentTarget) return
launcher.toggle()
})
const statusBar = await process.loadLibrary('lib/StatusBar')
statusBar.element.html(`
<div class="outlined" data-toolbar-id="start"><span class="material-symbols-rounded">space_dashboard</span></div>
<div data-toolbar-id="apps"></div>
<flex></flex>
<div class="outlined" data-toolbar-id="plugins"><span class="material-symbols-rounded">expand_less</span></div>
<div class="outlined" data-toolbar-id="controls">
<span class="material-symbols-rounded battery">battery_2_bar</span>
<span class="material-symbols-rounded signal">signal_cellular_4_bar</span>
</div>
<div class="outlined" data-toolbar-id="calendar"></div>
`)
setInterval((): any => {
getTime().then((time) => {
statusBar.element.qs('div[data-toolbar-id="calendar"]')?.text(time)
}).catch(e => console.error)
}, 1000)
statusBar.element.qs('div[data-toolbar-id="start"]')?.on('click', () => {
launcher.toggle()
})
if ('getBattery' in navigator) {
(navigator as any).getBattery().then((battery: any) => {
statusBar.updateBatteryIcon(battery)
battery.addEventListener('levelchange', () => {
statusBar.updateBatteryIcon(battery)
})
battery.addEventListener('chargingchange', () => {
statusBar.updateBatteryIcon(battery)
})
})
} else {
const batteryDiv = document.querySelector('div[data-toolbar-id="controls"] > .battery')
if (batteryDiv != null) {
batteryDiv.innerHTML = 'battery_unknown'
}
}
async function ping (startTime: number): Promise<void> {
fetch(`${process.kernel.config.SERVER as string}/bare/`)
.then(() => {
const endTime = performance.now()
const pingTime = endTime - startTime
statusBar.updateIcon(pingTime)
})
.catch(() => {
(document.querySelector('div[data-toolbar-id="controls"] > .signal') as HTMLElement).innerHTML = 'signal_cellular_connected_no_internet_4_bar'
})
}
setInterval((): any => ping(performance.now()), 10_000)
document.addEventListener('app_opened', (e: AppOpenedEvent): void => {
new HTML('app').appendMany(
new HTML('img').attr({
alt: `${e.detail.proc.config.name} icon`,
'data-id': e.detail.token,
src: e.detail.proc.config.icon ?? nullIcon
}).on('click', () => {
e.detail.win.focus()
e.detail.win.toggleMin()
})
).appendTo(statusBar.element.qs('div[data-toolbar-id="apps"]')?.elm as HTMLElement)
})
document.addEventListener('app_closed', (e: AppClosedEvent): void => {
statusBar.element.qs('div[data-toolbar-id="apps"]')?.qs(`img[data-id="${e.detail.token}"]`)?.elm.parentElement?.remove()
})
document.body.style.flexDirection = 'column-reverse'
await statusBar.element.appendTo(document.body)
await launcher.element.appendTo(document.body)
await wm.windowArea.appendTo(document.body)
splashElement.cleanup()
}
}
export default BootLoader