From 9712a046868ee51a565a425d3216a2bb297c4eee Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 17 Apr 2019 19:45:59 -0400 Subject: Got saving to database working --- src/client/DocServer.ts | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/client/DocServer.ts (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts new file mode 100644 index 000000000..9a3e122e8 --- /dev/null +++ b/src/client/DocServer.ts @@ -0,0 +1,72 @@ +import * as OpenSocket from 'socket.io-client'; +import { MessageStore, Types } from "./../server/Message"; +import { Opt, FieldWaiting, RefField, HandleUpdate } from '../fields/NewDoc'; +import { Utils } from '../Utils'; +import { SerializationHelper } from './util/SerializationHelper'; + +export namespace DocServer { + const _cache: { [id: string]: RefField | Promise> } = {}; + const _socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`); + const GUID: string = Utils.GenerateGuid(); + + export async function GetRefField(id: string): Promise> { + let cached = _cache[id]; + if (cached === undefined) { + const prom = Utils.EmitCallback(_socket, MessageStore.GetRefField, id).then(fieldJson => { + const field = fieldJson === undefined ? fieldJson : SerializationHelper.Deserialize(fieldJson); + if (field) { + _cache[id] = field; + } else { + delete _cache[id]; + } + return field; + }); + _cache[id] = prom; + return prom; + } else if (cached instanceof Promise) { + return cached; + } else { + return cached; + } + } + + export function UpdateField(id: string, diff: any) { + Utils.Emit(_socket, MessageStore.UpdateField, { id, diff }); + } + + export function CreateField(initialState: any) { + if (!("id" in initialState)) { + throw new Error("Can't create a field on the server without an id"); + } + Utils.Emit(_socket, MessageStore.CreateField, initialState); + } + + function respondToUpdate(diff: any) { + const id = diff.id; + if (id === undefined) { + return; + } + const field = _cache[id]; + const update = (f: Opt) => { + if (f === undefined) { + return; + } + const handler = f[HandleUpdate]; + if (handler) { + handler(diff); + } + }; + if (field instanceof Promise) { + field.then(update); + } else { + update(field); + } + } + + function connected(message: string) { + _socket.emit(MessageStore.Bar.Message, GUID); + } + + Utils.AddServerHandler(_socket, MessageStore.Foo, connected); + Utils.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate); +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From e678ac7f21e0c44eaa8ad88577093cdb313e21bb Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Fri, 19 Apr 2019 23:13:17 -0400 Subject: Deleted more old fields and split new stuff into multiple files --- src/client/DocServer.ts | 2 +- src/client/util/SerializationHelper.ts | 2 +- src/debug/Test.tsx | 8 +- src/fields/HtmlField.ts | 25 --- src/fields/ListField.ts | 196 ------------------ src/fields/NewDoc.ts | 358 --------------------------------- src/fields/TupleField.ts | 59 ------ src/new_fields/Doc.ts | 90 +++++++++ src/new_fields/HtmlField.ts | 14 ++ src/new_fields/List.ts | 35 ++++ src/new_fields/Proxy.ts | 55 +++++ src/new_fields/Schema.ts | 47 +++++ src/new_fields/Types.ts | 58 ++++++ src/new_fields/URLField.ts | 25 +++ src/new_fields/util.ts | 73 +++++++ 15 files changed, 405 insertions(+), 642 deletions(-) delete mode 100644 src/fields/HtmlField.ts delete mode 100644 src/fields/ListField.ts delete mode 100644 src/fields/NewDoc.ts delete mode 100644 src/fields/TupleField.ts create mode 100644 src/new_fields/Doc.ts create mode 100644 src/new_fields/HtmlField.ts create mode 100644 src/new_fields/List.ts create mode 100644 src/new_fields/Proxy.ts create mode 100644 src/new_fields/Schema.ts create mode 100644 src/new_fields/Types.ts create mode 100644 src/new_fields/URLField.ts create mode 100644 src/new_fields/util.ts (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 9a3e122e8..615e48af0 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,6 +1,6 @@ import * as OpenSocket from 'socket.io-client'; import { MessageStore, Types } from "./../server/Message"; -import { Opt, FieldWaiting, RefField, HandleUpdate } from '../fields/NewDoc'; +import { Opt, FieldWaiting, RefField, HandleUpdate } from '../new_fields/Doc'; import { Utils } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 7273c3fe4..ac70aba9d 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -1,5 +1,5 @@ import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr"; -import { Field } from "../../fields/NewDoc"; +import { Field } from "../../new_fields/Doc"; export namespace SerializationHelper { let serializing: number = 0; diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx index 6a677f80f..8b9c9fa0b 100644 --- a/src/debug/Test.tsx +++ b/src/debug/Test.tsx @@ -1,8 +1,12 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { serialize, deserialize, map } from 'serializr'; -import { URLField, Doc, createSchema, makeInterface, makeStrictInterface, List, ListSpec } from '../fields/NewDoc'; import { SerializationHelper } from '../client/util/SerializationHelper'; +import { createSchema, makeInterface, makeStrictInterface } from '../new_fields/Schema'; +import { URLField } from '../new_fields/URLField'; +import { Doc } from '../new_fields/Doc'; +import { ListSpec } from '../new_fields/Types'; +import { List } from '../new_fields/List'; +const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? const schema1 = createSchema({ hello: "number", diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts deleted file mode 100644 index a1d880070..000000000 --- a/src/fields/HtmlField.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BasicField } from "./BasicField"; -import { Types } from "../server/Message"; -import { FieldId } from "./Field"; - -export class HtmlField extends BasicField { - constructor(data: string = "", id?: FieldId, save: boolean = true) { - super(data, save, id); - } - - ToScriptString(): string { - return `new HtmlField("${this.Data}")`; - } - - Copy() { - return new HtmlField(this.Data); - } - - ToJson() { - return { - type: Types.Html, - data: this.Data, - id: this.Id, - }; - } -} \ No newline at end of file diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts deleted file mode 100644 index e24099126..000000000 --- a/src/fields/ListField.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { action, IArrayChange, IArraySplice, IObservableArray, observe, observable, Lambda } from "mobx"; -import { Server } from "../client/Server"; -import { UndoManager } from "../client/util/UndoManager"; -import { Types } from "../server/Message"; -import { BasicField } from "./BasicField"; -import { Field, FieldId } from "./Field"; -import { FieldMap } from "../client/SocketStub"; -import { ScriptField } from "./ScriptField"; - -export class ListField extends BasicField { - private _proxies: string[] = []; - private _scriptIds: string[] = []; - private scripts: ScriptField[] = []; - - constructor(data: T[] = [], scripts: ScriptField[] = [], id?: FieldId, save: boolean = true) { - super(data, save, id); - this.scripts = scripts; - this.updateProxies(); - this._scriptIds = this.scripts.map(script => script.Id); - if (save) { - Server.UpdateField(this); - } - this.observeList(); - } - - private _processingServerUpdate: boolean = false; - - private observeDisposer: Lambda | undefined; - private observeList(): void { - if (this.observeDisposer) { - this.observeDisposer(); - } - this.observeDisposer = observe(this.Data as IObservableArray, (change: IArrayChange | IArraySplice) => { - const target = change.object; - this.updateProxies(); - if (change.type === "splice") { - this.runScripts(change.removed, false); - UndoManager.AddEvent({ - undo: () => target.splice(change.index, change.addedCount, ...change.removed), - redo: () => target.splice(change.index, change.removedCount, ...change.added) - }); - this.runScripts(change.added, true); - } else { - this.runScripts([change.oldValue], false); - UndoManager.AddEvent({ - undo: () => target[change.index] = change.oldValue, - redo: () => target[change.index] = change.newValue - }); - this.runScripts([change.newValue], true); - } - if (!this._processingServerUpdate) { - Server.UpdateField(this); - } - }); - } - - private runScripts(fields: T[], added: boolean) { - for (const script of this.scripts) { - this.runScript(fields, script, added); - } - } - - private runScript(fields: T[], script: ScriptField, added: boolean) { - if (!this._processingServerUpdate) { - for (const field of fields) { - script.script.run({ field, added }); - } - } - } - - addScript(script: ScriptField) { - this.scripts.push(script); - this._scriptIds.push(script.Id); - - this.runScript(this.Data, script, true); - UndoManager.AddEvent({ - undo: () => this.removeScript(script), - redo: () => this.addScript(script), - }); - Server.UpdateField(this); - } - - removeScript(script: ScriptField) { - const index = this.scripts.indexOf(script); - if (index === -1) { - return; - } - this.scripts.splice(index, 1); - this._scriptIds.splice(index, 1); - UndoManager.AddEvent({ - undo: () => this.addScript(script), - redo: () => this.removeScript(script), - }); - this.runScript(this.Data, script, false); - Server.UpdateField(this); - } - - protected setData(value: T[]) { - this.runScripts(this.data, false); - - this.data = observable(value); - this.updateProxies(); - this.observeList(); - this.runScripts(this.data, true); - } - - private updateProxies() { - this._proxies = this.Data.map(field => field.Id); - } - - private arraysEqual(a: any[], b: any[]) { - if (a === b) return true; - if (a === null || b === null) return false; - if (a.length !== b.length) return false; - - // If you don't care about the order of the elements inside - // the array, you should sort both arrays here. - // Please note that calling sort on an array will modify that array. - // you might want to clone your array first. - - for (var i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) return false; - } - return true; - } - - init(callback: (field: Field) => any) { - const fieldsPromise = Server.GetFields(this._proxies).then(action((fields: FieldMap) => { - if (!this.arraysEqual(this._proxies, this.data.map(field => field.Id))) { - var dataids = this.data.map(d => d.Id); - var proxies = this._proxies.map(p => p); - var added = this.data.length < this._proxies.length; - var deleted = this.data.length > this._proxies.length; - for (let i = 0; i < dataids.length && added; i++) { - added = proxies.indexOf(dataids[i]) !== -1; - } - for (let i = 0; i < this._proxies.length && deleted; i++) { - deleted = dataids.indexOf(proxies[i]) !== -1; - } - - this._processingServerUpdate = true; - for (let i = 0; i < proxies.length && added; i++) { - if (dataids.indexOf(proxies[i]) === -1) { - this.Data.splice(i, 0, fields[proxies[i]] as T); - } - } - for (let i = dataids.length - 1; i >= 0 && deleted; i--) { - if (proxies.indexOf(dataids[i]) === -1) { - this.Data.splice(i, 1); - } - } - if (!added && !deleted) {// otherwise, just rebuild the whole list - this.setData(proxies.map(id => fields[id] as T)); - } - this._processingServerUpdate = false; - } - })); - - const scriptsPromise = Server.GetFields(this._scriptIds).then((fields: FieldMap) => { - this.scripts = this._scriptIds.map(id => fields[id] as ScriptField); - }); - - Promise.all([fieldsPromise, scriptsPromise]).then(() => callback(this)); - } - - ToScriptString(): string { - return "new ListField([" + this.Data.map(field => field.ToScriptString()).join(", ") + "])"; - } - - Copy(): Field { - return new ListField(this.Data); - } - - - UpdateFromServer(data: { fields: string[], scripts: string[] }) { - this._proxies = data.fields; - this._scriptIds = data.scripts; - } - ToJson() { - return { - type: Types.List, - data: { - fields: this._proxies, - scripts: this._scriptIds, - }, - id: this.Id - }; - } - - static FromJson(id: string, data: { fields: string[], scripts: string[] }): ListField { - let list = new ListField([], [], id, false); - list._proxies = data.fields; - list._scriptIds = data.scripts; - return list; - } -} \ No newline at end of file diff --git a/src/fields/NewDoc.ts b/src/fields/NewDoc.ts deleted file mode 100644 index 70dc1867c..000000000 --- a/src/fields/NewDoc.ts +++ /dev/null @@ -1,358 +0,0 @@ -import { observable, action } from "mobx"; -import { UndoManager } from "../client/util/UndoManager"; -import { serializable, primitive, map, alias, list } from "serializr"; -import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; -import { Utils } from "../Utils"; -import { DocServer } from "../client/DocServer"; - -export const HandleUpdate = Symbol("HandleUpdate"); -const Id = Symbol("Id"); -export abstract class RefField { - @serializable(alias("id", primitive())) - private __id: string; - readonly [Id]: string; - - constructor(id?: string) { - this.__id = id || Utils.GenerateGuid(); - this[Id] = this.__id; - } - - protected [HandleUpdate]?(diff: any): void; -} - -const Update = Symbol("Update"); -const OnUpdate = Symbol("OnUpdate"); -const Parent = Symbol("Parent"); -export class ObjectField { - protected [OnUpdate]?: (diff?: any) => void; - private [Parent]?: Doc; -} - -function url() { - return { - serializer: function (value: URL) { - return value.href; - }, - deserializer: function (jsonValue: string, done: (err: any, val: any) => void) { - done(undefined, new URL(jsonValue)); - } - }; -} - -@Deserializable("url") -export class URLField extends ObjectField { - @serializable(url()) - readonly url: URL; - - constructor(url: URL) { - super(); - this.url = url; - } -} - -@Deserializable("proxy") -export class ProxyField extends ObjectField { - constructor(); - constructor(value: T); - constructor(value?: T) { - super(); - if (value) { - this.cache = value; - this.fieldId = value[Id]; - } - } - - @serializable(primitive()) - readonly fieldId: string = ""; - - // This getter/setter and nested object thing is - // because mobx doesn't play well with observable proxies - @observable.ref - private _cache: { readonly field: T | undefined } = { field: undefined }; - private get cache(): T | undefined { - return this._cache.field; - } - private set cache(field: T | undefined) { - this._cache = { field }; - } - - private failed = false; - private promise?: Promise; - - value(callback?: ((field: T | undefined) => void)): T | undefined | null { - if (this.cache) { - callback && callback(this.cache); - return this.cache; - } - if (this.failed) { - return undefined; - } - if (!this.promise) { - // this.promise = Server.GetField(this.fieldId).then(action((field: any) => { - // this.promise = undefined; - // this.cache = field; - // if (field === undefined) this.failed = true; - // return field; - // })); - this.promise = new Promise(r => r()); - } - callback && this.promise.then(callback); - return null; - } -} - -export type Field = number | string | boolean | ObjectField | RefField; -export type Opt = T | undefined; -export type FieldWaiting = null; -export const FieldWaiting: FieldWaiting = null; - -const Self = Symbol("Self"); - -function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean { - if (SerializationHelper.IsSerializing()) { - target[prop] = value; - return true; - } - if (typeof prop === "symbol") { - target[prop] = value; - return true; - } - const curValue = target.__fields[prop]; - if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) { - // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically - // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way - return true; - } - if (value instanceof RefField) { - value = new ProxyField(value); - } - if (value instanceof ObjectField) { - if (value[Parent] && value[Parent] !== target) { - throw new Error("Can't put the same object in multiple documents at the same time"); - } - value[Parent] = target; - value[OnUpdate] = (diff?: any) => { - if (!diff) diff = SerializationHelper.Serialize(value); - target[Update]({ [prop]: diff }); - }; - } - if (curValue instanceof ObjectField) { - delete curValue[Parent]; - delete curValue[OnUpdate]; - } - target.__fields[prop] = value; - target[Update]({ ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) }); - UndoManager.AddEvent({ - redo: () => receiver[prop] = value, - undo: () => receiver[prop] = curValue - }); - return true; -} - -function getter(target: any, prop: string | symbol | number, receiver: any): any { - if (typeof prop === "symbol") { - return target.__fields[prop] || target[prop]; - } - if (SerializationHelper.IsSerializing()) { - return target[prop]; - } - return getField(target, prop, receiver); -} - -function getField(target: any, prop: string | number, ignoreProto: boolean = false, callback?: (field: Field | undefined) => void): any { - const field = target.__fields[prop]; - if (field instanceof ProxyField) { - return field.value(callback); - } - if (field === undefined && !ignoreProto) { - const proto = getField(target, "prototype", true); - if (proto instanceof Doc) { - let field = proto[prop]; - callback && callback(field === null ? undefined : field); - return field; - } - } - callback && callback(field); - return field; - -} - -@Deserializable("list") -class ListImpl extends ObjectField { - constructor() { - super(); - const list = new Proxy(this, { - set: function (a, b, c, d) { return setter(a, b, c, d); }, - get: getter, - deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); }, - defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, - }); - return list; - } - - [key: number]: T | null | undefined; - - @serializable(alias("fields", list(autoObject()))) - @observable - private __fields: (T | null | undefined)[] = []; - - private [Update] = (diff: any) => { - console.log(diff); - const update = this[OnUpdate]; - update && update(diff); - } - - private [Self] = this; -} -export type List = ListImpl & T[]; -export const List: { new (): List } = ListImpl as any; - -@Deserializable("doc").withFields(["id"]) -export class Doc extends RefField { - constructor(id?: string, forceSave?: boolean) { - super(id); - const doc = new Proxy(this, { - set: setter, - get: getter, - deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); }, - defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, - }); - if (!id || forceSave) { - DocServer.CreateField(SerializationHelper.Serialize(doc)); - } - return doc; - } - - [key: string]: Field | null | undefined; - - @serializable(alias("fields", map(autoObject()))) - @observable - private __fields: { [key: string]: Field | null | undefined } = {}; - - private [Update] = (diff: any) => { - DocServer.UpdateField(this[Id], diff); - } - - private [Self] = this; -} - -export namespace Doc { - export function GetAsync(doc: Doc, key: string, ignoreProto: boolean = false): Promise { - const self = doc[Self]; - return new Promise(res => getField(self, key, ignoreProto, res)); - } - export function GetTAsync(doc: Doc, key: string, ctor: FieldCtor, ignoreProto: boolean = false): Promise { - const self = doc[Self]; - return new Promise(async res => { - const field = await GetAsync(doc, key, ignoreProto); - return Cast(field, ctor); - }); - } - export function Get(doc: Doc, key: string, ignoreProto: boolean = false): Field | null | undefined { - const self = doc[Self]; - return getField(self, key, ignoreProto); - } - export function GetT(doc: Doc, key: string, ctor: FieldCtor, ignoreProto: boolean = false): Field | null | undefined { - return Cast(Get(doc, key, ignoreProto), ctor); - } - export const Prototype = Symbol("Prototype"); -} - -export const GetAsync = Doc.GetAsync; - -export type ToType = - T extends "string" ? string : - T extends "number" ? number : - T extends "boolean" ? boolean : - T extends ListSpec ? List : - T extends { new(...args: any[]): infer R } ? R : never; - -export type ToConstructor = - T extends string ? "string" : - T extends number ? "number" : - T extends boolean ? "boolean" : { new(...args: any[]): T }; - -export type ToInterface = { - [P in keyof T]: ToType; -}; - -// type ListSpec = { List: FieldCtor> | ListSpec> }; -export type ListSpec = { List: FieldCtor }; - -// type ListType = { 0: List>>, 1: ToType> }[HasTail extends true ? 0 : 1]; - -type Head = T extends [any, ...any[]] ? T[0] : never; -type Tail = - ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []; -type HasTail = T extends ([] | [any]) ? false : true; - -interface Interface { - [key: string]: ToConstructor | ListSpec; - // [key: string]: ToConstructor | ListSpec; -} - -type FieldCtor = ToConstructor | ListSpec; - -function Cast>(field: Field | null | undefined, ctor: T): ToType | null | undefined { - if (field !== undefined && field !== null) { - if (typeof ctor === "string") { - if (typeof field === ctor) { - return field as ToType; - } - } else if (typeof ctor === "object") { - if (field instanceof List) { - return field as ToType; - } - } else if (field instanceof (ctor as any)) { - return field as ToType; - } - } else { - return field; - } - return undefined; -} - -export type makeInterface = Partial> & Doc; -export function makeInterface(schema: T): (doc: Doc) => makeInterface { - return function (doc: any) { - return new Proxy(doc, { - get(target, prop) { - const field = target[prop]; - if (prop in schema) { - return Cast(field, (schema as any)[prop]); - } - return field; - } - }); - }; -} - -export type makeStrictInterface = Partial>; -export function makeStrictInterface(schema: T): (doc: Doc) => makeStrictInterface { - const proto = {}; - for (const key in schema) { - const type = schema[key]; - Object.defineProperty(proto, key, { - get() { - return Cast(this.__doc[key], type as any); - }, - set(value) { - value = Cast(value, type as any); - if (value !== undefined) { - this.__doc[key] = value; - return; - } - throw new TypeError("Expected type " + type); - } - }); - } - return function (doc: any) { - const obj = Object.create(proto); - obj.__doc = doc; - return obj; - }; -} - -export function createSchema(schema: T): T { - return schema; -} \ No newline at end of file diff --git a/src/fields/TupleField.ts b/src/fields/TupleField.ts deleted file mode 100644 index 347f1fa05..000000000 --- a/src/fields/TupleField.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { action, IArrayChange, IArraySplice, IObservableArray, observe, observable, Lambda } from "mobx"; -import { Server } from "../client/Server"; -import { UndoManager } from "../client/util/UndoManager"; -import { Types } from "../server/Message"; -import { BasicField } from "./BasicField"; -import { Field, FieldId } from "./Field"; - -export class TupleField extends BasicField<[T, U]> { - constructor(data: [T, U], id?: FieldId, save: boolean = true) { - super(data, save, id); - if (save) { - Server.UpdateField(this); - } - this.observeTuple(); - } - - private observeDisposer: Lambda | undefined; - private observeTuple(): void { - this.observeDisposer = observe(this.Data as (T | U)[] as IObservableArray, (change: IArrayChange | IArraySplice) => { - if (change.type === "update") { - UndoManager.AddEvent({ - undo: () => this.Data[change.index] = change.oldValue, - redo: () => this.Data[change.index] = change.newValue - }); - Server.UpdateField(this); - } else { - throw new Error("Why are you messing with the length of a tuple, huh?"); - } - }); - } - - protected setData(value: [T, U]) { - if (this.observeDisposer) { - this.observeDisposer(); - } - this.data = observable(value) as (T | U)[] as [T, U]; - this.observeTuple(); - } - - UpdateFromServer(values: [T, U]) { - this.setData(values); - } - - ToScriptString(): string { - return `new TupleField([${this.Data[0], this.Data[1]}])`; - } - - Copy(): Field { - return new TupleField(this.Data); - } - - ToJson() { - return { - type: Types.Tuple, - data: this.Data, - id: this.Id - }; - } -} \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts new file mode 100644 index 000000000..c67170573 --- /dev/null +++ b/src/new_fields/Doc.ts @@ -0,0 +1,90 @@ +import { observable, action } from "mobx"; +import { serializable, primitive, map, alias, list } from "serializr"; +import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; +import { Utils } from "../Utils"; +import { DocServer } from "../client/DocServer"; +import { setter, getter, getField } from "./util"; +import { Cast, FieldCtor } from "./Types"; + +export const HandleUpdate = Symbol("HandleUpdate"); +export const Id = Symbol("Id"); +export abstract class RefField { + @serializable(alias("id", primitive())) + private __id: string; + readonly [Id]: string; + + constructor(id?: string) { + this.__id = id || Utils.GenerateGuid(); + this[Id] = this.__id; + } + + protected [HandleUpdate]?(diff: any): void; +} + +export const Update = Symbol("Update"); +export const OnUpdate = Symbol("OnUpdate"); +export const Parent = Symbol("Parent"); +export class ObjectField { + protected [OnUpdate]?: (diff?: any) => void; + private [Parent]?: Doc; +} + +export type Field = number | string | boolean | ObjectField | RefField; +export type Opt = T | undefined; +export type FieldWaiting = null; +export const FieldWaiting: FieldWaiting = null; + +export const Self = Symbol("Self"); + +@Deserializable("doc").withFields(["id"]) +export class Doc extends RefField { + constructor(id?: string, forceSave?: boolean) { + super(id); + const doc = new Proxy(this, { + set: setter, + get: getter, + deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); }, + defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, + }); + if (!id || forceSave) { + DocServer.CreateField(SerializationHelper.Serialize(doc)); + } + return doc; + } + + [key: string]: Field | null | undefined; + + @serializable(alias("fields", map(autoObject()))) + @observable + private __fields: { [key: string]: Field | null | undefined } = {}; + + private [Update] = (diff: any) => { + DocServer.UpdateField(this[Id], diff); + } + + private [Self] = this; +} + +export namespace Doc { + export function GetAsync(doc: Doc, key: string, ignoreProto: boolean = false): Promise { + const self = doc[Self]; + return new Promise(res => getField(self, key, ignoreProto, res)); + } + export function GetTAsync(doc: Doc, key: string, ctor: FieldCtor, ignoreProto: boolean = false): Promise { + const self = doc[Self]; + return new Promise(async res => { + const field = await GetAsync(doc, key, ignoreProto); + return Cast(field, ctor); + }); + } + export function Get(doc: Doc, key: string, ignoreProto: boolean = false): Field | null | undefined { + const self = doc[Self]; + return getField(self, key, ignoreProto); + } + export function GetT(doc: Doc, key: string, ctor: FieldCtor, ignoreProto: boolean = false): Field | null | undefined { + return Cast(Get(doc, key, ignoreProto), ctor); + } + export const Prototype = Symbol("Prototype"); +} + +export const GetAsync = Doc.GetAsync; \ No newline at end of file diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts new file mode 100644 index 000000000..f8e54ade5 --- /dev/null +++ b/src/new_fields/HtmlField.ts @@ -0,0 +1,14 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive } from "serializr"; +import { ObjectField } from "./Doc"; + +@Deserializable("html") +export class URLField extends ObjectField { + @serializable(primitive()) + readonly html: string; + + constructor(html: string) { + super(); + this.html = html; + } +} diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts new file mode 100644 index 000000000..a1a623f83 --- /dev/null +++ b/src/new_fields/List.ts @@ -0,0 +1,35 @@ +import { Deserializable, autoObject } from "../client/util/SerializationHelper"; +import { Field, ObjectField, Update, OnUpdate, Self } from "./Doc"; +import { setter, getter } from "./util"; +import { serializable, alias, list } from "serializr"; +import { observable } from "mobx"; + +@Deserializable("list") +class ListImpl extends ObjectField { + constructor() { + super(); + const list = new Proxy(this, { + set: function (a, b, c, d) { return setter(a, b, c, d); }, + get: getter, + deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); }, + defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, + }); + return list; + } + + [key: number]: T | null | undefined; + + @serializable(alias("fields", list(autoObject()))) + @observable + private __fields: (T | null | undefined)[] = []; + + private [Update] = (diff: any) => { + console.log(diff); + const update = this[OnUpdate]; + update && update(diff); + } + + private [Self] = this; +} +export type List = ListImpl & T[]; +export const List: { new (): List } = ListImpl as any; \ No newline at end of file diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts new file mode 100644 index 000000000..3b4b2e452 --- /dev/null +++ b/src/new_fields/Proxy.ts @@ -0,0 +1,55 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { RefField, Id, ObjectField } from "./Doc"; +import { primitive, serializable } from "serializr"; +import { observable } from "mobx"; + +@Deserializable("proxy") +export class ProxyField extends ObjectField { + constructor(); + constructor(value: T); + constructor(value?: T) { + super(); + if (value) { + this.cache = value; + this.fieldId = value[Id]; + } + } + + @serializable(primitive()) + readonly fieldId: string = ""; + + // This getter/setter and nested object thing is + // because mobx doesn't play well with observable proxies + @observable.ref + private _cache: { readonly field: T | undefined } = { field: undefined }; + private get cache(): T | undefined { + return this._cache.field; + } + private set cache(field: T | undefined) { + this._cache = { field }; + } + + private failed = false; + private promise?: Promise; + + value(callback?: ((field: T | undefined) => void)): T | undefined | null { + if (this.cache) { + callback && callback(this.cache); + return this.cache; + } + if (this.failed) { + return undefined; + } + if (!this.promise) { + // this.promise = Server.GetField(this.fieldId).then(action((field: any) => { + // this.promise = undefined; + // this.cache = field; + // if (field === undefined) this.failed = true; + // return field; + // })); + this.promise = new Promise(r => r()); + } + callback && this.promise.then(callback); + return null; + } +} diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts new file mode 100644 index 000000000..c7d2f0801 --- /dev/null +++ b/src/new_fields/Schema.ts @@ -0,0 +1,47 @@ +import { Interface, ToInterface, Cast } from "./Types"; +import { Doc } from "./Doc"; + +export type makeInterface = Partial> & Doc; +export function makeInterface(schema: T): (doc: Doc) => makeInterface { + return function (doc: any) { + return new Proxy(doc, { + get(target, prop) { + const field = target[prop]; + if (prop in schema) { + return Cast(field, (schema as any)[prop]); + } + return field; + } + }); + }; +} + +export type makeStrictInterface = Partial>; +export function makeStrictInterface(schema: T): (doc: Doc) => makeStrictInterface { + const proto = {}; + for (const key in schema) { + const type = schema[key]; + Object.defineProperty(proto, key, { + get() { + return Cast(this.__doc[key], type as any); + }, + set(value) { + value = Cast(value, type as any); + if (value !== undefined) { + this.__doc[key] = value; + return; + } + throw new TypeError("Expected type " + type); + } + }); + } + return function (doc: any) { + const obj = Object.create(proto); + obj.__doc = doc; + return obj; + }; +} + +export function createSchema(schema: T): T { + return schema; +} diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts new file mode 100644 index 000000000..416298a64 --- /dev/null +++ b/src/new_fields/Types.ts @@ -0,0 +1,58 @@ +import { Field, Opt } from "./Doc"; +import { List } from "./List"; + +export type ToType = + T extends "string" ? string : + T extends "number" ? number : + T extends "boolean" ? boolean : + T extends ListSpec ? List : + T extends { new(...args: any[]): infer R } ? R : never; + +export type ToConstructor = + T extends string ? "string" : + T extends number ? "number" : + T extends boolean ? "boolean" : { new(...args: any[]): T }; + +export type ToInterface = { + [P in keyof T]: ToType; +}; + +// type ListSpec = { List: FieldCtor> | ListSpec> }; +export type ListSpec = { List: FieldCtor }; + +// type ListType = { 0: List>>, 1: ToType> }[HasTail extends true ? 0 : 1]; + +export type Head = T extends [any, ...any[]] ? T[0] : never; +export type Tail = + ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []; +export type HasTail = T extends ([] | [any]) ? false : true; + +export interface Interface { + [key: string]: ToConstructor | ListSpec; + // [key: string]: ToConstructor | ListSpec; +} + +export type FieldCtor = ToConstructor | ListSpec; + +export function Cast>(field: Field | null | undefined, ctor: T): ToType | null | undefined { + if (field !== undefined && field !== null) { + if (typeof ctor === "string") { + if (typeof field === ctor) { + return field as ToType; + } + } else if (typeof ctor === "object") { + if (field instanceof List) { + return field as ToType; + } + } else if (field instanceof (ctor as any)) { + return field as ToType; + } + } else { + return field; + } + return undefined; +} + +export function FieldValue(field: Opt | Promise>): Opt { + return field instanceof Promise ? undefined : field; +} diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts new file mode 100644 index 000000000..d27a2b692 --- /dev/null +++ b/src/new_fields/URLField.ts @@ -0,0 +1,25 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable } from "serializr"; +import { ObjectField } from "./Doc"; + +function url() { + return { + serializer: function (value: URL) { + return value.href; + }, + deserializer: function (jsonValue: string, done: (err: any, val: any) => void) { + done(undefined, new URL(jsonValue)); + } + }; +} + +@Deserializable("url") +export class URLField extends ObjectField { + @serializable(url()) + readonly url: URL; + + constructor(url: URL) { + super(); + this.url = url; + } +} \ No newline at end of file diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts new file mode 100644 index 000000000..0f08ecf03 --- /dev/null +++ b/src/new_fields/util.ts @@ -0,0 +1,73 @@ +import { UndoManager } from "../client/util/UndoManager"; +import { Update, OnUpdate, Parent, ObjectField, RefField, Doc, Id, Field } from "./Doc"; +import { SerializationHelper } from "../client/util/SerializationHelper"; +import { ProxyField } from "./Proxy"; + +export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean { + if (SerializationHelper.IsSerializing()) { + target[prop] = value; + return true; + } + if (typeof prop === "symbol") { + target[prop] = value; + return true; + } + const curValue = target.__fields[prop]; + if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) { + // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically + // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way + return true; + } + if (value instanceof RefField) { + value = new ProxyField(value); + } + if (value instanceof ObjectField) { + if (value[Parent] && value[Parent] !== target) { + throw new Error("Can't put the same object in multiple documents at the same time"); + } + value[Parent] = target; + value[OnUpdate] = (diff?: any) => { + if (!diff) diff = SerializationHelper.Serialize(value); + target[Update]({ [prop]: diff }); + }; + } + if (curValue instanceof ObjectField) { + delete curValue[Parent]; + delete curValue[OnUpdate]; + } + target.__fields[prop] = value; + target[Update]({ ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) }); + UndoManager.AddEvent({ + redo: () => receiver[prop] = value, + undo: () => receiver[prop] = curValue + }); + return true; +} + +export function getter(target: any, prop: string | symbol | number, receiver: any): any { + if (typeof prop === "symbol") { + return target.__fields[prop] || target[prop]; + } + if (SerializationHelper.IsSerializing()) { + return target[prop]; + } + return getField(target, prop, receiver); +} + +export function getField(target: any, prop: string | number, ignoreProto: boolean = false, callback?: (field: Field | undefined) => void): any { + const field = target.__fields[prop]; + if (field instanceof ProxyField) { + return field.value(callback); + } + if (field === undefined && !ignoreProto) { + const proto = getField(target, "prototype", true); + if (proto instanceof Doc) { + let field = proto[prop]; + callback && callback(field === null ? undefined : field); + return field; + } + } + callback && callback(field); + return field; + +} -- cgit v1.2.3-70-g09d2 From 50a950238def89ae30d386676bbfe884c8b8c7be Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 24 Apr 2019 02:23:36 -0400 Subject: Added get fields and did some more fixing --- src/client/DocServer.ts | 25 +++ src/client/documents/Documents.ts | 43 ++-- src/fields/Document.ts | 430 -------------------------------------- src/fields/IconFIeld.ts | 25 --- src/fields/InkField.ts | 53 ----- src/new_fields/IconField.ts | 14 ++ src/new_fields/InkField.ts | 31 +++ src/server/Message.ts | 1 + src/server/index.ts | 6 +- 9 files changed, 101 insertions(+), 527 deletions(-) delete mode 100644 src/fields/Document.ts delete mode 100644 src/fields/IconFIeld.ts delete mode 100644 src/fields/InkField.ts create mode 100644 src/new_fields/IconField.ts create mode 100644 src/new_fields/InkField.ts (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 615e48af0..cba0c4770 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -30,6 +30,31 @@ export namespace DocServer { } } + export async function GetRefFields(ids: string[]): Promise<{ [id: string]: Opt }> { + const requestedIds: string[] = []; + const waitingIds: string[] = []; + const promises: Promise>[] = []; + const map: { [id: string]: Opt } = {}; + for (const id of ids) { + const cached = _cache[id]; + if (cached === undefined) { + requestedIds.push(id); + } else if (cached instanceof Promise) { + promises.push(cached); + waitingIds.push(id); + } else { + map[id] = cached; + } + } + const prom = Utils.EmitCallback(_socket, MessageStore.GetFields, requestedIds); + requestedIds.map((id, index) => _cache[id] = prom.then((fields: RefField[]) => fields[index])); + const fields = await prom; + requestedIds.map((id, index) => map[id] = fields[index]); + const otherFields = await Promise.all(promises); + waitingIds.map((id, index) => map[id] = otherFields[index]); + return map; + } + export function UpdateField(id: string, diff: any) { Utils.Emit(_socket, MessageStore.UpdateField, { id, diff }); } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a145a76c9..a5ce7c076 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -26,6 +26,9 @@ import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../ne import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; import { Cast } from "../../new_fields/Types"; +import { IconField } from "../../new_fields/IconField"; +import { listSpec } from "../../new_fields/Schema"; +import { DocServer } from "../DocServer"; export interface DocumentOptions { x?: number; @@ -73,17 +76,17 @@ export namespace Docs { const iconProtoId = "iconProto"; export function initProtos(): Promise { - return Server.GetFields([textProtoId, histoProtoId, collProtoId, pdfProtoId, imageProtoId, videoProtoId, audioProtoId, webProtoId, kvpProtoId]).then(fields => { - textProto = fields[textProtoId] as Document || CreateTextPrototype(); - histoProto = fields[histoProtoId] as Document || CreateHistogramPrototype(); - collProto = fields[collProtoId] as Document || CreateCollectionPrototype(); - imageProto = fields[imageProtoId] as Document || CreateImagePrototype(); - webProto = fields[webProtoId] as Document || CreateWebPrototype(); - kvpProto = fields[kvpProtoId] as Document || CreateKVPPrototype(); - videoProto = fields[videoProtoId] as Document || CreateVideoPrototype(); - audioProto = fields[audioProtoId] as Document || CreateAudioPrototype(); - pdfProto = fields[pdfProtoId] as Document || CreatePdfPrototype(); - iconProto = fields[iconProtoId] as Document || CreateIconPrototype(); + return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, pdfProtoId, imageProtoId, videoProtoId, audioProtoId, webProtoId, kvpProtoId]).then(fields => { + textProto = fields[textProtoId] as Doc || CreateTextPrototype(); + histoProto = fields[histoProtoId] as Doc || CreateHistogramPrototype(); + collProto = fields[collProtoId] as Doc || CreateCollectionPrototype(); + imageProto = fields[imageProtoId] as Doc || CreateImagePrototype(); + webProto = fields[webProtoId] as Doc || CreateWebPrototype(); + kvpProto = fields[kvpProtoId] as Doc || CreateKVPPrototype(); + videoProto = fields[videoProtoId] as Doc || CreateVideoPrototype(); + audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); + pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); + iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); }); } @@ -191,25 +194,29 @@ export namespace Docs { let ctlog = await Gateway.Instance.GetSchema(url, schemaName); if (ctlog && ctlog.schemas) { let schema = ctlog.schemas[0]; - let schemaDoc = Documents.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); - let schemaDocuments = schemaDoc.GetList(KeyStore.Data, [] as Document[]); + let schemaDoc = Docs.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); + let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc)); + if (!schemaDocuments) { + return; + } + const docs = schemaDocuments; CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { - Server.GetField(attr.displayName! + ".alias", action((field: Opt) => { - if (field instanceof Document) { - schemaDocuments.push(field); + DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { + if (field instanceof Doc) { + docs.push(field); } else { var atmod = new ColumnAttributeModel(attr); let histoOp = new HistogramOperation(schema.displayName!, new AttributeTransformationModel(atmod, AggregateFunction.None), new AttributeTransformationModel(atmod, AggregateFunction.Count), new AttributeTransformationModel(atmod, AggregateFunction.Count)); - schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias")); + docs.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias")); } })); }); return schemaDoc; } - return Documents.TreeDocument([], { width: 50, height: 100, title: schemaName }); + return Docs.TreeDocument([], { width: 50, height: 100, title: schemaName }); } export function WebDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(webProto, new WebField(new URL(url)), options); diff --git a/src/fields/Document.ts b/src/fields/Document.ts deleted file mode 100644 index 7cf784f0e..000000000 --- a/src/fields/Document.ts +++ /dev/null @@ -1,430 +0,0 @@ -import { Key } from "./Key"; -import { KeyStore } from "./KeyStore"; -import { Field, Cast, FieldWaiting, FieldValue, FieldId, Opt } from "./Field"; -import { NumberField } from "./NumberField"; -import { ObservableMap, computed, action, runInAction } from "mobx"; -import { TextField } from "./TextField"; -import { ListField } from "./ListField"; -import { Server } from "../client/Server"; -import { Types } from "../server/Message"; -import { UndoManager } from "../client/util/UndoManager"; -import { HtmlField } from "./HtmlField"; -import { BooleanField } from "./BooleanField"; -import { allLimit } from "async"; -import { prototype } from "nodemailer/lib/smtp-pool"; -import { HistogramField } from "../client/northstar/dash-fields/HistogramField"; - -export class Document extends Field { - //TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field - public fields: ObservableMap = new ObservableMap(); - public _proxies: ObservableMap = new ObservableMap(); - - constructor(id?: string, save: boolean = true) { - super(id); - - if (save) { - Server.UpdateField(this); - } - } - static FromJson(data: any, id: string, save: boolean): Document { - let doc = new Document(id, save); - let fields = data as [string, string][]; - fields.forEach(pair => doc._proxies.set(pair[0], pair[1])); - return doc; - } - - UpdateFromServer(data: [string, string][]) { - for (const key in data) { - const element = data[key]; - this._proxies.set(element[0], element[1]); - } - } - - public Width = () => this.GetNumber(KeyStore.Width, 0); - public Height = () => this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? (this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0)) * this.GetNumber(KeyStore.Width, 0) : 0); - public Scale = () => this.GetNumber(KeyStore.Scale, 1); - - @computed - public get Title(): string { - let title = this.Get(KeyStore.Title, true); - if (title || title === FieldWaiting) { - if (title !== FieldWaiting && title instanceof TextField) { - return title.Data; - } - else return "-waiting-"; - } - let parTitle = this.GetT(KeyStore.Title, TextField); - if (parTitle || parTitle === FieldWaiting) { - if (parTitle !== FieldWaiting) return parTitle.Data + ".alias"; - else return "-waiting-.alias"; - } - return "-untitled-"; - } - - @computed - public get Fields() { - return this.fields; - } - - /** - * Get the field in the document associated with the given key. If the - * associated field has not yet been filled in from the server, a request - * to the server will automatically be sent, the value will be filled in - * when the request is completed, and {@link Field.ts#FieldWaiting} will be returned. - * @param key - The key of the value to get - * @param ignoreProto - If true, ignore any prototype this document - * might have and only search for the value on this immediate document. - * If false (default), search up the prototype chain, starting at this document, - * for a document that has a field associated with the given key, and return the first - * one found. - * - * @returns If the document does not have a field associated with the given key, returns `undefined`. - * If the document does have an associated field, but the field has not been fetched from the server, returns {@link Field.ts#FieldWaiting}. - * If the document does have an associated field, and the field has not been fetched from the server, returns the associated field. - */ - Get(key: Key, ignoreProto: boolean = false): FieldValue { - let field: FieldValue; - if (ignoreProto) { - if (this.fields.has(key.Id)) { - field = this.fields.get(key.Id)!.field; - } else if (this._proxies.has(key.Id)) { - Server.GetDocumentField(this, key); - /* - The field might have been instantly filled from the cache - Maybe we want to just switch back to returning the value - from Server.GetDocumentField if it's in the cache - */ - if (this.fields.has(key.Id)) { - field = this.fields.get(key.Id)!.field; - } else { - field = FieldWaiting; - } - } - } else { - let doc: FieldValue = this; - while (doc && field !== FieldWaiting) { - let curField = doc.fields.get(key.Id); - let curProxy = doc._proxies.get(key.Id); - if (!curField || (curProxy && curField.field.Id !== curProxy)) { - if (curProxy) { - Server.GetDocumentField(doc, key); - /* - The field might have been instantly filled from the cache - Maybe we want to just switch back to returning the value - from Server.GetDocumentField if it's in the cache - */ - if (this.fields.has(key.Id)) { - field = this.fields.get(key.Id)!.field; - } else { - field = FieldWaiting; - } - break; - } - if ( - doc.fields.has(KeyStore.Prototype.Id) || - doc._proxies.has(KeyStore.Prototype.Id) - ) { - doc = doc.GetPrototype(); - } else { - break; - } - } else { - field = curField.field; - break; - } - } - if (doc === FieldWaiting) field = FieldWaiting; - } - - return field; - } - - /** - * Tries to get the field associated with the given key, and if there is an - * associated field, calls the given callback with that field. - * @param key - The key of the value to get - * @param callback - A function that will be called with the associated field, if it exists, - * once it is fetched from the server (this may be immediately if the field has already been fetched). - * Note: The callback will not be called if there is no associated field. - * @returns `true` if the field exists on the document and `callback` will be called, and `false` otherwise - */ - GetAsync(key: Key, callback: (field: Opt) => void): void { - //TODO: This currently doesn't deal with prototypes - let field = this.fields.get(key.Id); - if (field && field.field) { - callback(field.field); - } else if (this._proxies.has(key.Id)) { - Server.GetDocumentField(this, key, callback); - } else if (this._proxies.has(KeyStore.Prototype.Id)) { - this.GetTAsync(KeyStore.Prototype, Document, proto => { - if (proto) { - proto.GetAsync(key, callback); - } else { - callback(undefined); - } - }); - } else { - callback(undefined); - } - } - - GetTAsync(key: Key, ctor: { new(): T }): Promise>; - GetTAsync( - key: Key, - ctor: { new(): T }, - callback: (field: Opt) => void - ): void; - GetTAsync( - key: Key, - ctor: { new(): T }, - callback?: (field: Opt) => void - ): Promise> | void { - let fn = (cb: (field: Opt) => void) => { - return this.GetAsync(key, field => { - cb(Cast(field, ctor)); - }); - }; - if (callback) { - fn(callback); - } else { - return new Promise(fn); - } - } - - /** - * Same as {@link Document#GetAsync}, except a field of the given type - * will be created if there is no field associated with the given key, - * or the field associated with the given key is not of the given type. - * @param ctor - Constructor of the field type to get. E.g., TextField, ImageField, etc. - */ - GetOrCreateAsync( - key: Key, - ctor: { new(): T }, - callback: (field: T) => void - ): void { - //This currently doesn't deal with prototypes - if (this._proxies.has(key.Id)) { - Server.GetDocumentField(this, key, field => { - if (field && field instanceof ctor) { - callback(field); - } else { - let newField = new ctor(); - this.Set(key, newField); - callback(newField); - } - }); - } else { - let newField = new ctor(); - this.Set(key, newField); - callback(newField); - } - } - - /** - * Same as {@link Document#Get}, except that it will additionally - * check if the field is of the given type. - * @param ctor - Constructor of the field type to get. E.g., `TextField`, `ImageField`, etc. - * @returns Same as {@link Document#Get}, except will return `undefined` - * if there is an associated field but it is of the wrong type. - */ - GetT( - key: Key, - ctor: { new(...args: any[]): T }, - ignoreProto: boolean = false - ): FieldValue { - var getfield = this.Get(key, ignoreProto); - if (getfield !== FieldWaiting) { - return Cast(getfield, ctor); - } - return FieldWaiting; - } - - GetOrCreate( - key: Key, - ctor: { new(): T }, - ignoreProto: boolean = false - ): T { - const field = this.GetT(key, ctor, ignoreProto); - if (field && field !== FieldWaiting) { - return field; - } - const newField = new ctor(); - this.Set(key, newField); - return newField; - } - - GetData( - key: Key, - ctor: { new(): U }, - defaultVal: T - ): T { - let val = this.Get(key); - let vval = val && val instanceof ctor ? val.Data : defaultVal; - return vval; - } - - GetHtml(key: Key, defaultVal: string): string { - return this.GetData(key, HtmlField, defaultVal); - } - - GetBoolean(key: Key, defaultVal: boolean): boolean { - return this.GetData(key, BooleanField, defaultVal); - } - - GetNumber(key: Key, defaultVal: number): number { - return this.GetData(key, NumberField, defaultVal); - } - - GetText(key: Key, defaultVal: string): string { - return this.GetData(key, TextField, defaultVal); - } - - GetList(key: Key, defaultVal: T[]): T[] { - return this.GetData>(key, ListField, defaultVal); - } - - @action - Set(key: Key, field: Field | undefined, setOnPrototype = false): void { - let old = this.fields.get(key.Id); - let oldField = old ? old.field : undefined; - if (setOnPrototype) { - this.SetOnPrototype(key, field); - } else { - if (field) { - this.fields.set(key.Id, { key, field }); - this._proxies.set(key.Id, field.Id); - // Server.AddDocumentField(this, key, field); - } else { - this.fields.delete(key.Id); - this._proxies.delete(key.Id); - // Server.DeleteDocumentField(this, key); - } - Server.UpdateField(this); - } - if (oldField || field) { - UndoManager.AddEvent({ - undo: () => this.Set(key, oldField, setOnPrototype), - redo: () => this.Set(key, field, setOnPrototype) - }); - } - } - - @action - SetOnPrototype(key: Key, field: Field | undefined): void { - this.GetTAsync(KeyStore.Prototype, Document, (f: Opt) => { - f && f.Set(key, field); - }); - } - - @action - SetDataOnPrototype(key: Key, value: T, ctor: { new(): U }, replaceWrongType = true) { - this.GetTAsync(KeyStore.Prototype, Document, (f: Opt) => { - f && f.SetData(key, value, ctor, replaceWrongType); - }); - } - - @action - SetData(key: Key, value: T, ctor: { new(data: T): U }, replaceWrongType = true) { - let field = this.Get(key, true); - if (field instanceof ctor) { - field.Data = value; - } else if (!field || replaceWrongType) { - let newField = new ctor(value); - // newField.Data = value; - this.Set(key, newField); - } - } - - @action - SetText(key: Key, value: string, replaceWrongType = true) { - this.SetData(key, value, TextField, replaceWrongType); - } - @action - SetBoolean(key: Key, value: boolean, replaceWrongType = true) { - this.SetData(key, value, BooleanField, replaceWrongType); - } - @action - SetNumber(key: Key, value: number, replaceWrongType = true) { - this.SetData(key, value, NumberField, replaceWrongType); - } - - GetPrototype(): FieldValue { - return this.GetT(KeyStore.Prototype, Document, true); - } - - GetAllPrototypes(): Document[] { - let protos: Document[] = []; - let doc: FieldValue = this; - while (doc && doc !== FieldWaiting) { - protos.push(doc); - doc = doc.GetPrototype(); - } - return protos; - } - - CreateAlias(id?: string): Document { - let alias = new Document(id); - this.GetTAsync(KeyStore.Prototype, Document, (f: Opt) => { - f && alias.Set(KeyStore.Prototype, f); - }); - - return alias; - } - - MakeDelegate(id?: string): Document { - let delegate = new Document(id); - - delegate.Set(KeyStore.Prototype, this); - - return delegate; - } - - ToScriptString(): string { - return ""; - } - - TrySetValue(value: any): boolean { - throw new Error("Method not implemented."); - } - GetValue() { - return this.Title; - var title = (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) + "(" + this.Id + ")"; - return title; - //throw new Error("Method not implemented."); - } - Copy(copyProto?: boolean, id?: string): Field { - let copy = new Document(); - this._proxies.forEach((fieldid, keyid) => { // copy each prototype field - let key = KeyStore.KeyLookup(keyid); - if (key) { - this.GetAsync(key, (field: Opt) => { - if (key === KeyStore.Prototype && copyProto) { // handle prototype field specially - if (field instanceof Document) { - copy.Set(key, field.Copy(false)); // only copying one level of prototypes for now... - } - } - else - if (field instanceof Document) { // ... TODO bcz: should we copy documents or reference them - copy.Set(key!, field); - } - else if (field) { - copy.Set(key!, field.Copy()); - } - }); - } - }); - return copy; - } - - ToJson() { - let fields: [string, string][] = []; - this._proxies.forEach((field, key) => - field && fields.push([key, field])); - - return { - type: Types.Document, - data: fields, - id: this.Id - }; - } -} diff --git a/src/fields/IconFIeld.ts b/src/fields/IconFIeld.ts deleted file mode 100644 index a6694cc49..000000000 --- a/src/fields/IconFIeld.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BasicField } from "./BasicField"; -import { FieldId } from "./Field"; -import { Types } from "../server/Message"; - -export class IconField extends BasicField { - constructor(data: string = "", id?: FieldId, save: boolean = true) { - super(data, save, id); - } - - ToScriptString(): string { - return `new IconField("${this.Data}")`; - } - - Copy() { - return new IconField(this.Data); - } - - ToJson() { - return { - type: Types.Icon, - data: this.Data, - id: this.Id - }; - } -} \ No newline at end of file diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts deleted file mode 100644 index 2eacd7d0c..000000000 --- a/src/fields/InkField.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { BasicField } from "./BasicField"; -import { Types } from "../server/Message"; -import { FieldId } from "./Field"; -import { observable, ObservableMap } from "mobx"; - -export enum InkTool { - None, - Pen, - Highlighter, - Eraser -} -export interface StrokeData { - pathData: Array<{ x: number, y: number }>; - color: string; - width: string; - tool: InkTool; - page: number; -} -export type StrokeMap = Map; - -export class InkField extends BasicField { - constructor(data: StrokeMap = new Map, id?: FieldId, save: boolean = true) { - super(data, save, id); - } - - ToScriptString(): string { - return `new InkField("${this.Data}")`; - } - - Copy() { - return new InkField(this.Data); - } - - ToJson() { - return { - type: Types.Ink, - data: this.Data, - id: this.Id, - }; - } - - UpdateFromServer(data: any) { - this.data = new ObservableMap(data); - } - - static FromJson(id: string, data: any): InkField { - let map: StrokeMap = new Map(); - Object.keys(data).forEach(key => { - map.set(key, data[key]); - }); - return new InkField(map, id, false); - } -} \ No newline at end of file diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts new file mode 100644 index 000000000..32f3aa4d5 --- /dev/null +++ b/src/new_fields/IconField.ts @@ -0,0 +1,14 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive } from "serializr"; +import { ObjectField } from "./Doc"; + +@Deserializable("icon") +export class IconField extends ObjectField { + @serializable(primitive()) + readonly layout: string; + + constructor(layout: string) { + super(); + this.layout = layout; + } +} diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts new file mode 100644 index 000000000..cdb34cedf --- /dev/null +++ b/src/new_fields/InkField.ts @@ -0,0 +1,31 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; +import { ObjectField } from "./Doc"; + +export enum InkTool { + None, + Pen, + Highlighter, + Eraser +} +export interface StrokeData { + pathData: Array<{ x: number, y: number }>; + color: string; + width: string; + tool: InkTool; + page: number; +} + +const pointSchema = createSimpleSchema({ + x: true, y: true +}); + +const strokeDataSchema = createSimpleSchema({ + pathData: list(object(pointSchema)), + "*": true +}); + +export class InkField extends ObjectField { + @serializable(map(object(strokeDataSchema))) + readonly inkData: Map = new Map; +} diff --git a/src/server/Message.ts b/src/server/Message.ts index b01934724..81da44f72 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -42,6 +42,7 @@ export namespace MessageStore { export const DeleteAll = new Message("Delete All"); export const GetRefField = new Message("Get Ref Field"); + export const GetRefFields = new Message("Get Ref Fields"); export const UpdateField = new Message("Update Ref Field"); export const CreateField = new Message("Create Ref Field"); } diff --git a/src/server/index.ts b/src/server/index.ts index d6d5f0e55..10158eb96 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -16,7 +16,6 @@ import { Socket } from 'socket.io'; import * as webpack from 'webpack'; import * as wdm from 'webpack-dev-middleware'; import * as whm from 'webpack-hot-middleware'; -import { Field, FieldId } from '../fields/Field'; import { Utils } from '../Utils'; import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/controllers/user_controller'; import { DashUserModel } from './authentication/models/user_model'; @@ -236,6 +235,7 @@ server.on("connection", function (socket: Socket) { Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField); Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff)); Utils.AddServerHandler(socket, MessageStore.GetRefField, GetRefField); + Utils.AddServerHandler(socket, MessageStore.GetRefFields, GetRefFields); }); function deleteFields() { @@ -270,6 +270,10 @@ function GetRefField([id, callback]: [string, (result?: Transferable) => void]) Database.Instance.getDocument(id, callback, "newDocuments"); } +function GetRefFields([ids, callback]: [string[], (result?: Transferable[]) => void]) { + Database.Instance.getDocuments(ids, callback, "newDocuments"); +} + function UpdateField(socket: Socket, diff: Diff) { Database.Instance.update(diff.id, diff.diff, () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false, "newDocuments"); -- cgit v1.2.3-70-g09d2 From e1a65c5fa52dd094cc48c0c85b3c92ae8789666d Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 25 Apr 2019 01:26:28 -0400 Subject: Bunch of files --- src/client/DocServer.ts | 4 + src/client/Server.ts | 173 --------------------- src/client/SocketStub.ts | 69 -------- src/client/util/Scripting.ts | 33 +--- .../views/collections/CollectionBaseView.tsx | 76 +++++---- src/client/views/nodes/LinkMenu.tsx | 26 ++-- .../authentication/models/current_user_utils.ts | 28 ++-- 7 files changed, 73 insertions(+), 336 deletions(-) delete mode 100644 src/client/Server.ts delete mode 100644 src/client/SocketStub.ts (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index cba0c4770..02fd28a86 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -9,6 +9,10 @@ export namespace DocServer { const _socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`); const GUID: string = Utils.GenerateGuid(); + export function prepend(extension: string): string { + return window.location.origin + extension; + } + export async function GetRefField(id: string): Promise> { let cached = _cache[id]; if (cached === undefined) { diff --git a/src/client/Server.ts b/src/client/Server.ts deleted file mode 100644 index 66e9878d9..000000000 --- a/src/client/Server.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { Key } from "../fields/Key"; -import { ObservableMap, action, reaction, runInAction } from "mobx"; -import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field"; -import { Document } from "../fields/Document"; -import { SocketStub, FieldMap } from "./SocketStub"; -import * as OpenSocket from 'socket.io-client'; -import { Utils, emptyFunction } from "./../Utils"; -import { MessageStore, Types } from "./../server/Message"; - -export class Server { - public static ClientFieldsCached: ObservableMap = new ObservableMap(); - static Socket: SocketIOClient.Socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`); - static GUID: string = Utils.GenerateGuid(); - - // Retrieves the cached value of the field and sends a request to the server for the real value (if it's not cached). - // Call this is from within a reaction and test whether the return value is FieldWaiting. - public static GetField(fieldid: FieldId): Promise>; - public static GetField(fieldid: FieldId, callback: (field: Opt) => void): void; - public static GetField(fieldid: FieldId, callback?: (field: Opt) => void): Promise> | void { - let fn = (cb: (field: Opt) => void) => { - - let cached = this.ClientFieldsCached.get(fieldid); - if (cached === undefined) { - this.ClientFieldsCached.set(fieldid, FieldWaiting); - SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => { - let cached = this.ClientFieldsCached.get(fieldid); - if (cached !== FieldWaiting) { - cb(cached); - } - else { - if (field) { - this.ClientFieldsCached.set(fieldid, field); - } else { - this.ClientFieldsCached.delete(fieldid); - } - cb(field); - } - })); - } else if (cached !== FieldWaiting) { - setTimeout(() => cb(cached as Field), 0); - } else { - reaction(() => this.ClientFieldsCached.get(fieldid), - (field, reaction) => { - if (field !== FieldWaiting) { - reaction.dispose(); - cb(field); - } - }); - } - }; - if (callback) { - fn(callback); - } else { - return new Promise(fn); - } - } - - public static GetFields(fieldIds: FieldId[]): Promise<{ [id: string]: Field }>; - public static GetFields(fieldIds: FieldId[], callback: (fields: FieldMap) => any): void; - public static GetFields(fieldIds: FieldId[], callback?: (fields: FieldMap) => any): Promise | void { - let fn = action((cb: (fields: FieldMap) => void) => { - - let neededFieldIds: FieldId[] = []; - let waitingFieldIds: FieldId[] = []; - let existingFields: FieldMap = {}; - for (let id of fieldIds) { - let field = this.ClientFieldsCached.get(id); - if (field === undefined) { - neededFieldIds.push(id); - this.ClientFieldsCached.set(id, FieldWaiting); - } else if (field === FieldWaiting) { - waitingFieldIds.push(id); - } else { - existingFields[id] = field; - } - } - SocketStub.SEND_FIELDS_REQUEST(neededFieldIds, action((fields: FieldMap) => { - for (let id of neededFieldIds) { - let field = fields[id]; - if (field) { - if (this.ClientFieldsCached.get(field.Id) === FieldWaiting) { - this.ClientFieldsCached.set(field.Id, field); - } else { - throw new Error("we shouldn't be trying to replace things that are already in the cache"); - } - } else { - if (this.ClientFieldsCached.get(id) === FieldWaiting) { - this.ClientFieldsCached.delete(id); - } else { - throw new Error("we shouldn't be trying to replace things that are already in the cache"); - } - } - } - reaction(() => waitingFieldIds.map(id => this.ClientFieldsCached.get(id)), - (cachedFields, reaction) => { - if (!cachedFields.some(field => field === FieldWaiting)) { - const realFields = cachedFields as Opt[]; - reaction.dispose(); - waitingFieldIds.forEach((id, index) => { - existingFields[id] = realFields[index]; - }); - cb({ ...fields, ...existingFields }); - } - }, { fireImmediately: true }); - })); - }); - if (callback) { - fn(callback); - } else { - return new Promise(fn); - } - } - - public static GetDocumentField(doc: Document, key: Key, callback?: (field: Field) => void) { - let field = doc._proxies.get(key.Id); - if (field) { - this.GetField(field, - action((fieldfromserver: Opt) => { - if (fieldfromserver) { - doc.fields.set(key.Id, { key, field: fieldfromserver }); - if (callback) { - callback(fieldfromserver); - } - } - })); - } - } - - public static DeleteDocumentField(doc: Document, key: Key) { - SocketStub.SEND_DELETE_DOCUMENT_FIELD(doc, key); - } - - public static UpdateField(field: Field) { - if (!this.ClientFieldsCached.has(field.Id)) { - this.ClientFieldsCached.set(field.Id, field); - } - SocketStub.SEND_SET_FIELD(field); - } - - static connected(message: string) { - Server.Socket.emit(MessageStore.Bar.Message, Server.GUID); - } - - @action - private static cacheField(clientField: Field) { - var cached = this.ClientFieldsCached.get(clientField.Id); - if (!cached) { - this.ClientFieldsCached.set(clientField.Id, clientField); - } else { - // probably should overwrite the values within any field that was already here... - } - return this.ClientFieldsCached.get(clientField.Id); - } - - @action - static updateField(field: { id: string, data: any, type: Types }) { - if (Server.ClientFieldsCached.has(field.id)) { - var f = Server.ClientFieldsCached.get(field.id); - if (f) { - // console.log("Applying : " + field.id); - f.UpdateFromServer(field.data); - f.init(emptyFunction); - } else { - // console.log("Not applying wa : " + field.id); - } - } else { - // console.log("Not applying mi : " + field.id); - } - } -} - -Utils.AddServerHandler(Server.Socket, MessageStore.Foo, Server.connected); -Utils.AddServerHandler(Server.Socket, MessageStore.SetField, Server.updateField); diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts deleted file mode 100644 index 382a81f66..000000000 --- a/src/client/SocketStub.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Key } from "../fields/Key"; -import { Field, FieldId, Opt } from "../fields/Field"; -import { ObservableMap } from "mobx"; -import { Document } from "../fields/Document"; -import { MessageStore, Transferable } from "../server/Message"; -import { Utils } from "../Utils"; -import { Server } from "./Server"; -import { ServerUtils } from "../server/ServerUtil"; - - -export interface FieldMap { - [id: string]: Opt; -} - -//TODO tfs: I think it might be cleaner to not have SocketStub deal with turning what the server gives it into Fields (in other words not call ServerUtils.FromJson), and leave that for the Server class. -export class SocketStub { - - static FieldStore: ObservableMap = new ObservableMap(); - - public static SEND_FIELD_REQUEST(fieldid: FieldId): Promise>; - public static SEND_FIELD_REQUEST(fieldid: FieldId, callback: (field: Opt) => void): void; - public static SEND_FIELD_REQUEST(fieldid: FieldId, callback?: (field: Opt) => void): Promise> | void { - let fn = function (cb: (field: Opt) => void) { - Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: Transferable) => { - if (field) { - ServerUtils.FromJson(field).init(cb); - } else { - cb(undefined); - } - }); - }; - if (callback) { - fn(callback); - } else { - return new Promise(fn); - } - } - - public static SEND_FIELDS_REQUEST(fieldIds: FieldId[], callback: (fields: FieldMap) => any) { - Utils.EmitCallback(Server.Socket, MessageStore.GetFields, fieldIds, (fields: Transferable[]) => { - let fieldMap: FieldMap = {}; - fields.map(field => fieldMap[field.id] = ServerUtils.FromJson(field)); - let proms = Object.values(fieldMap).map(val => - new Promise(resolve => val!.init(resolve))); - Promise.all(proms).then(() => callback(fieldMap)); - }); - } - - public static SEND_DELETE_DOCUMENT_FIELD(doc: Document, key: Key) { - // Send a request to delete the field stored under the specified key from the document - - // ...SOCKET(DELETE_DOCUMENT_FIELD, document id, key id) - - // Server removes the field id from the document's list of field proxies - var document = this.FieldStore.get(doc.Id) as Document; - if (document) { - document._proxies.delete(key.Id); - } - } - - public static SEND_SET_FIELD(field: Field) { - // Send a request to set the value of a field - - // ...SOCKET(SET_FIELD, field id, serialized field value) - - // Server updates the value of the field in its fieldstore - Utils.Emit(Server.Socket, MessageStore.SetField, field.ToJson()); - } -} diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index c67cc067a..dbec82340 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,13 +1,5 @@ // import * as ts from "typescript" let ts = (window as any).ts; -import { Opt, Field } from "../../fields/Field"; -import { Document } from "../../fields/Document"; -import { NumberField } from "../../fields/NumberField"; -import { ImageField } from "../../fields/ImageField"; -import { TextField } from "../../fields/TextField"; -import { RichTextField } from "../../fields/RichTextField"; -import { KeyStore } from "../../fields/KeyStore"; -import { ListField } from "../../fields/ListField"; // // @ts-ignore // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // // @ts-ignore @@ -15,8 +7,10 @@ import { ListField } from "../../fields/ListField"; // @ts-ignore import * as typescriptlib from '!!raw-loader!./type_decls.d'; -import { Documents } from "../documents/Documents"; -import { Key } from "../../fields/Key"; +import { Docs } from "../documents/Documents"; +import { Doc, Field } from '../../new_fields/Doc'; +import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; +import { List } from '../../new_fields/List'; export interface ScriptSucccess { success: true; @@ -50,9 +44,9 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an return { compiled: false, errors: diagnostics }; } - let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key]; - let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)]; - let params: any[] = [KeyStore, Documents, ...fieldTypes]; + let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField]; + let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)]; + let params: any[] = [Docs, ...fieldTypes]; let compiledFunction = new Function(...paramNames, `return ${script}`); let { capturedVariables = {} } = options; let run = (args: { [name: string]: any } = {}): ScriptResult => { @@ -171,17 +165,4 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics); return Run(outputText, paramNames, diagnostics, script, options); -} - -export function OrLiteralType(returnType: string): string { - return `${returnType} | string | number`; -} - -export function ToField(data: any): Opt { - if (typeof data === "string") { - return new TextField(data); - } else if (typeof data === "number") { - return new NumberField(data); - } - return undefined; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 4755b2d57..058893198 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -1,13 +1,12 @@ import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Document } from '../../../fields/Document'; -import { Field, FieldValue, FieldWaiting } from '../../../fields/Field'; -import { KeyStore } from '../../../fields/KeyStore'; -import { ListField } from '../../../fields/ListField'; -import { NumberField } from '../../../fields/NumberField'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; +import { Cast, FieldValue, PromiseValue } from '../../../new_fields/Types'; +import { Doc, FieldResult, Opt, Id } from '../../../new_fields/Doc'; +import { listSpec } from '../../../new_fields/Schema'; +import { List } from '../../../new_fields/List'; export enum CollectionViewType { Invalid, @@ -18,9 +17,9 @@ export enum CollectionViewType { } export interface CollectionRenderProps { - addDocument: (document: Document, allowDuplicates?: boolean) => boolean; - removeDocument: (document: Document) => boolean; - moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; + addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + removeDocument: (document: Doc) => boolean; + moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; active: () => boolean; whenActiveChanged: (isActive: boolean) => void; } @@ -37,11 +36,9 @@ export interface CollectionViewProps extends FieldViewProps { export class CollectionBaseView extends React.Component { get collectionViewType(): CollectionViewType | undefined { let Document = this.props.Document; - let viewField = Document.GetT(KeyStore.ViewType, NumberField); - if (viewField === FieldWaiting) { - return undefined; - } else if (viewField) { - return viewField.Data; + let viewField = Cast(Document.viewType, "number"); + if (viewField !== undefined) { + return viewField; } else { return CollectionViewType.Invalid; } @@ -60,47 +57,48 @@ export class CollectionBaseView extends React.Component { this.props.whenActiveChanged(isActive); } - createsCycle(documentToAdd: Document, containerDocument: Document): boolean { - if (!(documentToAdd instanceof Document)) { + createsCycle(documentToAdd: Doc, containerDocument: Doc): boolean { + if (!(documentToAdd instanceof Doc)) { return false; } - let data = documentToAdd.GetList(KeyStore.Data, [] as Document[]); + let data = Cast(documentToAdd.data, listSpec(Doc), []); for (const doc of data.filter(d => d instanceof Document)) { if (this.createsCycle(doc, containerDocument)) { return true; } } - let annots = documentToAdd.GetList(KeyStore.Annotations, [] as Document[]); + let annots = Cast(documentToAdd.annotations, listSpec(Doc), []); for (const annot of annots) { if (this.createsCycle(annot, containerDocument)) { return true; } } - for (let containerProto: FieldValue = containerDocument; containerProto && containerProto !== FieldWaiting; containerProto = containerProto.GetPrototype()) { - if (containerProto.Id === documentToAdd.Id) { + for (let containerProto: Opt = containerDocument; containerProto !== undefined; containerProto = FieldValue(containerProto.proto)) { + if (containerProto[Id] === documentToAdd[Id]) { return true; } } return false; } - @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's? + @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; } @action.bound - addDocument(doc: Document, allowDuplicates: boolean = false): boolean { + addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { let props = this.props; - var curPage = props.Document.GetNumber(KeyStore.CurPage, -1); - doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage)); + var curPage = Cast(props.Document.curPage, "number", -1); + Doc.SetOnPrototype(doc, "page", curPage); if (true || this.isAnnotationOverlay) { - doc.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1)); + doc.zoom = Cast(this.props.Document.scale, "number", 1); } if (curPage >= 0) { - doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document); + Doc.SetOnPrototype(doc, "annotationOn", props.Document); } - if (props.Document.Get(props.fieldKey) instanceof Field) { + const data = props.Document[props.fieldKey]; + if (data !== undefined) { //TODO This won't create the field if it doesn't already exist - const value = props.Document.GetData(props.fieldKey, ListField, new Array()); - if (!this.createsCycle(doc, props.Document)) { - if (!value.some(v => v.Id === doc.Id) || allowDuplicates) { + const value = Cast(data, listSpec(Doc)); + if (!this.createsCycle(doc, props.Document) && value !== undefined) { + if (allowDuplicates || !value.some(v => v.Id === doc.Id)) { value.push(doc); } } @@ -108,9 +106,9 @@ export class CollectionBaseView extends React.Component { return false; } } else { - let proto = props.Document.GetPrototype(); - if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) { - const field = new ListField([doc]); + let proto = FieldValue(props.Document.proto); + if (!proto || !this.createsCycle(proto, doc)) { + const field = new List([doc]); // const script = CompileScript(` // if(added) { // console.log("added " + field.Title + " " + doc.Title); @@ -130,7 +128,7 @@ export class CollectionBaseView extends React.Component { // if (script.compiled) { // field.addScript(new ScriptField(script)); // } - props.Document.SetOnPrototype(props.fieldKey, field); + Doc.SetOnPrototype(props.Document, props.fieldKey, field); } else { return false; @@ -140,20 +138,20 @@ export class CollectionBaseView extends React.Component { } @action.bound - removeDocument(doc: Document): boolean { + removeDocument(doc: Doc): boolean { const props = this.props; //TODO This won't create the field if it doesn't already exist - const value = props.Document.GetData(props.fieldKey, ListField, new Array()); + const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []); let index = -1; for (let i = 0; i < value.length; i++) { - if (value[i].Id === doc.Id) { + if (value[i][Id] === doc[Id]) { index = i; break; } } - doc.GetTAsync(KeyStore.AnnotationOn, Document).then((annotationOn) => { + PromiseValue(Cast(doc.annotationOn, Doc)).then((annotationOn) => { if (annotationOn === props.Document) { - doc.Set(KeyStore.AnnotationOn, undefined, true); + doc.annotationOn = undefined; } }); @@ -168,7 +166,7 @@ export class CollectionBaseView extends React.Component { } @action.bound - moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => boolean): boolean { + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { if (this.props.Document === targetCollection) { return true; } diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index ac09da305..3ecc8555d 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -1,15 +1,13 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { Document } from "../../../fields/Document"; -import { FieldWaiting } from "../../../fields/Field"; -import { Key } from "../../../fields/Key"; -import { KeyStore } from '../../../fields/KeyStore'; -import { ListField } from "../../../fields/ListField"; import { DocumentView } from "./DocumentView"; import { LinkBox } from "./LinkBox"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); +import { Doc, Id } from "../../../new_fields/Doc"; +import { Cast, FieldValue } from "../../../new_fields/Types"; +import { listSpec } from "../../../new_fields/Schema"; interface Props { docView: DocumentView; @@ -19,28 +17,28 @@ interface Props { @observer export class LinkMenu extends React.Component { - @observable private _editingLink?: Document; + @observable private _editingLink?: Doc; - renderLinkItems(links: Document[], key: Key, type: string) { + renderLinkItems(links: Doc[], key: string, type: string) { return links.map(link => { - let doc = link.GetT(key, Document); - if (doc && doc !== FieldWaiting) { - return this._editingLink = link)} type={type} />; + let doc = FieldValue(Cast(link[key], Doc)); + if (doc) { + return this._editingLink = link)} type={type} />; } }); } render() { //get list of links from document - let linkFrom: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedFromDocs, ListField, []); - let linkTo: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []); + let linkFrom: Doc[] = Cast(this.props.docView.props.Document.linkedFromDocs, listSpec(Doc), []); + let linkTo: Doc[] = Cast(this.props.docView.props.Document.linkedToDocs, listSpec(Doc), []); if (this._editingLink === undefined) { return (
- {this.renderLinkItems(linkTo, KeyStore.LinkedToDocs, "Destination: ")} - {this.renderLinkItems(linkFrom, KeyStore.LinkedFromDocs, "Source: ")} + {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} + {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")}
); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 5d4479c88..30a8980ae 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -1,19 +1,17 @@ import { computed, observable, action, runInAction } from "mobx"; import * as rp from 'request-promise'; -import { Documents } from "../../../client/documents/Documents"; +import { Docs } from "../../../client/documents/Documents"; import { Attribute, AttributeGroup, Catalog, Schema } from "../../../client/northstar/model/idea/idea"; import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil"; -import { Server } from "../../../client/Server"; -import { Document } from "../../../fields/Document"; -import { KeyStore } from "../../../fields/KeyStore"; -import { ListField } from "../../../fields/ListField"; import { RouteStore } from "../../RouteStore"; -import { ServerUtils } from "../../ServerUtil"; +import { DocServer } from "../../../client/DocServer"; +import { Doc } from "../../../new_fields/Doc"; +import { List } from "../../../new_fields/List"; export class CurrentUserUtils { private static curr_email: string; private static curr_id: string; - @observable private static user_document: Document; + @observable private static user_document: Doc; //TODO tfs: these should be temporary... private static mainDocId: string | undefined; @@ -23,15 +21,15 @@ export class CurrentUserUtils { public static get MainDocId() { return this.mainDocId; } public static set MainDocId(id: string | undefined) { this.mainDocId = id; } - private static createUserDocument(id: string): Document { - let doc = new Document(id); - doc.Set(KeyStore.Workspaces, new ListField()); - doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" })); + private static createUserDocument(id: string): Doc { + let doc = new Doc(id, true); + doc.workspaces = new List(); + doc.optionalRightCollection = Docs.SchemaDocument([], { title: "Pending documents" }); return doc; } public static loadCurrentUser(): Promise { - let userPromise = rp.get(ServerUtils.prepend(RouteStore.getCurrUser)).then(response => { + let userPromise = rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => { if (response) { let obj = JSON.parse(response); CurrentUserUtils.curr_id = obj.id as string; @@ -40,10 +38,10 @@ export class CurrentUserUtils { throw new Error("There should be a user! Why does Dash think there isn't one?"); } }); - let userDocPromise = rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(id => { + let userDocPromise = rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { if (id) { - return Server.GetField(id).then(field => - runInAction(() => this.user_document = field instanceof Document ? field : this.createUserDocument(id))); + return DocServer.GetRefField(id).then(field => + runInAction(() => this.user_document = field instanceof Doc ? field : this.createUserDocument(id))); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); } -- cgit v1.2.3-70-g09d2 From 513e9042ea815e964462e824d85fbd229381250f Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 27 Apr 2019 01:19:52 -0400 Subject: A bunch more stuff --- src/client/DocServer.ts | 6 +- .../northstar/core/brusher/IBaseBrushable.ts | 4 +- src/client/northstar/core/filter/FilterModel.ts | 17 ++- .../northstar/core/filter/IBaseFilterConsumer.ts | 4 +- src/client/northstar/dash-fields/HistogramField.ts | 3 +- src/client/util/TooltipTextMenu.tsx | 4 +- src/client/views/InkingStroke.tsx | 2 +- src/client/views/Main.tsx | 152 ++++++++++----------- .../views/collections/CollectionDockingView.tsx | 8 +- src/client/views/collections/CollectionPDFView.tsx | 10 +- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionVideoView.tsx | 9 +- .../CollectionFreeFormLinkView.tsx | 20 +-- .../CollectionFreeFormLinksView.tsx | 63 +++++---- .../CollectionFreeFormRemoteCursors.tsx | 1 - src/client/views/nodes/IconBox.tsx | 12 +- 16 files changed, 165 insertions(+), 154 deletions(-) (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 02fd28a86..3f17baec6 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,5 +1,5 @@ import * as OpenSocket from 'socket.io-client'; -import { MessageStore, Types } from "./../server/Message"; +import { MessageStore, Types, Message } from "./../server/Message"; import { Opt, FieldWaiting, RefField, HandleUpdate } from '../new_fields/Doc'; import { Utils } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; @@ -13,6 +13,10 @@ export namespace DocServer { return window.location.origin + extension; } + export function DeleteDatabase() { + Utils.Emit(_socket, MessageStore.DeleteAll, {}); + } + export async function GetRefField(id: string): Promise> { let cached = _cache[id]; if (cached === undefined) { diff --git a/src/client/northstar/core/brusher/IBaseBrushable.ts b/src/client/northstar/core/brusher/IBaseBrushable.ts index c46db4d22..87f4ba413 100644 --- a/src/client/northstar/core/brusher/IBaseBrushable.ts +++ b/src/client/northstar/core/brusher/IBaseBrushable.ts @@ -1,9 +1,9 @@ import { PIXIPoint } from '../../utils/MathUtil'; import { IEquatable } from '../../utils/IEquatable'; -import { Document } from '../../../../fields/Document'; +import { Doc } from '../../../../new_fields/Doc'; export interface IBaseBrushable extends IEquatable { - BrusherModels: Array; + BrusherModels: Array; BrushColors: Array; Position: PIXIPoint; Size: PIXIPoint; diff --git a/src/client/northstar/core/filter/FilterModel.ts b/src/client/northstar/core/filter/FilterModel.ts index e2ba3f652..6ab96b33d 100644 --- a/src/client/northstar/core/filter/FilterModel.ts +++ b/src/client/northstar/core/filter/FilterModel.ts @@ -2,10 +2,9 @@ import { ValueComparison } from "./ValueComparision"; import { Utils } from "../../utils/Utils"; import { IBaseFilterProvider } from "./IBaseFilterProvider"; import { FilterOperand } from "./FilterOperand"; -import { KeyStore } from "../../../../fields/KeyStore"; -import { FieldWaiting } from "../../../../fields/Field"; -import { Document } from "../../../../fields/Document"; import { HistogramField } from "../../dash-fields/HistogramField"; +import { Cast, FieldValue } from "../../../../new_fields/Types"; +import { Doc } from "../../../../new_fields/Doc"; export class FilterModel { public ValueComparisons: ValueComparison[]; @@ -52,12 +51,12 @@ export class FilterModel { let children = new Array(); let linkedGraphNodes = baseOperation.Links; linkedGraphNodes.map(linkVm => { - let filterDoc = linkVm.Get(KeyStore.LinkedFromDocs); - if (filterDoc && filterDoc !== FieldWaiting && filterDoc instanceof Document) { - let filterHistogram = filterDoc.GetT(KeyStore.Data, HistogramField); - if (filterHistogram && filterHistogram !== FieldWaiting) { - if (!visitedFilterProviders.has(filterHistogram.Data)) { - let child = FilterModel.GetFilterModelsRecursive(filterHistogram.Data, visitedFilterProviders, filterModels, false); + let filterDoc = FieldValue(Cast(linkVm.linkedFrom, Doc)); + if (filterDoc) { + let filterHistogram = Cast(filterDoc.data, HistogramField); + if (filterHistogram) { + if (!visitedFilterProviders.has(filterHistogram.HistoOp)) { + let child = FilterModel.GetFilterModelsRecursive(filterHistogram.HistoOp, visitedFilterProviders, filterModels, false); if (child !== "") { // if (linkVm.IsInverted) { // child = "! " + child; diff --git a/src/client/northstar/core/filter/IBaseFilterConsumer.ts b/src/client/northstar/core/filter/IBaseFilterConsumer.ts index 59d7adf4c..e7549d113 100644 --- a/src/client/northstar/core/filter/IBaseFilterConsumer.ts +++ b/src/client/northstar/core/filter/IBaseFilterConsumer.ts @@ -1,10 +1,10 @@ import { FilterOperand } from '../filter/FilterOperand'; import { IEquatable } from '../../utils/IEquatable'; -import { Document } from "../../../../fields/Document"; +import { Doc } from '../../../../new_fields/Doc'; export interface IBaseFilterConsumer extends IEquatable { FilterOperand: FilterOperand; - Links: Document[]; + Links: Doc[]; } export function instanceOfIBaseFilterConsumer(object: any): object is IBaseFilterConsumer { diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index baeaca1dd..118f4cf7f 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -7,8 +7,9 @@ import { ObjectField } from "../../../new_fields/Doc"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { OmitKeys } from "../../../Utils"; import { Deserializable } from "../../util/SerializationHelper"; + function serialize(field: HistogramField) { - return OmitKeys(field.HistoOp, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit + return OmitKeys(field.HistoOp, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit; } function deserialize(jp: any) { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 4f0eb7d63..1b6647003 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -35,8 +35,8 @@ export class TooltipTextMenu { private fontStylesToName: Map; private fontSizeIndicator: HTMLSpanElement = document.createElement("span"); //dropdown doms - private fontSizeDom: Node; - private fontStyleDom: Node; + private fontSizeDom?: Node; + private fontStyleDom?: Node; constructor(view: EditorView, editorProps: FieldViewProps) { this.view = view; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 0f05da22c..616299146 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -1,8 +1,8 @@ import { observer } from "mobx-react"; import { observable } from "mobx"; import { InkingControl } from "./InkingControl"; -import { InkTool } from "../../fields/InkField"; import React = require("react"); +import { InkTool } from "../../new_fields/InkField"; interface StrokeProps { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index c6b3f06d8..1e3d4e259 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -8,17 +8,12 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import Measure from 'react-measure'; import * as request from 'request'; -import { Document } from '../../fields/Document'; -import { Field, FieldWaiting, Opt, FIELD_WAITING } from '../../fields/Field'; -import { KeyStore } from '../../fields/KeyStore'; -import { ListField } from '../../fields/ListField'; import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { MessageStore } from '../../server/Message'; import { RouteStore } from '../../server/RouteStore'; -import { ServerUtils } from '../../server/ServerUtil'; -import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; -import { Documents } from '../documents/Documents'; +import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; +import { Docs } from '../documents/Documents'; import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel'; import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel'; import { Gateway, NorthstarSettings } from '../northstar/manager/Gateway'; @@ -26,7 +21,6 @@ import { AggregateFunction, Catalog } from '../northstar/model/idea/idea'; import '../northstar/model/ModelExtensions'; import { HistogramOperation } from '../northstar/operations/HistogramOperation'; import '../northstar/utils/Extensions'; -import { Server } from '../Server'; import { SetupDrag, DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; @@ -39,6 +33,10 @@ import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; import { PreviewCursor } from './PreviewCursor'; import { SelectionManager } from '../util/SelectionManager'; +import { FieldResult, Field, Doc, Id, Opt } from '../../new_fields/Doc'; +import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; +import { DocServer } from '../DocServer'; +import { listSpec } from '../../new_fields/Schema'; @observer @@ -48,11 +46,13 @@ export class Main extends React.Component { @observable public pwidth: number = 0; @observable public pheight: number = 0; - @computed private get mainContainer(): Document | undefined | FIELD_WAITING { - return CurrentUserUtils.UserDocument.GetT(KeyStore.ActiveWorkspace, Document); + @computed private get mainContainer(): Opt { + return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); } - private set mainContainer(doc: Document | undefined | FIELD_WAITING) { - doc && CurrentUserUtils.UserDocument.Set(KeyStore.ActiveWorkspace, doc); + private set mainContainer(doc: Opt) { + if (doc) { + CurrentUserUtils.UserDocument.activeWorkspace = doc; + } } constructor(props: Readonly<{}>) { @@ -100,8 +100,8 @@ export class Main extends React.Component { if (window.location.pathname !== RouteStore.home) { let pathname = window.location.pathname.split("/"); CurrentUserUtils.MainDocId = pathname[pathname.length - 1]; - Server.GetField(CurrentUserUtils.MainDocId, action((field: Opt) => { - if (field instanceof Document) { + DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt) => { + if (field instanceof Doc) { this.openWorkspace(field, true); } })); @@ -113,9 +113,9 @@ export class Main extends React.Component { window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler window.addEventListener("keydown", (e) => { - if (e.key == "Escape") { + if (e.key === "Escape") { DragManager.AbortDrag(); - SelectionManager.DeselectAll() + SelectionManager.DeselectAll(); } }, false); // drag event handler // click interactions for the context menu @@ -126,54 +126,55 @@ export class Main extends React.Component { }), true); } - initAuthenticationRouters = () => { + initAuthenticationRouters = async () => { // Load the user's active workspace, or create a new one if initial session after signup if (!CurrentUserUtils.MainDocId) { - CurrentUserUtils.UserDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => { - if (doc) { - CurrentUserUtils.MainDocId = doc.Id; - this.openWorkspace(doc); - } else { - this.createNewWorkspace(); - } - }); + const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc); + if (doc) { + CurrentUserUtils.MainDocId = doc[Id]; + this.openWorkspace(doc); + } else { + this.createNewWorkspace(); + } } else { - Server.GetField(CurrentUserUtils.MainDocId).then(field => - field instanceof Document ? this.openWorkspace(field) : + DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field => + field instanceof Doc ? this.openWorkspace(field) : this.createNewWorkspace(CurrentUserUtils.MainDocId)); } } @action - createNewWorkspace = (id?: string): void => { - CurrentUserUtils.UserDocument.GetTAsync>(KeyStore.Workspaces, ListField).then(action((list: Opt>) => { - if (list) { - let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; - let mainDoc = Documents.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.Data.length + 1}` }, id); - list.Data.push(mainDoc); - CurrentUserUtils.MainDocId = mainDoc.Id; - // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => { - this.openWorkspace(mainDoc); - let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" }); - mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument); - }, 0); - } - })); + createNewWorkspace = async (id?: string) => { + const list = Cast(CurrentUserUtils.UserDocument.workspaces, listSpec(Doc)); + if (list) { + let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; + let mainDoc = Docs.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.length + 1}` }, id); + list.push(mainDoc); + CurrentUserUtils.MainDocId = mainDoc[Id]; + // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) + setTimeout(() => { + this.openWorkspace(mainDoc); + let pendingDocument = Docs.SchemaDocument([], { title: "New Mobile Uploads" }); + mainDoc.optionalRightCollection = pendingDocument; + }, 0); + } } @action - openWorkspace = (doc: Document, fromHistory = false): void => { + openWorkspace = async (doc: Doc, fromHistory = false) => { this.mainContainer = doc; - fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id); - CurrentUserUtils.UserDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => - // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) - setTimeout(() => - col && col.GetTAsync>(KeyStore.Data, ListField, (f: Opt>) => - f && f.Data.length > 0 && CollectionDockingView.Instance.AddRightSplit(col)) - , 100) - ); + fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc.Id); + const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc); + // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) + setTimeout(async () => { + if (col) { + const l = Cast(col.data, listSpec(Doc)); + if (l && l.length > 0) { + CollectionDockingView.Instance.AddRightSplit(col); + } + } + }, 100); } @computed @@ -196,7 +197,7 @@ export class Main extends React.Component { PanelHeight={pheightFunc} isTopMost={true} selectOnLoad={false} - focus={emptyDocFunction} + focus={emptyFunction} parentActive={returnTrue} whenActiveChanged={emptyFunction} ContainingCollectionView={undefined} />} @@ -214,17 +215,17 @@ export class Main extends React.Component { let audiourl = "http://techslides.com/demos/samples/sample.mp3"; let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; - let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" })); - let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); - let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" })); - let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true })); - let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, title: "video node" })); - let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); - let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); - let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); - let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); + let addTextNode = action(() => Docs.TextDocument({ width: 200, height: 200, title: "a text note" })); + let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); + let addSchemaNode = action(() => Docs.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" })); + let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true })); + let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); + let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); + let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); + let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); + let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); - let btns: [React.RefObject, IconName, string, () => Document][] = [ + let btns: [React.RefObject, IconName, string, () => Doc][] = [ [React.createRef(), "font", "Add Textbox", addTextNode], [React.createRef(), "image", "Add Image", addImageNode], [React.createRef(), "file-pdf", "Add PDF", addPDFNode], @@ -260,9 +261,8 @@ export class Main extends React.Component { let logoutRef = React.createRef(); let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown); - let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {})); return [ - , + ,
@@ -271,7 +271,7 @@ export class Main extends React.Component {
,
-
+
]; } @@ -279,10 +279,10 @@ export class Main extends React.Component { get workspaceMenu() { let areWorkspacesShown = () => this._workspacesShown; let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown); - let workspaces = CurrentUserUtils.UserDocument.GetT>(KeyStore.Workspaces, ListField); - return (!workspaces || workspaces === FieldWaiting || this.mainContainer === FieldWaiting) ? (null) : + let workspaces = Cast(CurrentUserUtils.UserDocument.workspaces, listSpec(Doc)); + return (!workspaces || !this.mainContainer) ? (null) : ; } @@ -303,17 +303,17 @@ export class Main extends React.Component { } // --------------- Northstar hooks ------------- / - private _northstarSchemas: Document[] = []; + private _northstarSchemas: Doc[] = []; @action SetNorthstarCatalog(ctlog: Catalog) { CurrentUserUtils.NorthstarDBCatalog = ctlog; if (ctlog && ctlog.schemas) { ctlog.schemas.map(schema => { - let schemaDocuments: Document[] = []; + let schemaDocuments: Doc[] = []; let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema); Promise.all(attributesToBecomeDocs.reduce((promises, attr) => { - promises.push(Server.GetField(attr.displayName! + ".alias").then(action((field: Opt) => { - if (field instanceof Document) { + promises.push(DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { + if (field instanceof Doc) { schemaDocuments.push(field); } else { var atmod = new ColumnAttributeModel(attr); @@ -321,12 +321,12 @@ export class Main extends React.Component { new AttributeTransformationModel(atmod, AggregateFunction.None), new AttributeTransformationModel(atmod, AggregateFunction.Count), new AttributeTransformationModel(atmod, AggregateFunction.Count)); - schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias")); + schemaDocuments.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! })); } }))); return promises; }, [] as Promise[])).finally(() => - this._northstarSchemas.push(Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! }))); + this._northstarSchemas.push(Docs.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! }))); }); } } @@ -338,7 +338,7 @@ export class Main extends React.Component { } (async () => { - await Documents.initProtos(); + await Docs.initProtos(); await CurrentUserUtils.loadCurrentUser(); ReactDOM.render(
, document.getElementById('root')); })(); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 69401ceeb..2ff409b9b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -6,13 +6,11 @@ import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; import { Utils, returnTrue, emptyFunction, returnOne, returnZero } from "../../../Utils"; -import { Server } from "../../Server"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; import React = require("react"); import { SubCollectionViewProps } from "./CollectionSubView"; -import { ServerUtils } from "../../../server/ServerUtil"; import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; import { Transform } from '../../util/Transform'; import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc"; @@ -206,7 +204,7 @@ export class CollectionDockingView extends React.Component) => + DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt) => (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc))); } else if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) { @@ -216,7 +214,7 @@ export class CollectionDockingView extends React.Component) => { + DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y, { @@ -301,7 +299,7 @@ export class CollectionDockingView extends React.Component { return FieldView.LayoutString(CollectionPDFView, fieldKey); } - private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); } - private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); } - @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : -1; - @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1; + private get curPage() { return NumCast(this.props.Document.curPage, -1); } + private get numPages() { return NumCast(this.props.Document.numPages); } + @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1; + @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1; private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 2e1175f28..874170f3d 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, untracked } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; -import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss' +import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; import "react-table/react-table.css"; import { emptyFunction, returnFalse, returnZero } from "../../../Utils"; import { SetupDrag } from "../../util/DragManager"; @@ -283,7 +283,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu // is displayed (unlikely) it won't show up until something else changes. //TODO Types - untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false)))); + untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)))); this.columns.forEach(key => keys[key] = true); return Array.from(Object.keys(keys)).map(item => diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 779dc8fc3..d314e3fc0 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -1,6 +1,5 @@ import { action, observable, trace } from "mobx"; import { observer } from "mobx-react"; -import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView"; import React = require("react"); @@ -8,6 +7,7 @@ import "./CollectionVideoView.scss"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import { emptyFunction } from "../../../Utils"; +import { NumCast } from "../../../new_fields/Types"; @observer @@ -44,8 +44,9 @@ export class CollectionVideoView extends React.Component { if (ele) { this._player = ele.getElementsByTagName("video")[0]; console.log(this._player); - if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) { - this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1); + const curPage = NumCast(this.props.Document.curPage, -1); + if (curPage >= 0) { + this._currentTimecode = curPage; } } } @@ -69,7 +70,7 @@ export class CollectionVideoView extends React.Component { (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1; } else { this._currentTimecode = this._player.currentTime; - this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this._currentTimecode)); + this.props.Document.curPage = Math.round(this._currentTimecode); } } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 20c5a84bf..d4987fc18 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -1,15 +1,15 @@ import { observer } from "mobx-react"; -import { Document } from "../../../../fields/Document"; -import { KeyStore } from "../../../../fields/KeyStore"; import { Utils } from "../../../../Utils"; import "./CollectionFreeFormLinkView.scss"; import React = require("react"); import v5 = require("uuid/v5"); +import { StrCast, NumCast, BoolCast } from "../../../../new_fields/Types"; +import { Doc } from "../../../../new_fields/Doc"; export interface CollectionFreeFormLinkViewProps { - A: Document; - B: Document; - LinkDocs: Document[]; + A: Doc; + B: Doc; + LinkDocs: Doc[]; } @observer @@ -17,16 +17,16 @@ export class CollectionFreeFormLinkView extends React.Component { this.props.LinkDocs.map(l => - console.log("Link:" + l.Title)); + console.log("Link:" + StrCast(l.title))); } render() { let l = this.props.LinkDocs; let a = this.props.A; let b = this.props.B; - let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Width() / 2); - let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Height() / 2); - let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Width() / 2); - let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Height() / 2); + let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2); + let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2); + let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2); + let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2); return ( { _brushReactionDisposer?: IReactionDisposer; componentDidMount() { - this._brushReactionDisposer = reaction(() => this.props.Document.GetList(this.props.fieldKey, [] as Document[]).map(doc => doc.GetNumber(KeyStore.X, 0)), + this._brushReactionDisposer = reaction(() => Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).map(doc => NumCast(doc.x)), () => { - let views = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc.GetText(KeyStore.BackgroundLayout, "").indexOf("istogram") !== -1); + let views = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => StrCast(doc.backgroundLayout, "").indexOf("istogram") !== -1); for (let i = 0; i < views.length; i++) { for (let j = 0; j < views.length; j++) { let srcDoc = views[j]; let dstDoc = views[i]; - let x1 = srcDoc.GetNumber(KeyStore.X, 0); - let x1w = srcDoc.GetNumber(KeyStore.Width, -1); - let x2 = dstDoc.GetNumber(KeyStore.X, 0); - let x2w = dstDoc.GetNumber(KeyStore.Width, -1); + let x1 = NumCast(srcDoc.x); + let x1w = NumCast(srcDoc.width, -1); + let x2 = NumCast(dstDoc.x); + let x2w = NumCast(dstDoc.width, -1); if (x1w < 0 || x2w < 0 || i === j) { continue; } let dstTarg = dstDoc; let srcTarg = srcDoc; - let findBrush = (field: ListField) => field.Data.findIndex(brush => { - let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : []; + let findBrush = (field: List) => field.findIndex(brush => { + let bdocs = brush ? Cast(brush.brushingDocs, listSpec(Doc), []) : []; return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false); }); - let brushAction = (field: ListField) => { + let brushAction = (field: List) => { let found = findBrush(field); if (found !== -1) { console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title); - field.Data.splice(found, 1); + field.splice(found, 1); } }; if (Math.abs(x1 + x1w - x2) < 20) { - let linkDoc: Document = new Document(); - linkDoc.SetText(KeyStore.Title, "Histogram Brush"); - linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title); - linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField); + let linkDoc: Doc = new Doc(); + linkDoc.title = "Histogram Brush"; + linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title); + linkDoc.brushingDocs = new List([dstTarg, srcTarg]); - brushAction = (field: ListField) => { + brushAction = (field: List) => { if (findBrush(field) === -1) { console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title); - (findBrush(field) === -1) && field.Data.push(linkDoc); + (findBrush(field) === -1) && field.push(linkDoc); } }; } - dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction); - srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction); + let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc)); + if (dstBrushDocs === undefined) { + dstTarg.brushingDocs = dstBrushDocs = new List(); + } + let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc)); + if (srcBrushDocs === undefined) { + srcTarg.brushingDocs = srcBrushDocs = new List(); + } + brushAction(dstBrushDocs); + brushAction(srcBrushDocs); } } @@ -70,9 +79,9 @@ export class CollectionFreeFormLinksView extends React.Component sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document); } @@ -82,12 +91,12 @@ export class CollectionFreeFormLinksView extends React.Component { let srcViews = this.documentAnchors(connection.a); let targetViews = this.documentAnchors(connection.b); - let possiblePairs: { a: Document, b: Document, }[] = []; + let possiblePairs: { a: Doc, b: Doc, }[] = []; srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }))); possiblePairs.map(possiblePair => drawnPairs.reduce((found, drawnPair) => { let match = (possiblePair.a === drawnPair.a && possiblePair.b === drawnPair.b); - if (match && !drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false)) { + if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) { drawnPair.l.push(connection.l); } return match || found; @@ -96,7 +105,7 @@ export class CollectionFreeFormLinksView extends React.Component ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index cf0a6de00..036745eca 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -1,6 +1,5 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; -import { KeyStore } from "../../../../fields/KeyStore"; import { CollectionViewProps, CursorEntry } from "../CollectionSubView"; import "./CollectionFreeFormView.scss"; import React = require("react"); diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 9c90c0a0e..f7cceb3d4 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -4,12 +4,12 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } fr import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { Document } from '../../../fields/Document'; -import { IconField } from "../../../fields/IconFIeld"; -import { KeyStore } from "../../../fields/KeyStore"; import { SelectionManager } from "../../util/SelectionManager"; import { FieldView, FieldViewProps } from './FieldView'; import "./IconBox.scss"; +import { Cast } from "../../../new_fields/Types"; +import { Doc } from "../../../new_fields/Doc"; +import { IconField } from "../../../new_fields/IconField"; library.add(faCaretUp); @@ -22,8 +22,8 @@ library.add(faFilm); export class IconBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(IconBox); } - @computed get maximized() { return this.props.Document.GetT(KeyStore.MaximizedDoc, Document); } - @computed get layout(): string { return this.props.Document.GetData(this.props.fieldKey, IconField, "

Error loading layout data

" as string); } + @computed get maximized() { return Cast(this.props.Document.maximizedDoc, Doc); } + @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.layout : "

Error loading layout data

"; } @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); } public static DocumentIcon(layout: string) { @@ -33,7 +33,7 @@ export class IconBox extends React.Component { layout.indexOf("Video") !== -1 ? faFilm : layout.indexOf("Collection") !== -1 ? faObjectGroup : faCaretUp; - return + return ; } render() { -- cgit v1.2.3-70-g09d2 From d4a77dd055685dd81a762ef40e0c3b7606586e9c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 27 Apr 2019 21:40:17 -0400 Subject: Split more files up --- src/client/DocServer.ts | 3 +- src/client/northstar/dash-fields/HistogramField.ts | 2 +- src/client/northstar/dash-nodes/HistogramBox.tsx | 3 +- src/client/util/Scripting.ts | 1 + src/client/views/Main.tsx | 3 +- .../views/collections/CollectionBaseView.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 3 +- .../views/collections/CollectionSchemaView.tsx | 3 +- src/client/views/collections/CollectionSubView.tsx | 3 +- .../views/collections/CollectionTreeView.tsx | 3 +- .../CollectionFreeFormLinksView.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 3 +- src/client/views/nodes/FieldView.tsx | 1 + src/client/views/nodes/LinkMenu.tsx | 3 +- src/new_fields/Doc.ts | 37 +++------------------- src/new_fields/HtmlField.ts | 2 +- src/new_fields/IconField.ts | 2 +- src/new_fields/InkField.ts | 2 +- src/new_fields/List.ts | 3 +- src/new_fields/ObjectField.ts | 17 ++++++++++ src/new_fields/Proxy.ts | 4 ++- src/new_fields/RefField.ts | 18 +++++++++++ src/new_fields/RichTextField.ts | 2 +- src/new_fields/Schema.ts | 8 ++--- src/new_fields/Types.ts | 2 +- src/new_fields/URLField.ts | 2 +- src/new_fields/util.ts | 4 ++- .../authentication/controllers/WorkspacesMenu.tsx | 3 +- src/server/database.ts | 2 ++ 30 files changed, 88 insertions(+), 60 deletions(-) create mode 100644 src/new_fields/ObjectField.ts create mode 100644 src/new_fields/RefField.ts (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 3f17baec6..c7cbfce37 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,8 +1,9 @@ import * as OpenSocket from 'socket.io-client'; import { MessageStore, Types, Message } from "./../server/Message"; -import { Opt, FieldWaiting, RefField, HandleUpdate } from '../new_fields/Doc'; +import { Opt, FieldWaiting } from '../new_fields/Doc'; import { Utils } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; +import { RefField, HandleUpdate } from '../new_fields/RefField'; export namespace DocServer { const _cache: { [id: string]: RefField | Promise> } = {}; diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index 118f4cf7f..730289536 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -3,7 +3,7 @@ import { custom, serializable } from "serializr"; import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel"; import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel"; import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation"; -import { ObjectField } from "../../../new_fields/Doc"; +import { ObjectField } from "../../../new_fields/ObjectField"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { OmitKeys } from "../../../Utils"; import { Deserializable } from "../../util/SerializationHelper"; diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index 4a65b14cf..a9c68ccba 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -20,7 +20,8 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives"; import { StyleConstants } from "../utils/StyleContants"; import { NumCast, Cast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; -import { Doc, Id } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/RefField"; @observer diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index dbec82340..e45f61c11 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -11,6 +11,7 @@ import { Docs } from "../documents/Documents"; import { Doc, Field } from '../../new_fields/Doc'; import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; import { List } from '../../new_fields/List'; +import { RichTextField } from '../../new_fields/RichTextField'; export interface ScriptSucccess { success: true; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 1e3d4e259..4a68d1c68 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -33,10 +33,11 @@ import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; import { PreviewCursor } from './PreviewCursor'; import { SelectionManager } from '../util/SelectionManager'; -import { FieldResult, Field, Doc, Id, Opt } from '../../new_fields/Doc'; +import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc'; import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; import { DocServer } from '../DocServer'; import { listSpec } from '../../new_fields/Schema'; +import { Id } from '../../new_fields/RefField'; @observer diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 4807dc40a..b2fba1415 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -4,9 +4,10 @@ import * as React from 'react'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types'; -import { Doc, FieldResult, Opt, Id } from '../../../new_fields/Doc'; +import { Doc, FieldResult, Opt } from '../../../new_fields/Doc'; import { listSpec } from '../../../new_fields/Schema'; import { List } from '../../../new_fields/List'; +import { Id } from '../../../new_fields/RefField'; export enum CollectionViewType { Invalid, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 2ff409b9b..1574562c6 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -13,11 +13,12 @@ import React = require("react"); import { SubCollectionViewProps } from "./CollectionSubView"; import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; import { Transform } from '../../util/Transform'; -import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc"; +import { Doc, Opt, Field } from "../../../new_fields/Doc"; import { Cast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; import { listSpec } from "../../../new_fields/Schema"; +import { Id, FieldId } from "../../../new_fields/RefField"; @observer export class CollectionDockingView extends React.Component { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 874170f3d..58d20819b 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -19,10 +19,11 @@ import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { Opt, Field, Doc, Id } from "../../../new_fields/Doc"; +import { Opt, Field, Doc } from "../../../new_fields/Doc"; import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; +import { Id } from "../../../new_fields/RefField"; // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 558a8728f..4d090b680 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -10,12 +10,13 @@ import * as rp from 'request-promise'; import { CollectionView } from "./CollectionView"; import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; -import { Doc, ObjectField, Opt } from "../../../new_fields/Doc"; +import { Doc, Opt } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, PromiseValue } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; +import { ObjectField } from "../../../new_fields/ObjectField"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c9d8d83c8..7ec9a8549 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -10,7 +10,8 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { Document, listSpec } from '../../../new_fields/Schema'; import { Cast, StrCast, BoolCast } from '../../../new_fields/Types'; -import { Doc, Id } from '../../../new_fields/Doc'; +import { Doc } from '../../../new_fields/Doc'; +import { Id } from '../../../new_fields/RefField'; export interface TreeViewProps { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index ce9995630..f693d55e8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -7,10 +7,11 @@ import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); -import { Doc, Id } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../new_fields/Doc"; import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types"; import { listSpec } from "../../../../new_fields/Schema"; import { List } from "../../../../new_fields/List"; +import { Id } from "../../../../new_fields/RefField"; @observer export class CollectionFreeFormLinksView extends React.Component { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 047fbad18..18107e98a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -19,10 +19,11 @@ import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema"; -import { Doc, Id } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../new_fields/Doc"; import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types"; import { pageSchema } from "../../nodes/ImageBox"; import { List } from "../../../../new_fields/List"; +import { Id } from "../../../../new_fields/RefField"; export const panZoomSchema = createSchema({ panX: "number", diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index aabc1633e..c304b6a35 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -15,7 +15,7 @@ import { ContextMenu } from "../ContextMenu"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); -import { Field, Opt, Doc, Id } from "../../../new_fields/Doc"; +import { Opt, Doc } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; import { FieldValue, Cast, PromiseValue } from "../../../new_fields/Types"; @@ -24,6 +24,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView"; import { DocServer } from "../../DocServer"; +import { Id } from "../../../new_fields/RefField"; const linkSchema = createSchema({ title: "string", diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c00c47fc4..dc36c5914 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -16,6 +16,7 @@ import { Opt, Doc, FieldResult } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField"; import { IconField } from "../../../new_fields/IconField"; +import { RichTextField } from "../../../new_fields/RichTextField"; // diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 3ecc8555d..e21adebbc 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -5,9 +5,10 @@ import { LinkBox } from "./LinkBox"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); -import { Doc, Id } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; import { Cast, FieldValue } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; +import { Id } from "../../../new_fields/RefField"; interface Props { docView: DocumentView; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index fb7b6e360..4ef2a465f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -8,38 +8,8 @@ import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types"; import { UndoManager, undoBatch } from "../client/util/UndoManager"; import { listSpec } from "./Schema"; import { List } from "./List"; - -export type FieldId = string; -export const HandleUpdate = Symbol("HandleUpdate"); -export const Id = Symbol("Id"); -export abstract class RefField { - @serializable(alias("id", primitive())) - private __id: FieldId; - readonly [Id]: FieldId; - - constructor(id?: FieldId) { - this.__id = id || Utils.GenerateGuid(); - this[Id] = this.__id; - } - - protected [HandleUpdate]?(diff: any): void; -} - -export const Update = Symbol("Update"); -export const OnUpdate = Symbol("OnUpdate"); -export const Parent = Symbol("Parent"); -export class ObjectField { - protected [OnUpdate]?: (diff?: any) => void; - private [Parent]?: Doc; - readonly [Id] = ""; -} - -export namespace ObjectField { - export function MakeCopy(field: ObjectField) { - //TODO Types - return field; - } -} +import { ObjectField } from "./ObjectField"; +import { RefField, FieldId, Id } from "./RefField"; export function IsField(field: any): field is Field { return (typeof field === "string") @@ -53,6 +23,7 @@ export type Opt = T | undefined; export type FieldWaiting = T extends undefined ? never : Promise; export type FieldResult = Opt | FieldWaiting>; +export const Update = Symbol("Update"); export const Self = Symbol("Self"); @Deserializable("doc").withFields(["id"]) @@ -161,7 +132,7 @@ export namespace Doc { copy[key] = field; } } - }) + }); return copy; } diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts index 76fdb1f62..808a3499b 100644 --- a/src/new_fields/HtmlField.ts +++ b/src/new_fields/HtmlField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; @Deserializable("html") export class HtmlField extends ObjectField { diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts index 32f3aa4d5..46f111f8e 100644 --- a/src/new_fields/IconField.ts +++ b/src/new_fields/IconField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; @Deserializable("icon") export class IconField extends ObjectField { diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 49e6bf61e..42223c494 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; export enum InkTool { None, diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index f01ac210a..428f661c9 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -1,8 +1,9 @@ import { Deserializable, autoObject } from "../client/util/SerializationHelper"; -import { Field, ObjectField, Update, OnUpdate, Self } from "./Doc"; +import { Field, Update, Self } from "./Doc"; import { setter, getter } from "./util"; import { serializable, alias, list } from "serializr"; import { observable } from "mobx"; +import { ObjectField, OnUpdate } from "./ObjectField"; @Deserializable("list") class ListImpl extends ObjectField { diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts new file mode 100644 index 000000000..9cac2c528 --- /dev/null +++ b/src/new_fields/ObjectField.ts @@ -0,0 +1,17 @@ +import { Doc } from "./Doc"; + +export const OnUpdate = Symbol("OnUpdate"); +export const Parent = Symbol("Parent"); +const Id = Symbol("Object Id"); +export class ObjectField { + protected [OnUpdate]?: (diff?: any) => void; + private [Parent]?: Doc; + readonly [Id] = ""; +} + +export namespace ObjectField { + export function MakeCopy(field: ObjectField) { + //TODO Types + return field; + } +} diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index 2aa78731e..56e41cc0f 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -1,8 +1,10 @@ import { Deserializable } from "../client/util/SerializationHelper"; -import { RefField, Id, ObjectField, FieldWaiting } from "./Doc"; +import { FieldWaiting } from "./Doc"; import { primitive, serializable } from "serializr"; import { observable, action } from "mobx"; import { DocServer } from "../client/DocServer"; +import { RefField, Id } from "./RefField"; +import { ObjectField } from "./ObjectField"; @Deserializable("proxy") export class ProxyField extends ObjectField { diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts new file mode 100644 index 000000000..202c65f21 --- /dev/null +++ b/src/new_fields/RefField.ts @@ -0,0 +1,18 @@ +import { serializable, primitive, alias } from "serializr"; +import { Utils } from "../Utils"; + +export type FieldId = string; +export const HandleUpdate = Symbol("HandleUpdate"); +export const Id = Symbol("Id"); +export abstract class RefField { + @serializable(alias("id", primitive())) + private __id: FieldId; + readonly [Id]: FieldId; + + constructor(id?: FieldId) { + this.__id = id || Utils.GenerateGuid(); + this[Id] = this.__id; + } + + protected [HandleUpdate]?(diff: any): void; +} diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index 156e4efd9..0fa3cf73c 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -1,4 +1,4 @@ -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; import { serializable } from "serializr"; export class RichTextField extends ObjectField { diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts index 5081521c7..7444878fe 100644 --- a/src/new_fields/Schema.ts +++ b/src/new_fields/Schema.ts @@ -21,15 +21,15 @@ export function makeInterface(...schemas: T): (doc?: Doc) } } const proto = new Proxy({}, { - get(target: any, prop) { - const field = target.doc[prop]; + get(target: any, prop, receiver) { + const field = receiver.doc[prop]; if (prop in schema) { return Cast(field, (schema as any)[prop]); } return field; }, - set(target: any, prop, value) { - target.doc[prop] = value; + set(target: any, prop, value, receiver) { + receiver.doc[prop] = value; return true; } }); diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts index 7fa18673f..3f8eabd45 100644 --- a/src/new_fields/Types.ts +++ b/src/new_fields/Types.ts @@ -1,4 +1,4 @@ -import { Field, Opt, FieldWaiting, FieldResult, RefField } from "./Doc"; +import { Field, Opt, FieldResult } from "./Doc"; import { List } from "./List"; export type ToType | ListSpec> = diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts index 1da245e73..95c679df7 100644 --- a/src/new_fields/URLField.ts +++ b/src/new_fields/URLField.ts @@ -1,6 +1,6 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom } from "serializr"; -import { ObjectField } from "./Doc"; +import { ObjectField } from "./ObjectField"; function url() { return custom( diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 2d9721b2e..011e8c8d9 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -1,8 +1,10 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Update, OnUpdate, Parent, ObjectField, RefField, Doc, Id, Field } from "./Doc"; +import { Update, Doc, Field } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField } from "./Proxy"; import { FieldValue } from "./Types"; +import { RefField, Id } from "./RefField"; +import { ObjectField, Parent, OnUpdate } from "./ObjectField"; export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index 29327e5ad..91756315d 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -3,8 +3,9 @@ import { observable, action, configure, reaction, computed, ObservableMap, runIn import { observer } from "mobx-react"; import './WorkspacesMenu.css'; import { EditableView } from '../../../client/views/EditableView'; -import { Doc, Id } from '../../../new_fields/Doc'; +import { Doc } from '../../../new_fields/Doc'; import { StrCast } from '../../../new_fields/Types'; +import { Id } from '../../../new_fields/RefField'; export interface WorkspaceMenuProps { active: Doc | undefined; diff --git a/src/server/database.ts b/src/server/database.ts index a61b4d823..6b3b6797f 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -60,11 +60,13 @@ export class Database { } public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = Database.DocumentsCollection) { + console.log("getDocument"); this.db && this.db.collection(collectionName).findOne({ id: id }, (err, result) => fn(result ? ({ id: result._id, type: result.type, data: result.data }) : undefined)); } public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) { + console.log("getDocuments"); this.db && this.db.collection(collectionName).find({ id: { "$in": ids } }).toArray((err, docs) => { if (err) { console.log(err.message); -- cgit v1.2.3-70-g09d2 From 506af03831bf3cc002f93ad8708eafb909c0a194 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 27 Apr 2019 22:27:26 -0400 Subject: Various fixes --- src/client/DocServer.ts | 24 ++++++++++++++++++---- src/client/documents/Documents.ts | 2 +- src/client/northstar/dash-nodes/HistogramBox.tsx | 2 +- src/client/views/collections/CollectionPDFView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 1 + .../views/collections/CollectionVideoView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/FieldView.tsx | 4 ++-- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/new_fields/util.ts | 5 +++-- src/server/database.ts | 21 +++++++++++++------ src/server/index.ts | 10 +++++---- 14 files changed, 55 insertions(+), 26 deletions(-) (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index c7cbfce37..07997f072 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -55,12 +55,28 @@ export namespace DocServer { map[id] = cached; } } - const prom = Utils.EmitCallback(_socket, MessageStore.GetFields, requestedIds); - requestedIds.map((id, index) => _cache[id] = prom.then((fields: RefField[]) => fields[index])); + const prom = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds).then(fields => { + for (const key in fields) { + const field = fields[key]; + if (field) { + fields[key] = SerializationHelper.Deserialize(field); + } + } + return fields; + }); + requestedIds.forEach((id, index) => _cache[id] = prom.then((fields: RefField[]) => fields[index])); const fields = await prom; - requestedIds.map((id, index) => map[id] = fields[index]); + requestedIds.forEach((id, index) => { + const field = fields[index]; + if (field) { + _cache[id] = field; + } else { + delete _cache[id]; + } + map[id] = field; + }); const otherFields = await Promise.all(promises); - waitingIds.map((id, index) => map[id] = otherFields[index]); + waitingIds.forEach((id, index) => map[id] = otherFields[index]); return map; } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c30fb21d5..2a9687bda 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -138,7 +138,7 @@ export namespace Docs { return webProto; } function CreateCollectionPrototype(): Doc { - let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("DataKey"), + let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("data"), { panX: 0, panY: 0, scale: 1, width: 500, height: 500 }); return collProto; } diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index a9c68ccba..19d108676 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -26,7 +26,7 @@ import { Id } from "../../../new_fields/RefField"; @observer export class HistogramBox extends React.Component { - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr); } + public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(HistogramBox, fieldStr); } private _dropXRef = React.createRef(); private _dropYRef = React.createRef(); private _dropXDisposer?: DragManager.DragDropDisposer; diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index e73b7b4a6..99438b4e8 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -13,7 +13,7 @@ import { NumCast } from "../../../new_fields/Types"; @observer export class CollectionPDFView extends React.Component { - public static LayoutString(fieldKey: string = "DataKey") { + public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(CollectionPDFView, fieldKey); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 4d090b680..2c2d74302 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -53,6 +53,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @action protected async setCursorPosition(position: [number, number]) { + return; let ind; let doc = this.props.Document; let id = CurrentUserUtils.id; diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index d314e3fc0..d45be228a 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -18,7 +18,7 @@ export class CollectionVideoView extends React.Component { @observable _currentTimecode: number = 0; @observable _isPlaying: boolean = false; - public static LayoutString(fieldKey: string = "DataKey") { + public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(CollectionVideoView, fieldKey); } private get uIButtons() { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index e7bf1e121..b72065bca 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -13,7 +13,7 @@ import { trace } from 'mobx'; @observer export class CollectionView extends React.Component { - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr); } + public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(CollectionView, fieldStr); } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 18107e98a..dfacca204 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -314,7 +314,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { {this.childViews} - + {/* */} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index dc36c5914..df76f7cea 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -45,8 +45,8 @@ export interface FieldViewProps { @observer export class FieldView extends React.Component { - public static LayoutString(fieldType: { name: string }, fieldStr: string = "DataKey") { - return `<${fieldType.name} {...props} fieldKey={${fieldStr}} />`; + public static LayoutString(fieldType: { name: string }, fieldStr: string = "data") { + return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} />`; } @computed diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 7a85c9dd3..96512718f 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -55,7 +55,7 @@ const RichTextDocument = makeInterface(richTextSchema); @observer export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxOverlay), RichTextDocument>(RichTextDocument) { - public static LayoutString(fieldStr: string = "DataKey") { + public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(FormattedTextBox, fieldStr); } private _ref: React.RefObject; diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index ae39ebe2d..876a3c173 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -14,7 +14,7 @@ import { Doc, IsField } from "../../../new_fields/Doc"; export class KeyValueBox extends React.Component { private _mainCont = React.createRef(); - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr); } + public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(KeyValueBox, fieldStr); } @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 011e8c8d9..b2299f34a 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -5,8 +5,9 @@ import { ProxyField } from "./Proxy"; import { FieldValue } from "./Types"; import { RefField, Id } from "./RefField"; import { ObjectField, Parent, OnUpdate } from "./ObjectField"; +import { action } from "mobx"; -export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean { +export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { target[prop] = value; return true; @@ -46,7 +47,7 @@ export function setter(target: any, prop: string | symbol | number, value: any, undo: () => receiver[prop] = curValue }); return true; -} +}); export function getter(target: any, prop: string | symbol | number, receiver: any): any { if (typeof prop === "symbol") { diff --git a/src/server/database.ts b/src/server/database.ts index 6b3b6797f..4775c0eeb 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -60,19 +60,28 @@ export class Database { } public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = Database.DocumentsCollection) { - console.log("getDocument"); - this.db && this.db.collection(collectionName).findOne({ id: id }, (err, result) => - fn(result ? ({ id: result._id, type: result.type, data: result.data }) : undefined)); + this.db && this.db.collection(collectionName).findOne({ _id: id }, (err, result) => { + if (result) { + result.id = result._id; + delete result._id; + fn(result); + } else { + fn(undefined); + } + }); } public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) { - console.log("getDocuments"); - this.db && this.db.collection(collectionName).find({ id: { "$in": ids } }).toArray((err, docs) => { + this.db && this.db.collection(collectionName).find({ _id: { "$in": ids } }).toArray((err, docs) => { if (err) { console.log(err.message); console.log(err.errmsg); } - fn(docs.map(doc => ({ id: doc._id, type: doc.type, data: doc.data }))); + fn(docs.map(doc => { + doc.id = doc._id; + delete doc._id; + return doc; + })); }); } diff --git a/src/server/index.ts b/src/server/index.ts index 10158eb96..6801b3132 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -234,16 +234,18 @@ server.on("connection", function (socket: Socket) { Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField); Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff)); - Utils.AddServerHandler(socket, MessageStore.GetRefField, GetRefField); - Utils.AddServerHandler(socket, MessageStore.GetRefFields, GetRefFields); + Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); + Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); }); -function deleteFields() { - return Database.Instance.deleteAll(); +async function deleteFields() { + await Database.Instance.deleteAll(); + await Database.Instance.deleteAll('newDocuments'); } async function deleteAll() { await Database.Instance.deleteAll(); + await Database.Instance.deleteAll('newDocuments'); await Database.Instance.deleteAll('sessions'); await Database.Instance.deleteAll('users'); } -- cgit v1.2.3-70-g09d2 From c89806e27fe3c3d951e86262f065e6df4073a2b8 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 27 Apr 2019 23:05:49 -0400 Subject: More fixes --- src/client/DocServer.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 07997f072..31a50adbd 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,6 +1,6 @@ import * as OpenSocket from 'socket.io-client'; -import { MessageStore, Types, Message } from "./../server/Message"; -import { Opt, FieldWaiting } from '../new_fields/Doc'; +import { MessageStore } from "./../server/Message"; +import { Opt } from '../new_fields/Doc'; import { Utils } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField, HandleUpdate } from '../new_fields/RefField'; @@ -56,18 +56,18 @@ export namespace DocServer { } } const prom = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds).then(fields => { - for (const key in fields) { - const field = fields[key]; + const fieldMap: { [id: string]: RefField } = {}; + for (const field of fields) { if (field) { - fields[key] = SerializationHelper.Deserialize(field); + fieldMap[field.id] = SerializationHelper.Deserialize(field); } } - return fields; + return fieldMap; }); - requestedIds.forEach((id, index) => _cache[id] = prom.then((fields: RefField[]) => fields[index])); + requestedIds.forEach(id => _cache[id] = prom.then(fields => fields[id])); const fields = await prom; - requestedIds.forEach((id, index) => { - const field = fields[index]; + requestedIds.forEach(id => { + const field = fields[id]; if (field) { _cache[id] = field; } else { @@ -113,7 +113,7 @@ export namespace DocServer { } } - function connected(message: string) { + function connected() { _socket.emit(MessageStore.Bar.Message, GUID); } -- cgit v1.2.3-70-g09d2 From 5d1a60b3df65b801de504c3d7b08d6a1fc9ef03b Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 1 May 2019 02:22:00 -0400 Subject: Added collaboration functionality --- src/client/DocServer.ts | 8 +++++++- src/new_fields/Doc.ts | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 31a50adbd..1d73abd1f 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -81,6 +81,9 @@ export namespace DocServer { } export function UpdateField(id: string, diff: any) { + if (id === updatingId) { + return; + } Utils.Emit(_socket, MessageStore.UpdateField, { id, diff }); } @@ -91,6 +94,7 @@ export namespace DocServer { Utils.Emit(_socket, MessageStore.CreateField, initialState); } + let updatingId: string | undefined; function respondToUpdate(diff: any) { const id = diff.id; if (id === undefined) { @@ -103,7 +107,9 @@ export namespace DocServer { } const handler = f[HandleUpdate]; if (handler) { - handler(diff); + updatingId = id; + handler.call(f, diff.diff); + updatingId = undefined; } }; if (field instanceof Promise) { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index b2979af11..6162b3c76 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -8,7 +8,7 @@ import { UndoManager, undoBatch } from "../client/util/UndoManager"; import { listSpec } from "./Schema"; import { List } from "./List"; import { ObjectField, Parent, OnUpdate } from "./ObjectField"; -import { RefField, FieldId, Id } from "./RefField"; +import { RefField, FieldId, Id, HandleUpdate } from "./RefField"; import { Docs } from "../client/documents/Documents"; export function IsField(field: any): field is Field { @@ -86,6 +86,21 @@ export class Doc extends RefField { private [SelfProxy]: any; public [WidthSym] = () => NumCast(this.__fields.width); // bcz: is this the right way to access width/height? it didn't work with : this.width public [HeightSym] = () => NumCast(this.__fields.height); + + public [HandleUpdate](diff: any) { + console.log(diff); + const set = diff.$set; + if (set) { + for (const key in set) { + if (!key.startsWith("fields.")) { + continue; + } + const value = SerializationHelper.Deserialize(set[key]); + const fKey = key.substring(7); + this[fKey] = value; + } + } + } } export namespace Doc { -- cgit v1.2.3-70-g09d2 From 718070e6a627305cfff7575797e8eb4abb93714a Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 1 May 2019 18:40:10 -0400 Subject: Fixed most of video annotations --- src/client/DocServer.ts | 8 ++++---- src/client/util/SerializationHelper.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 1d73abd1f..8e9f7865b 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -22,8 +22,8 @@ export namespace DocServer { let cached = _cache[id]; if (cached === undefined) { const prom = Utils.EmitCallback(_socket, MessageStore.GetRefField, id).then(fieldJson => { - const field = fieldJson === undefined ? fieldJson : SerializationHelper.Deserialize(fieldJson); - if (field) { + const field = SerializationHelper.Deserialize(fieldJson); + if (field !== undefined) { _cache[id] = field; } else { delete _cache[id]; @@ -58,7 +58,7 @@ export namespace DocServer { const prom = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds).then(fields => { const fieldMap: { [id: string]: RefField } = {}; for (const field of fields) { - if (field) { + if (field !== undefined) { fieldMap[field.id] = SerializationHelper.Deserialize(field); } } @@ -68,7 +68,7 @@ export namespace DocServer { const fields = await prom; requestedIds.forEach(id => { const field = fields[id]; - if (field) { + if (field !== undefined) { _cache[id] = field; } else { delete _cache[id]; diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index b5873eeb3..7ded85e43 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -8,8 +8,8 @@ export namespace SerializationHelper { } export function Serialize(obj: Field): any { - if (!obj) { - return null; + if (obj === undefined || obj === null) { + return undefined; } if (typeof obj !== 'object') { @@ -28,8 +28,8 @@ export namespace SerializationHelper { } export function Deserialize(obj: any): any { - if (!obj) { - return null; + if (obj === undefined || obj === null) { + return undefined; } if (typeof obj !== 'object') { -- cgit v1.2.3-70-g09d2 From ad9111cdef77795b2eca57b62d203c21cfef3357 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 2 May 2019 03:29:02 -0400 Subject: Various fixes: Added a couple missing prototype ids that weren't fetched Fixed DocServer bug that was affecting docking view --- src/client/DocServer.ts | 12 +++++++----- src/client/documents/Documents.ts | 2 +- src/client/views/collections/CollectionDockingView.tsx | 4 +--- src/new_fields/Doc.ts | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/client/DocServer.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 8e9f7865b..a288d394a 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -3,7 +3,7 @@ import { MessageStore } from "./../server/Message"; import { Opt } from '../new_fields/Doc'; import { Utils } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; -import { RefField, HandleUpdate } from '../new_fields/RefField'; +import { RefField, HandleUpdate, Id } from '../new_fields/RefField'; export namespace DocServer { const _cache: { [id: string]: RefField | Promise> } = {}; @@ -23,6 +23,9 @@ export namespace DocServer { if (cached === undefined) { const prom = Utils.EmitCallback(_socket, MessageStore.GetRefField, id).then(fieldJson => { const field = SerializationHelper.Deserialize(fieldJson); + if (_cache[id] !== undefined && !(_cache[id] instanceof Promise)) { + id; + } if (field !== undefined) { _cache[id] = field; } else { @@ -87,10 +90,9 @@ export namespace DocServer { Utils.Emit(_socket, MessageStore.UpdateField, { id, diff }); } - export function CreateField(initialState: any) { - if (!("id" in initialState)) { - throw new Error("Can't create a field on the server without an id"); - } + export function CreateField(field: RefField) { + _cache[field[Id]] = field; + const initialState = SerializationHelper.Serialize(field); Utils.Emit(_socket, MessageStore.CreateField, initialState); } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0333f4673..2de389a3c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -85,7 +85,7 @@ export namespace Docs { const iconProtoId = "iconProto"; export function initProtos(): Promise { - return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, pdfProtoId, imageProtoId, videoProtoId, audioProtoId, webProtoId, kvpProtoId]).then(fields => { + return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { textProto = fields[textProtoId] as Doc || CreateTextPrototype(); histoProto = fields[histoProtoId] as Doc || CreateHistogramPrototype(); collProto = fields[collProtoId] as Doc || CreateCollectionPrototype(); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 2facd404f..014773ab6 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -292,9 +292,7 @@ export class CollectionDockingView extends React.Component runInAction(() => { if (f instanceof Doc) { let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc)); - docs && docs.map((d, i) => d[Id] === f[Id] && docs!.splice(i, 1)); - // bcz: this seems like it should work, but it only does occasionally -- usually I get -1 - // docs && docs.indexOf(f) !== -1 && docs.splice(docs.indexOf(f), 1); + docs && docs.indexOf(f) !== -1 && docs.splice(docs.indexOf(f), 1); } })); tab.contentItem.remove(); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 8dd293f2d..d3d6ff93a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -51,7 +51,7 @@ export class Doc extends RefField { }); this[SelfProxy] = doc; if (!id || forceSave) { - DocServer.CreateField(SerializationHelper.Serialize(doc)); + DocServer.CreateField(doc); } return doc; } -- cgit v1.2.3-70-g09d2