aboutsummaryrefslogtreecommitdiff
path: root/src/fields/NewDoc.ts
diff options
context:
space:
mode:
authorTyler Schicke <tyler_schicke@brown.edu>2019-05-07 16:29:02 -0400
committerTyler Schicke <tyler_schicke@brown.edu>2019-05-07 16:29:02 -0400
commit14c776b6d30e0bc0d5b3712f28e4b9f1170eae3b (patch)
tree5255d8cce8a72a5b09cc1ad58661e2176295467a /src/fields/NewDoc.ts
parente19fdbba4cf672aee5bfb59b91b6162431d146d3 (diff)
parent26141a697ae52a7edf3cc6845ce2153111f8860e (diff)
Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web into new_search
Diffstat (limited to 'src/fields/NewDoc.ts')
-rw-r--r--src/fields/NewDoc.ts342
1 files changed, 0 insertions, 342 deletions
diff --git a/src/fields/NewDoc.ts b/src/fields/NewDoc.ts
deleted file mode 100644
index c22df4b70..000000000
--- a/src/fields/NewDoc.ts
+++ /dev/null
@@ -1,342 +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<T extends RefField> 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<any>;
-
- 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> = 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<T extends Field> extends ObjectField {
- constructor() {
- super();
- const list = new Proxy<this>(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<T extends Field> = ListImpl<T> & T[];
-export const List: { new <T extends Field>(): List<T> } = ListImpl as any;
-
-@Deserializable("doc").withFields(["id"])
-export class Doc extends RefField {
- constructor(id?: string, forceSave?: boolean) {
- super(id);
- const doc = new Proxy<this>(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<Field | undefined> {
- const self = doc[Self];
- return new Promise(res => getField(self, key, ignoreProto, res));
- }
- export const Prototype = Symbol("Prototype");
-}
-
-export const GetAsync = Doc.GetAsync;
-
-export type ToType<T> =
- T extends "string" ? string :
- T extends "number" ? number :
- T extends "boolean" ? boolean :
- T extends ListSpec<infer U> ? List<U> :
- T extends { new(...args: any[]): infer R } ? R : never;
-
-export type ToConstructor<T> =
- T extends string ? "string" :
- T extends number ? "number" :
- T extends boolean ? "boolean" : { new(...args: any[]): T };
-
-export type ToInterface<T> = {
- [P in keyof T]: ToType<T[P]>;
-};
-
-// type ListSpec<T extends Field[]> = { List: FieldCtor<Head<T>> | ListSpec<Tail<T>> };
-export type ListSpec<T> = { List: FieldCtor<T> };
-
-// type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
-
-type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
-type Tail<T extends any[]> =
- ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : [];
-type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true;
-
-interface Interface {
- [key: string]: ToConstructor<Field> | ListSpec<Field>;
- // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
-}
-
-type FieldCtor<T extends Field> = ToConstructor<T> | ListSpec<T>;
-
-function Cast<T extends FieldCtor<Field>>(field: Field | undefined, ctor: T): ToType<T> | undefined {
- if (field !== undefined) {
- if (typeof ctor === "string") {
- if (typeof field === ctor) {
- return field as ToType<T>;
- }
- } else if (typeof ctor === "object") {
- if (field instanceof List) {
- return field as ToType<T>;
- }
- } else if (field instanceof (ctor as any)) {
- return field as ToType<T>;
- }
- }
- return undefined;
-}
-
-export type makeInterface<T extends Interface> = Partial<ToInterface<T>> & Doc;
-export function makeInterface<T extends Interface>(schema: T): (doc: Doc) => makeInterface<T> {
- 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<T extends Interface> = Partial<ToInterface<T>>;
-export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc) => makeStrictInterface<T> {
- 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<T extends Interface>(schema: T): T {
- return schema;
-} \ No newline at end of file