diff --git a/public/uv-sw.js b/public/uv-sw.js index 4ed866e..843853a 100644 --- a/public/uv-sw.js +++ b/public/uv-sw.js @@ -1,9 +1,12 @@ importScripts('/uv/uv.sw.js'); -const sw = new UVServiceWorker(); +const params = new URLSearchParams(self.serviceWorker.scriptURL) + +var cfg = JSON.parse(params.get('config')); +const sw = new UVServiceWorker(cfg); self.addEventListener('fetch', event => - event.respondWith( - sw.fetch(event) - ) -); \ No newline at end of file + event.respondWith(sw.fetch(event)) +); + + diff --git a/public/uv/uv.config.js b/public/uv/uv.config.js index 88e90e9..44c5fa4 100644 --- a/public/uv/uv.config.js +++ b/public/uv/uv.config.js @@ -28,10 +28,9 @@ self.xor = { ) } } - self.__uv$config = { prefix: '/service/', - bare: 'https://server.flow-works.me/bare/', + bare: 'https://server.flow-works.me' + '/bare/', encodeUrl: self.xor.encode, decodeUrl: self.xor.decode, handler: '/uv/uv.handler.js', diff --git a/public/uv/uv.sw.js b/public/uv/uv.sw.js index cf16200..4e851eb 100644 --- a/public/uv/uv.sw.js +++ b/public/uv/uv.sw.js @@ -2,9 +2,9 @@ importScripts('/uv/uv.bundle.js'); importScripts('/uv/uv.config.js'); class UVServiceWorker extends EventEmitter { - constructor(config = __uv$config) { + constructor(flowURL, config = __uv$config) { super(); - if (!config.bare) config.bare = '/bare/'; + config.bare = flowURL + '/bare/'; this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location)); this.headers = { csp: [ diff --git a/src/assets/style.less b/src/assets/style.less index adda753..9334dc3 100644 --- a/src/assets/style.less +++ b/src/assets/style.less @@ -197,7 +197,7 @@ launcher { preloader { position: absolute; - z-index: 999999999999999999; + z-index: 9999999; top: 0; left: 0; background: var(--crust); diff --git a/src/builtin/apps/settings.ts b/src/builtin/apps/settings.ts index 89056b8..5b858aa 100644 --- a/src/builtin/apps/settings.ts +++ b/src/builtin/apps/settings.ts @@ -15,24 +15,7 @@ export default class SettingsApp implements App { configFolderLoc = '/.flow/' async open (): Promise { - const fs = new (window as any).Filer.FileSystem() - - try { - fs.exists(this.configFolderLoc, function (exists: any) { - if (exists === false) { - fs.mkdir(this.configFolderLoc, () => {}) - } - }) - } catch (e) { alert(e.message) } - try { - fs.exists(this.configFileLoc, function (exists: any) { - if (exists === false) { - fs.writeFile(this.configFileLoc, '{"clock-Type":"0", "tab-title": "Flow OS", "favicon-icon": "to-be-replaced", "proxy-type": "uv", "search-engine": "google" }', () => {}) - } - }) - } catch (e) {} - - const win = (window as any).wm.createWindow({ + const win = window.wm.createWindow({ title: this.meta.name, icon: this.meta.icon, width: 700, @@ -41,167 +24,81 @@ export default class SettingsApp implements App { }) win.content.style.padding = '10px' + win.content.innerHTML = ` - -
- -

Appereance

- -

Clock

- - - -

Tab Title

- -

Tab Icon

- -

- -

Browser Settings

- -

Proxy Type

- - -

Search Engine

- - -

Server

- -

- - - +

Settings

+
+ ` - // Get current data - const data = JSON.parse(await fs.promises.readFile(this.configFileLoc)) - let oldData = JSON.stringify(data) - // Handle value changes - - win.content.getElementsByClassName('btn')[0].onclick = async () => { - data['clock-Type'] = '0' - win.content.getElementsByClassName('btn')[0].disabled = true - win.content.getElementsByClassName('btn')[1].disabled = false - settingsChange() - } - win.content.getElementsByClassName('btn')[1].onclick = async () => { - data['clock-Type'] = '1' - win.content.getElementsByClassName('btn')[0].disabled = false - win.content.getElementsByClassName('btn')[1].disabled = true - settingsChange() + const titles: { + [key: string]: string + } = { + SERVER_URL: 'FlowServer URL', + HOSTNAME: 'Device Hostname', + USERNAME: 'Username', + '24HR_CLOCK': '24hr Clock' } - win.content.getElementsByClassName('btn')[2].addEventListener('change', () => { - data['tab-title'] = win.content.getElementsByClassName('btn')[2].value - settingsChange() - }) + const config = await window.config() - win.content.getElementsByClassName('btn')[6].addEventListener('change', () => { - if (isURL(win.content.getElementsByClassName('btn')[6].value) === true && win.content.getElementsByClassName('btn')[6].value !== '') { - data['flow-server'] = win.content.getElementsByClassName('btn')[6].value - win.content.getElementsByClassName('error-text')[1].textContent = '' - } else { - if (win.content.getElementsByClassName('btn')[6].value !== '') { - win.content.getElementsByClassName('error-text')[1].textContent = 'Please input a vaild URL' - } else { - win.content.getElementsByClassName('error-text')[1].textContent = '' - } + for (const key of Object.keys(config)) { + const container = document.createElement('div') + const label = document.createElement('label') + const input = document.createElement('input') + + if (typeof (config as any)[key] === 'boolean') { + label.innerText = `${titles[key] ?? key}` + input.type = 'checkbox' + } else if (typeof (config as any)[key] === 'string') { + label.innerText = `${titles[key] ?? key}:\n` + input.type = 'text' } - settingsChange() - }) - win.content.getElementsByClassName('btn')[3].addEventListener('change', () => { - if (isURL(win.content.getElementsByClassName('btn')[3].value) === true && win.content.getElementsByClassName('btn')[4].value !== '') { - data['favicon-url'] = win.content.getElementsByClassName('btn')[3].value - win.content.getElementsByClassName('error-text')[0].textContent = '' - } else { - if (win.content.getElementsByClassName('btn')[4].value !== '') { - win.content.getElementsByClassName('error-text')[0].textContent = 'Please input a vaild URL' - } else { - win.content.getElementsByClassName('error-text')[0].textContent = '' - } - } - settingsChange() - }) + input.value = (config as any)[key] - win.content.getElementsByClassName('btn')[4].value = data['proxy-type'] - win.content.getElementsByClassName('btn')[4].addEventListener('change', (event: any) => { - data['proxy-type'] = event.target.value - settingsChange() - }) + container.appendChild(label) + container.appendChild(input) - win.content.getElementsByClassName('btn')[5].value = data['search-engine'] - win.content.getElementsByClassName('btn')[5].addEventListener('change', (event: any) => { - data['search-engine'] = event.target.value - settingsChange() - }) + win.content.querySelector('.settings')?.appendChild(container) - // Handle saving. + win.content.querySelector('.save')?.addEventListener('click', () => { + (config as any)[key] = input.value - win.content.getElementsByClassName('save')[0].onclick = async () => { - await fs.promises.writeFile(this.configFileLoc, JSON.stringify(data)) - oldData = JSON.stringify(data) - settingsChange() + navigator.serviceWorker.getRegistrations().then(function (registrations) { + for (const registration of registrations) { + registration.unregister().then(() => { + navigator.serviceWorker.register('/uv-sw.js?config=' + encodeURIComponent(config.SERVER_URL), { + scope: '/service/' + }).catch(e => console.error(e)) + }).catch(e => console.error(e)) + } + }).catch(e => console.error(e)) + }) } - // Display unsaved changes / prevent Save button from being clicked twice. - - function settingsChange (): void { - if (oldData !== JSON.stringify(data)) { - win.setTitle('Settings - Unsaved Changes') - win.content.getElementsByClassName('save')[0].disabled = false - } else { - win.setTitle('Settings') - win.content.getElementsByClassName('save')[0].disabled = true - } - } - // URL checker - - function isURL (input: string): any { - const regex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/ - return input.match(regex) - } - - // Set values - win.content.getElementsByClassName('btn')[0].disabled = true - win.content.getElementsByClassName('btn')[1].disabled = false - if (data['clock-Type'] === '0') { - win.content.getElementsByClassName('btn')[0].disabled = false - win.content.getElementsByClassName('btn')[1].disabled = true - } + win.content.querySelector('.save')?.addEventListener('click', () => { + window.fs.promises.writeFile('/.config/flow.json', JSON.stringify(config)) + .then(null) + .catch(e => console.error(e)) + }) return win } diff --git a/src/builtin/plugins/clock.ts b/src/builtin/plugins/clock.ts index 560696b..0208f86 100644 --- a/src/builtin/plugins/clock.ts +++ b/src/builtin/plugins/clock.ts @@ -1,3 +1,5 @@ +import { getTime } from '../../utils' + export const meta = { name: 'Clock', description: 'Displays the date & time.', @@ -5,7 +7,7 @@ export const meta = { version: '1.0.0' } -export const run = (element: HTMLDivElement): void => { +export const run = async (element: HTMLDivElement): Promise => { let date: Date = new Date() element.style.display = 'flex' @@ -19,17 +21,16 @@ export const run = (element: HTMLDivElement): void => { return `${split[0]},${split[1]} ` } - const refreshClock = (): string => { - return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' }) - } - - refreshDate() - refreshClock() + let clock = await getTime() + let date_ = refreshDate() + element.innerHTML = `${clock}
${date_}
` setInterval(() => { - date = new Date() - const clock = refreshClock() - const date_ = refreshDate() - element.innerHTML = `${clock}
${date_}
` + (async () => { + date = new Date() + clock = await getTime() + date_ = refreshDate() + element.innerHTML = `${clock}
${date_}
` + })().catch(e => console.error(e)) }, 1000) } diff --git a/src/index.ts b/src/index.ts index 5673062..c325422 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import WindowManager from './instances/WindowManager' import Flow from './instances/Flow' import * as fs from 'fs' +import { FlowConfig } from './types' declare global { interface Window { @@ -14,6 +15,7 @@ declare global { fs: typeof fs statusBar: StatusBar wm: WindowManager + config: () => Promise } } @@ -37,6 +39,46 @@ window.wm = new WindowManager(); (async function () { window.preloader.setPending('filesystem') window.fs = new (window as any).Filer.FileSystem() + + const defaultConfig = { + SERVER_URL: 'https://server.flow-works.me', + HOSTNAME: 'flow', + USERNAME: 'user', + '24HR_CLOCK': false + } + + window.fs.exists('/.config', (exists) => { + if (!exists) window.fs.promises.mkdir('/.config').then(null).catch(e => console.error) + + window.fs.exists('/.config/flow.json', (exists) => { + // if (!exists) { + window.fs.promises.writeFile('/.config/flow.json', JSON.stringify(defaultConfig)).then(null).catch(e => console.error) + // } + }) + }) + /** + * Gets the current FlowOS config. + * + * @returns The current FlowOS config. + */ + window.config = async (): Promise => { + return await new Promise((resolve, reject) => { + window.fs.exists('/.config/flow.json', (exists) => { + if (exists) { + window.fs.promises.readFile('/.config/flow.json') + .then(content => { resolve(JSON.parse(content.toString())) }) + .catch(() => reject(new Error('Unable to read config file.'))) + } else reject(new Error('Config file does not exist.')) + }) + }) + } + + console.log(await window.config()) + + navigator.serviceWorker.register('/uv-sw.js?config=' + encodeURIComponent((await window.config()).SERVER_URL), { + scope: '/service/' + }).catch(e => console.error(e)) + await window.preloader.setDone('filesystem') await window.wm.init() @@ -45,8 +87,4 @@ window.wm = new WindowManager(); window.preloader.setStatus('') window.preloader.finish() - - await navigator.serviceWorker.register('/uv-sw.js', { - scope: '/service/' - }) })().catch(e => console.error) diff --git a/src/instances/StatusBar.ts b/src/instances/StatusBar.ts index 70edd3f..2a49fcb 100644 --- a/src/instances/StatusBar.ts +++ b/src/instances/StatusBar.ts @@ -24,7 +24,7 @@ class StatusBar { this.element.appendChild(element) - await item.run(element) + await item.run(element, await window.config()) } /** diff --git a/src/types.ts b/src/types.ts index 094a100..03317df 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,7 @@ import FlowWindow from './structures/FlowWindow' export type AppOpenFunction = (data: any) => Promise -export type PluginRunFunction = (element: HTMLDivElement) => void | Promise +export type PluginRunFunction = (element: HTMLDivElement, config: any) => void | Promise export interface AppClosedEvent extends CustomEvent { detail: { @@ -72,3 +72,10 @@ export interface LoadedPlugin extends Plugin { export interface PackageJSON { version: string } + +export interface FlowConfig { + SERVER_URL: string + HOSTNAME: string + USERNAME: string + '24HOUR_CLOCK': boolean +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..5ab2caa --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,32 @@ +/** + * Gets the current time in 12hrs/24hrs. + * + * @returns The time. + */ +export const getTime = async (): Promise => { + const config = await window.config() + const use24hrs = config['24HOUR_CLOCK'] + + const now = new Date() + let hours: string | number = now.getHours() + let minutes: string | number = now.getMinutes() + let period = 'AM' + + if (!use24hrs) { + period = (hours >= 12) ? 'PM' : 'AM' + if (hours === 0) { + hours = 12 + } else if (hours > 12) { + hours = hours % 12 + } + } + + hours = (hours < 10) ? `0${hours}` : hours + minutes = (minutes < 10) ? `0${minutes}` : minutes + + const timeString = use24hrs + ? `${hours}:${minutes}` + : `${hours}:${minutes} ${period}` + + return timeString +}