diff --git a/app.js b/app.js index 5b51a7e..fdab7b8 100644 --- a/app.js +++ b/app.js @@ -7,14 +7,26 @@ import { NavMenu } from "/components/nav-menu.js" import { Dialog } from "/dialog/dialog.js" import { ButtonGroup } from "/forms/button-group.js" import { Dropdown } from "/menu/dropdown.js" +import { NetworkSettings } from "/settings/network-settings.js" import { PageSettings } from "/settings/page-settings.js" +import { Connections } from "/settings/connections.js" +import { ConnectionEdit } from "/settings/connection-edit.js" customElements.define('m-layout', Layout) customElements.define('m-page', Page) customElements.define('m-page-actions', PageActions) +customElements.define( + 'm-settings-network-settings', NetworkSettings +) customElements.define( 'm-settings-page-settings', PageSettings ) +customElements.define( + 'm-settings-connections', Connections +) +customElements.define( + 'm-settings-connection-edit', ConnectionEdit +) customElements.define( 'm-file-group-page', FileGroupPage ) diff --git a/components/file-group-page.js b/components/file-group-page.js index 3125673..4895c5d 100644 --- a/components/file-group-page.js +++ b/components/file-group-page.js @@ -29,7 +29,10 @@ addEventListener('message', event => { frame !== undefined && event.source == frame.contentWindow ) if (isChild) { - parent.postMessage(event.data, '*') + const transferArg = ( + event.ports?.length ? [[...event.ports]] : [] + ) + parent.postMessage(event.data, '*', ...transferArg) } else { let isNew = false const d = event.data @@ -44,7 +47,12 @@ addEventListener('message', event => { document.body.appendChild(frame) } } else if (frame !== undefined) { - frame.contentWindow.postMessage(event.data, '*') + const transferArg = ( + event.ports?.length ? [[...event.ports]] : [] + ) + frame.contentWindow.postMessage( + event.data, '*', ...transferArg + ) } } }) @@ -184,6 +192,11 @@ export class FileGroupPage extends HTMLElement { } async handleViewMessage(event) { + if (Array.isArray(event.data)) { + if (['get', 'put'].includes(event.data[0])) { + await this.handleRequestMessage('view', event) + } + } } async handleEditMessage(event) { @@ -198,8 +211,71 @@ export class FileGroupPage extends HTMLElement { } else if (event.data[0] === 'save') { const doc = event.data[1] this.body = doc + } else if (['get', 'put'].includes(event.data[0])) { + await this.handleRequestMessage('edit', event) + } + } + } + + async handleRequestMessage(frame, event) { + if (event.ports.length === 1) { + const port = event.ports[0] + const method = event.data[0] + const path = event.data[1] + const access = this.getAccess(path) + if (method === 'get') { + if (['read', 'readWrite'].includes(access)) { + port.postMessage({ + status: 200, + body: this.getPage(path), + }) + } else { + port.postMessage({ + status: 401, + }) + } + } else if (method === 'put') { + const body = event.data[2] + if (access === 'readWrite') { + this.setPage(path, body) + port.postMessage({ + status: 200, + }) + } else { + port.postMessage({ + status: 401, + }) + } } + port.close() + } else { + throw new Error('request message without port') + } + } + + getAccess(path) { + const settings = this.settings + const outbound = settings.connections?.outbound + if (outbound && outbound[path]) { + return outbound[path] + } + } + + getPage(path) { + const body = localStorage.getItem(path) + try { + return JSON.parse(body) + } catch (err) { + return body + } + } + + setPage(path, body) { + let value = body + if (typeof value !== 'string') { + value = JSON.stringify(value) } + localStorage.setItem(path, value) } set body(value) { diff --git a/components/page-actions.js b/components/page-actions.js index e9a5c14..d811804 100644 --- a/components/page-actions.js +++ b/components/page-actions.js @@ -122,18 +122,36 @@ export class PageActions extends HTMLElement { } return } + const sKeyOld = 'settings/page:' + this.path + const sKeyNew = 'settings/page:' + newPath + const settingsJson = localStorage.getItem(sKeyOld) + if (settingsJson ?? true === true) { + localStorage.setItem(sKeyNew, settingsJson) + localStorage.removeItem(sKeyOld) + let settingsData + try { + settingsData = JSON.parse(settingsJson) + } catch (err) { + settingsData = {} + } + if (settingsData?.connections) { + for (const dir of ['outbound', 'inbound']) { + const otherDir = ( + dir === 'outbound' ? 'inbound' : 'outbound' + ) + this.applyInverseRename( + settingsData, dir, otherDir, this.path, newPath + ) + } + } + } else { + localStorage.removeItem(sKeyNew) + } localStorage.setItem( newPath, localStorage.getItem(this.path) ) - localStorage.setItem( - 'settings/page:' + newPath, - localStorage.getItem(this.path) - ) localStorage.removeItem(this.path) - localStorage.removeItem( - 'settings/page:' + newPath, - ) dialog.close() location.hash = newPath }) @@ -227,6 +245,7 @@ export class PageActions extends HTMLElement { 'm-settings-page-settings' ) settingsEl.cspProfiles = this.cspProfiles + settingsEl.path = this.path settingsEl.data = this.page.settings const h = document.createElement('h2') h.innerText = this.text.settings @@ -236,7 +255,16 @@ export class PageActions extends HTMLElement { 'm-forms-button-group' ) bGroup.addPrimary(this.text.save, () => { - this.page.settings = settingsEl.data + const settingsData = settingsEl.data + this.page.settings = settingsData + for (const dir of ['outbound', 'inbound']) { + const otherDir = ( + dir === 'outbound' ? 'inbound' : 'outbound' + ) + this.applyInverseSettings( + settingsData, dir, otherDir + ) + } dialog.close() this.dispatchEvent(new CustomEvent( 'settings-change', {bubbles: true, composed: true} @@ -249,6 +277,62 @@ export class PageActions extends HTMLElement { dialog.open() } + applyInverseSettings(settingsData, dir, otherDir) { + const selfEntries = Object.entries( + settingsData.connections[dir] + ) + for (const [path, access] of selfEntries) { + const key = 'settings/page:' + path + let val = localStorage.getItem(key) + try { + if (val !== null) { + val = JSON.parse(val) + } + } catch (err) { + // ignore + } + const data = val ?? {} + data.connections = data.connections ?? {} + data.connections[otherDir] = ( + data.connections[otherDir] ?? {} + ) + data.connections[otherDir][this.path] = access + localStorage.setItem( + key, JSON.stringify(data) + ) + } + } + + applyInverseRename( + settingsData, dir, otherDir, oldPath, newPath + ) { + const selfEntries = Object.entries( + settingsData.connections[dir] ?? {} + ) + for (const [path, access] of selfEntries) { + const key = 'settings/page:' + path + let val = localStorage.getItem(key) + try { + if (val !== null) { + val = JSON.parse(val) + } + } catch (err) { + // ignore + } + const data = val ?? {} + data.connections = data.connections ?? {} + data.connections[otherDir] = ( + data.connections[otherDir] ?? {} + ) + const accessValue = data.connections[otherDir][oldPath] + data.connections[otherDir][newPath] = accessValue + data.connections[otherDir][oldPath] = undefined + localStorage.setItem( + key, JSON.stringify(data) + ) + } + } + get language() { return this._language } diff --git a/sw.js b/sw.js index 48a6727..58c5f18 100644 --- a/sw.js +++ b/sw.js @@ -19,7 +19,10 @@ async function initCache() { '/loader/editor-build.js', '/menu/dropdown.js', '/settings/page-settings.js', - ]) //10 + '/settings/network-settings.js', + '/settings/connections.js', + '/settings/connection-edit.js', + ]) } self.addEventListener("install", event => {