[] Finished Settings application

This commit is contained in:
ThinLiquid 2023-11-24 14:14:26 +00:00 committed by GitHub
parent fb82c26573
commit 092652df12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 165 additions and 188 deletions

View file

@ -1,9 +1,12 @@
importScripts('/uv/uv.sw.js'); 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 => self.addEventListener('fetch', event =>
event.respondWith( event.respondWith(sw.fetch(event))
sw.fetch(event)
)
); );

View file

@ -28,10 +28,9 @@ self.xor = {
) )
} }
} }
self.__uv$config = { self.__uv$config = {
prefix: '/service/', prefix: '/service/',
bare: 'https://server.flow-works.me/bare/', bare: 'https://server.flow-works.me' + '/bare/',
encodeUrl: self.xor.encode, encodeUrl: self.xor.encode,
decodeUrl: self.xor.decode, decodeUrl: self.xor.decode,
handler: '/uv/uv.handler.js', handler: '/uv/uv.handler.js',

View file

@ -2,9 +2,9 @@ importScripts('/uv/uv.bundle.js');
importScripts('/uv/uv.config.js'); importScripts('/uv/uv.config.js');
class UVServiceWorker extends EventEmitter { class UVServiceWorker extends EventEmitter {
constructor(config = __uv$config) { constructor(flowURL, config = __uv$config) {
super(); 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.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location));
this.headers = { this.headers = {
csp: [ csp: [

View file

@ -197,7 +197,7 @@ launcher {
preloader { preloader {
position: absolute; position: absolute;
z-index: 999999999999999999; z-index: 9999999;
top: 0; top: 0;
left: 0; left: 0;
background: var(--crust); background: var(--crust);

View file

@ -15,24 +15,7 @@ export default class SettingsApp implements App {
configFolderLoc = '/.flow/' configFolderLoc = '/.flow/'
async open (): Promise<FlowWindow> { async open (): Promise<FlowWindow> {
const fs = new (window as any).Filer.FileSystem() const win = window.wm.createWindow({
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({
title: this.meta.name, title: this.meta.name,
icon: this.meta.icon, icon: this.meta.icon,
width: 700, width: 700,
@ -41,167 +24,81 @@ export default class SettingsApp implements App {
}) })
win.content.style.padding = '10px' win.content.style.padding = '10px'
win.content.innerHTML = ` win.content.innerHTML = `
<h1>Settings</h1>
<div id=> <div class="settings"></div>
<button class="save">Save!</button>
<h1>Appereance</h1>
<p>Clock</p>
<button class="btn"> 12 Hour Clock </button>
<button class="btn"> 24 Hour Clock </button>
<p>Tab Title</p>
<input class="btn" placeholder="Enter Tab Title">
<p>Tab Icon</p>
<input class="btn" placeholder="Enter Icon URL">
<p class="error-text"> </p>
<h1>Browser Settings</h1>
<p>Proxy Type</p>
<select class="btn">
<option value="uv">Ultraviolet</option>
<option value="dy">Dynamic</option>
</select>
<p>Search Engine</p>
<select class="btn">
<option value="google">Google</option>
<option value="bing">Bing</option>
<option value="yahoo">Yahoo!</option>
<option value="duck">DuckDuckGo</option>
</select>
<p>Server</p>
<input class="btn" placeholder="Enter Server URL">
<p class="error-text"> </p>
<button disabled class="btn save"> Save </button>
<style> <style>
h1 {
margin: 0;
}
.error-text { input, button {
font-size:12px; background: var(--mantle);
color:red; padding: 2.5px;
} border: 1px solid var(--surface-0);
border-radius: 5px;
.btn { margin: 2.5px;
background-color:#45475a; }
border: none;
border-radius: 5px;
border: 2px solid #11111b;
transition: 0.2s;
}
.btn:disabled {
color:rgba(205, 214, 244, 0.4);
transition: 0.2s;
}
.save {
float: right;
position: relative;
}
label {
margin: 2.5px;
}
</style> </style>
` `
// Get current data
const data = JSON.parse(await fs.promises.readFile(this.configFileLoc))
let oldData = JSON.stringify(data)
// Handle value changes const titles: {
[key: string]: string
win.content.getElementsByClassName('btn')[0].onclick = async () => { } = {
data['clock-Type'] = '0' SERVER_URL: 'FlowServer URL',
win.content.getElementsByClassName('btn')[0].disabled = true HOSTNAME: 'Device Hostname',
win.content.getElementsByClassName('btn')[1].disabled = false USERNAME: 'Username',
settingsChange() '24HR_CLOCK': '24hr Clock'
}
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()
} }
win.content.getElementsByClassName('btn')[2].addEventListener('change', () => { const config = await window.config()
data['tab-title'] = win.content.getElementsByClassName('btn')[2].value
settingsChange()
})
win.content.getElementsByClassName('btn')[6].addEventListener('change', () => { for (const key of Object.keys(config)) {
if (isURL(win.content.getElementsByClassName('btn')[6].value) === true && win.content.getElementsByClassName('btn')[6].value !== '') { const container = document.createElement('div')
data['flow-server'] = win.content.getElementsByClassName('btn')[6].value const label = document.createElement('label')
win.content.getElementsByClassName('error-text')[1].textContent = '' const input = document.createElement('input')
} else {
if (win.content.getElementsByClassName('btn')[6].value !== '') { if (typeof (config as any)[key] === 'boolean') {
win.content.getElementsByClassName('error-text')[1].textContent = 'Please input a vaild URL' label.innerText = `${titles[key] ?? key}`
} else { input.type = 'checkbox'
win.content.getElementsByClassName('error-text')[1].textContent = '' } 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', () => { input.value = (config as any)[key]
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()
})
win.content.getElementsByClassName('btn')[4].value = data['proxy-type'] container.appendChild(label)
win.content.getElementsByClassName('btn')[4].addEventListener('change', (event: any) => { container.appendChild(input)
data['proxy-type'] = event.target.value
settingsChange()
})
win.content.getElementsByClassName('btn')[5].value = data['search-engine'] win.content.querySelector('.settings')?.appendChild(container)
win.content.getElementsByClassName('btn')[5].addEventListener('change', (event: any) => {
data['search-engine'] = event.target.value
settingsChange()
})
// Handle saving. win.content.querySelector('.save')?.addEventListener('click', () => {
(config as any)[key] = input.value
win.content.getElementsByClassName('save')[0].onclick = async () => { navigator.serviceWorker.getRegistrations().then(function (registrations) {
await fs.promises.writeFile(this.configFileLoc, JSON.stringify(data)) for (const registration of registrations) {
oldData = JSON.stringify(data) registration.unregister().then(() => {
settingsChange() 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. win.content.querySelector('.save')?.addEventListener('click', () => {
window.fs.promises.writeFile('/.config/flow.json', JSON.stringify(config))
function settingsChange (): void { .then(null)
if (oldData !== JSON.stringify(data)) { .catch(e => console.error(e))
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
}
return win return win
} }

View file

@ -1,3 +1,5 @@
import { getTime } from '../../utils'
export const meta = { export const meta = {
name: 'Clock', name: 'Clock',
description: 'Displays the date & time.', description: 'Displays the date & time.',
@ -5,7 +7,7 @@ export const meta = {
version: '1.0.0' version: '1.0.0'
} }
export const run = (element: HTMLDivElement): void => { export const run = async (element: HTMLDivElement): Promise<void> => {
let date: Date = new Date() let date: Date = new Date()
element.style.display = 'flex' element.style.display = 'flex'
@ -19,17 +21,16 @@ export const run = (element: HTMLDivElement): void => {
return `<i>${split[0]}</i>,${split[1]} ` return `<i>${split[0]}</i>,${split[1]} `
} }
const refreshClock = (): string => { let clock = await getTime()
return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' }) let date_ = refreshDate()
} element.innerHTML = `${clock}<div>${date_}</div>`
refreshDate()
refreshClock()
setInterval(() => { setInterval(() => {
date = new Date() (async () => {
const clock = refreshClock() date = new Date()
const date_ = refreshDate() clock = await getTime()
element.innerHTML = `${clock}<div>${date_}</div>` date_ = refreshDate()
element.innerHTML = `${clock}<div>${date_}</div>`
})().catch(e => console.error(e))
}, 1000) }, 1000)
} }

View file

@ -6,6 +6,7 @@ import WindowManager from './instances/WindowManager'
import Flow from './instances/Flow' import Flow from './instances/Flow'
import * as fs from 'fs' import * as fs from 'fs'
import { FlowConfig } from './types'
declare global { declare global {
interface Window { interface Window {
@ -14,6 +15,7 @@ declare global {
fs: typeof fs fs: typeof fs
statusBar: StatusBar statusBar: StatusBar
wm: WindowManager wm: WindowManager
config: () => Promise<FlowConfig>
} }
} }
@ -37,6 +39,46 @@ window.wm = new WindowManager();
(async function () { (async function () {
window.preloader.setPending('filesystem') window.preloader.setPending('filesystem')
window.fs = new (window as any).Filer.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<FlowConfig> => {
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.preloader.setDone('filesystem')
await window.wm.init() await window.wm.init()
@ -45,8 +87,4 @@ window.wm = new WindowManager();
window.preloader.setStatus('') window.preloader.setStatus('')
window.preloader.finish() window.preloader.finish()
await navigator.serviceWorker.register('/uv-sw.js', {
scope: '/service/'
})
})().catch(e => console.error) })().catch(e => console.error)

View file

@ -24,7 +24,7 @@ class StatusBar {
this.element.appendChild(element) this.element.appendChild(element)
await item.run(element) await item.run(element, await window.config())
} }
/** /**

View file

@ -1,7 +1,7 @@
import FlowWindow from './structures/FlowWindow' import FlowWindow from './structures/FlowWindow'
export type AppOpenFunction = (data: any) => Promise<FlowWindow> export type AppOpenFunction = (data: any) => Promise<FlowWindow>
export type PluginRunFunction = (element: HTMLDivElement) => void | Promise<void> export type PluginRunFunction = (element: HTMLDivElement, config: any) => void | Promise<void>
export interface AppClosedEvent extends CustomEvent { export interface AppClosedEvent extends CustomEvent {
detail: { detail: {
@ -72,3 +72,10 @@ export interface LoadedPlugin extends Plugin {
export interface PackageJSON { export interface PackageJSON {
version: string version: string
} }
export interface FlowConfig {
SERVER_URL: string
HOSTNAME: string
USERNAME: string
'24HOUR_CLOCK': boolean
}

32
src/utils.ts Normal file
View file

@ -0,0 +1,32 @@
/**
* Gets the current time in 12hrs/24hrs.
*
* @returns The time.
*/
export const getTime = async (): Promise<string> => {
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
}