diff --git a/light9/web/live/Effect.ts b/light9/web/live/Effect.ts --- a/light9/web/live/Effect.ts +++ b/light9/web/live/Effect.ts @@ -1,7 +1,7 @@ import debug from "debug"; import { Literal, NamedNode, Quad_Object, Quad_Predicate, Quad_Subject, Term } from "n3"; import { some } from "underscore"; -import { Patch, patchContainsPreds } from "../patch"; +import { Patch, patchContainsPreds, patchUpdate } from "../patch"; import { SyncedGraph } from "../SyncedGraph"; type Color = string; @@ -60,13 +60,16 @@ export class Effect { const U = this.graph.U(); if (patch && !patchContainsPreds(patch, [U(":setting"), U(":device"), U(":deviceAttr")])) { // that's an approx list of preds , but it just means we'll miss some pathological settings edits - // return; + // return; } // log("syncFromGraph", this.uri); + // this repeats work- it gathers all settings when really some values changed (and we might even know about them). maybe push the value-fetching into a secnod phase of the run, and have the 1st phase drop out early const newSettings = []; + const seenDevAttrPairs: Set = new Set(); + for (let setting of Array.from(this.graph.objects(this.uri, U(":setting")))) { // log(` setting ${setting.value}`); if (!isUri(setting)) throw new Error(); @@ -87,7 +90,7 @@ export class Effect { value = this.graph.stringValue(setting, pred); // this may find multi values and throw } } - // log(`change: graph contains ${deviceAttr.value} ${value}`); + // log(`change: graph contains ${deviceAttr.value} ${value}`); newSettings.push({ device, deviceAttr, setting, value }); } @@ -109,10 +112,13 @@ export class Effect { edit(device: NamedNode, deviceAttr: NamedNode, newValue: ControlValue | null): Patch { log(`edit: value=${newValue}`); let existingSetting: NamedNode | null = null; + let result = { adds: [], dels: [] }; for (let s of this.settings) { if (device.equals(s.device) && deviceAttr.equals(s.deviceAttr)) { if (existingSetting !== null) { - throw new Error(`${this.uri.value} had two settings for ${device.value} - ${deviceAttr.value}`); + // this is corrupt. There was only supposed to be one setting per (dev,attr) pair. But we can fix it because we're going to update existingSetting to the user's requested value. + log(`${this.uri.value} had two settings for ${device.value} - ${deviceAttr.value} - deleting ${s.setting}`); + patchUpdate(result, this._removeEffectSetting(s.setting)); } existingSetting = s.setting; } @@ -120,16 +126,16 @@ export class Effect { if (newValue !== null && this.shouldBeStored(deviceAttr, newValue)) { if (existingSetting === null) { - return this._addEffectSetting(device, deviceAttr, newValue); + patchUpdate(result, this._addEffectSetting(device, deviceAttr, newValue)); } else { - return this._patchExistingEffectSetting(existingSetting, deviceAttr, newValue); + patchUpdate(result, this._patchExistingEffectSetting(existingSetting, deviceAttr, newValue)); } } else { if (existingSetting !== null) { - return this._removeEffectSetting(existingSetting); + patchUpdate(result, this._removeEffectSetting(existingSetting)); } } - return { adds: [], dels: [] }; + return result; } shouldBeStored(deviceAttr: NamedNode, value: ControlValue | null): boolean { diff --git a/light9/web/live/Light9LiveControls.ts b/light9/web/live/Light9LiveControls.ts --- a/light9/web/live/Light9LiveControls.ts +++ b/light9/web/live/Light9LiveControls.ts @@ -7,8 +7,8 @@ import { Patch, patchContainsPreds } fro import { getTopGraph } from "../RdfdbSyncedGraph"; import { SyncedGraph } from "../SyncedGraph"; import { GraphToControls } from "./GraphToControls"; +export { EditChoice } from "../EditChoice"; export { Light9DeviceControl as Light9LiveDeviceControl } from "./Light9DeviceControl"; -export { EditChoice } from "../EditChoice"; const log = debug("controls"); @customElement("light9-live-controls") diff --git a/light9/web/patch.ts b/light9/web/patch.ts --- a/light9/web/patch.ts +++ b/light9/web/patch.ts @@ -1,6 +1,6 @@ +import * as async from "async"; import debug from "debug"; -import * as async from "async"; -import { Writer, Parser, Quad, NamedNode } from "n3"; +import { NamedNode, Parser, Quad, Writer } from "n3"; const log = debug("patch"); export interface Patch { @@ -14,6 +14,12 @@ interface SyncgraphPatchMessage { patch: { adds: string; deletes: string }; } +export function patchUpdate(p1: Patch, p2: Patch): void { + // this is approx, since it doesnt handle matching existing quads. + p1.adds = p1.adds.concat(p2.adds); + p1.dels = p1.dels.concat(p2.dels); +} + export function patchSizeSummary(patch: Patch) { return "-" + patch.dels.length + " +" + patch.adds.length; } @@ -43,6 +49,7 @@ export function parseJsonPatch(input: Sy }); }; + // todo: is it faster to run them in series? might be return async.parallel([parseAdds, parseDels], (err: any) => cb(patch)); }