From 430878f6dd8d36b1322e15d0898ada0d44fecacb Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 13 Feb 2019 16:38:09 -0500 Subject: Asdfjkla/sdfklj --- src/server/ServerUtil.ts | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/server/ServerUtil.ts (limited to 'src/server/ServerUtil.ts') diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts new file mode 100644 index 000000000..6757615fb --- /dev/null +++ b/src/server/ServerUtil.ts @@ -0,0 +1,60 @@ +import { Field } from './../fields/Field'; +import { TextField } from './../fields/TextField'; +import { NumberField } from './../fields/NumberField'; +import { RichTextField } from './../fields/RichTextField'; +import { Key } from './../fields/Key'; +import { ImageField } from './../fields/ImageField'; +import { ListField } from './../fields/ListField'; +import { Document } from './../fields/Document'; +import { Server } from './../client/Server'; +import { Types } from './Message'; +import { Utils } from '../Utils'; + +export class ServerUtils { + public static FromJson(json: string): Field { + let obj = JSON.parse(json) + let data: any = obj.data + let id: string = obj.id + let type: Types = obj.type + + if (!(data && id && type != undefined)) { + console.log("how did you manage to get an object that doesn't have a data or an id?") + return new TextField("Something to fill the space", Utils.GenerateGuid()); + } + + switch (type) { + case Types.Number: + return new NumberField(data, id) + case Types.Text: + return new TextField(data, id) + case Types.RichText: + return new RichTextField(data, id) + case Types.Key: + return new Key(data, id) + case Types.Image: + return new ImageField(data, id) + case Types.List: + return new ListField(data, id) + case Types.Document: + let doc: Document = new Document(id) + let fields: [string, string][] = data as [string, string][] + fields.forEach(element => { + doc._proxies.set(element[0], element[1]); + let keyId: string = element[0] + let valueId: string = element[1] + Server.GetField(keyId, (key: Field) => { + if (key instanceof Key) { + Server.GetField(valueId, (field: Field) => { + doc.Set(key as Key, field) + }) + } + else { + console.log("how did you get a key that isnt a key wtf") + } + }) + }); + return doc + } + return new TextField(data, id) + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f3544f004a696e4682fbd8d1f18ac69beec59439 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 13 Feb 2019 20:17:21 -0500 Subject: asdfkj --- src/Utils.ts | 2 - src/client/Server.ts | 35 +++++++++-- src/client/SocketStub.ts | 7 +-- src/client/documents/Documents.ts | 8 +-- src/client/views/Main.tsx | 122 ++++++++++++++++++++++---------------- src/fields/BasicField.ts | 1 + src/fields/Document.ts | 14 ++++- src/fields/DocumentReference.ts | 4 +- src/fields/Field.ts | 3 +- src/fields/ImageField.ts | 4 +- src/fields/Key.ts | 6 +- src/fields/ListField.ts | 5 +- src/fields/NumberField.ts | 5 +- src/fields/RichTextField.ts | 5 +- src/fields/TextField.ts | 5 +- src/server/Message.ts | 29 ++++----- src/server/ServerUtil.ts | 18 ++---- src/server/database.ts | 40 ++++++++----- src/server/index.ts | 33 ++++++----- 19 files changed, 198 insertions(+), 148 deletions(-) (limited to 'src/server/ServerUtil.ts') diff --git a/src/Utils.ts b/src/Utils.ts index f07c644b7..ce4f7ac3e 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -16,12 +16,10 @@ export class Utils { public static GenerateGuid(): string { return v4() - // return new Buffer(v4()).toString("hex").substr(0, 24); } public static GenerateDeterministicGuid(seed: string): string { return v5(seed, v5.URL) - // return new Buffer(v5(seed, v5.URL)).toString("hex").substr(0, 24); } public static GetScreenTransform(ele: HTMLElement): { scale: number, translateX: number, translateY: number } { diff --git a/src/client/Server.ts b/src/client/Server.ts index a08fc2e87..fab51ca9c 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -20,7 +20,9 @@ export class Server { this.ClientFieldsCached.set(fieldid, FieldWaiting); //simulating a server call with a registered callback action SocketStub.SEND_FIELD_REQUEST(fieldid, - action((field: Field) => callback(Server.cacheField(field)))); + action((field: Field) => { + callback(Server.cacheField(field)) + })); } else if (this.ClientFieldsCached.get(fieldid) != FieldWaiting) { callback(this.ClientFieldsCached.get(fieldid) as Field); } @@ -29,14 +31,24 @@ export class Server { static times = 0; // hack for testing public static GetDocumentField(doc: Document, key: Key) { - var hackTimeout: number = key == KeyStore.Data ? (this.times++ == 0 ? 5000 : 1000) : key == KeyStore.X ? 2500 : 500; + // let keyId: string = element[0] + // let valueId: string = element[1] + // Server.GetField(keyId, (key: Field) => { + // if (key instanceof Key) { + // Server.GetField(valueId, (field: Field) => { + // console.log(field) + // doc.Set(key as Key, field) + // }) + // } + // else { + // console.log("how did you get a key that isnt a key wtf") + // } + // }) return this.GetField(doc._proxies.get(key.Id), action((fieldfromserver: Field) => { - doc._proxies.delete(key.Id); doc.fields.set(key, fieldfromserver); - }) - , hackTimeout); + })); } public static AddDocument(document: Document) { @@ -48,8 +60,19 @@ export class Server { public static DeleteDocumentField(doc: Document, key: Key) { SocketStub.SEND_DELETE_DOCUMENT_FIELD(doc, key); } + + private static lock: boolean = false; + public static UpdateField(field: Field) { - SocketStub.SEND_SET_FIELD(field); + if (this.lock) { + setTimeout(this.UpdateField, 1000, field) + } + this.lock = true + SocketStub.SEND_SET_FIELD(field, (args: any) => { + if (this.lock) { + this.lock = false + } + }); } static connected(message: string) { diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts index 03c0131ba..136c69668 100644 --- a/src/client/SocketStub.ts +++ b/src/client/SocketStub.ts @@ -34,8 +34,7 @@ export class SocketStub { public static SEND_FIELD_REQUEST(fieldid: FIELD_ID, callback: (field: Field) => void) { if (fieldid) { - let args: GetFieldArgs = new GetFieldArgs(fieldid) - Utils.EmitCallback(Server.Socket, MessageStore.GetField, args, (field: Field) => callback(field)) + Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: Field) => callback(field)) } } @@ -67,12 +66,12 @@ export class SocketStub { document._proxies.delete(key.Id); } - public static SEND_SET_FIELD(field: Field) { + public static SEND_SET_FIELD(field: Field, fn: (args: any) => void) { // 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()) + Utils.EmitCallback(Server.Socket, MessageStore.SetField, field.ToJson(), fn) } } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 460bf9b23..f362af392 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -137,9 +137,9 @@ export namespace Documents { } let collectionProto: Document; - function GetCollectionPrototype(): Document { + function GetCollectionPrototype(isMainDoc: boolean): Document { if (!collectionProto) { - collectionProto = new Document(); + collectionProto = new Document(isMainDoc ? "dash" : undefined); collectionProto.Set(KeyStore.X, new NumberField(0)); collectionProto.Set(KeyStore.Y, new NumberField(0)); collectionProto.Set(KeyStore.Scale, new NumberField(1)); @@ -153,8 +153,8 @@ export namespace Documents { return collectionProto; } - export function CollectionDocument(documents: Array, options: DocumentOptions = {}): Document { - let doc = GetCollectionPrototype().MakeDelegate(); + export function CollectionDocument(documents: Array, options: DocumentOptions = {}, isMainDoc: boolean = false): Document { + let doc = GetCollectionPrototype(isMainDoc).MakeDelegate(); setupOptions(doc, options); doc.Set(KeyStore.Data, new ListField(documents)); return doc; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 197529c91..0f06f1278 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -14,16 +14,21 @@ import { ContextMenu } from './ContextMenu'; import { DocumentView } from './nodes/DocumentView'; import { ImageField } from '../../fields/ImageField'; import { CompileScript } from './../util/Scripting'; +import { Server } from '../Server'; +import { Utils } from '../../Utils'; +import { ServerUtils } from '../../server/ServerUtil'; +import { MessageStore, DocumentTransfer } from '../../server/Message'; +import { Database } from '../../server/database'; configure({ enforceActions: "observed" }); -const mainNodeCollection = new Array(); -let mainContainer = Documents.DockDocument(mainNodeCollection, { - x: 0, y: 0, title: "main container" -}) +// const mainNodeCollection = new Array(); +// let mainContainer = Documents.DockDocument(mainNodeCollection, { +// x: 0, y: 0, title: "main container" +// }) window.addEventListener("drop", function (e) { e.preventDefault(); @@ -54,11 +59,68 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) { // schemaDocs[4].SetData(KS.Author, "Bob", TextField); // schemaDocs.push(doc2); // const doc7 = Documents.SchemaDocument(schemaDocs) -const docset: Document[] = []; -let doc4 = Documents.CollectionDocument(docset, { - x: 0, y: 400, title: "mini collection" -}); -mainContainer = doc4; + +Utils.EmitCallback(Server.Socket, MessageStore.GetField, "dash", (res: any) => { + console.log("HELLO WORLD") + console.log("RESPONSE: " + res) + let mainContainer: Document = new Document(); + if (res) { + let obj = ServerUtils.FromJson(res) as Document + mainContainer = obj + console.log(mainContainer) + } + else { + const docset: Document[] = []; + let doc4 = Documents.CollectionDocument(docset, { + x: 0, y: 400, title: "mini collection" + }, true); + mainContainer = doc4; + let args = new DocumentTransfer(mainContainer.ToJson()) + Utils.Emit(Server.Socket, MessageStore.AddDocument, args) + } + + let addImageNode = action(() => { + mainContainer.GetList(KeyStore.Data, []).push(Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { + x: 0, y: 300, width: 200, height: 200, title: "added note" + })); + }) + let addTextNode = action(() => { + mainContainer.GetList(KeyStore.Data, []).push(Documents.TextDocument({ + x: 0, y: 300, width: 200, height: 200, title: "added note" + })); + }) + let addColNode = action(() => { + mainContainer.GetList(KeyStore.Data, []).push(Documents.CollectionDocument([], { + x: 0, y: 300, width: 200, height: 200, title: "added note" + })); + }) + + ReactDOM.render(( +
+ + + + + + +
), + document.getElementById('root')); +}) // let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { // x: 650, y: 500, width: 600, height: 600, title: "cat 2" // }); @@ -78,45 +140,3 @@ mainContainer = doc4; // mainContainer.Set(KeyStore.Data, mainNodes); //} //); - -let addImageNode = action(() => { - doc4.GetList(KeyStore.Data, []).push(Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { - x: 0, y: 300, width: 200, height: 200, title: "added note" - })); -}) -let addTextNode = action(() => { - doc4.GetList(KeyStore.Data, []).push(Documents.TextDocument({ - x: 0, y: 300, width: 200, height: 200, title: "added note" - })); -}) -let addColNode = action(() => { - doc4.GetList(KeyStore.Data, []).push(Documents.CollectionDocument([], { - x: 0, y: 300, width: 200, height: 200, title: "added note" - })); -}) - -ReactDOM.render(( -
- - - - - - -
), - document.getElementById('root')); \ No newline at end of file diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts index 9476f5d21..4b68ba01f 100644 --- a/src/fields/BasicField.ts +++ b/src/fields/BasicField.ts @@ -7,6 +7,7 @@ export abstract class BasicField extends Field { super(id); this.data = data; + Server.UpdateField(this) } @observable diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 56ac3c471..cb4f6f25c 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -8,12 +8,17 @@ import { findDOMNode } from "react-dom"; import { Server } from "../client/Server"; import { Types } from "../server/Message"; import { ObjectID } from "bson"; -import { Utils } from "../Utils"; export class Document extends Field { public fields: ObservableMap> = new ObservableMap(); public _proxies: ObservableMap = new ObservableMap(); + constructor(id?: string) { + super(id) + + Server.UpdateField(this) + } + @computed public get Title() { return this.GetText(KeyStore.Title, ""); @@ -89,9 +94,11 @@ export class Document extends Field { Set(key: Key, field: Field | undefined): void { if (field) { this.fields.set(key, field); + this._proxies.set(key.Id, field.Id) // Server.AddDocumentField(this, key, field); } else { this.fields.delete(key); + this._proxies.delete(key.Id) // Server.DeleteDocumentField(this, key); } Server.UpdateField(this); @@ -159,13 +166,15 @@ export class Document extends Field { throw new Error("Method not implemented."); } - ToJson(): { type: Types, data: [string, string][], _id: ObjectID } { + ToJson(): { type: Types, data: [string, string][], _id: string } { + console.log(this.fields) let fields: [string, string][] = [] this._proxies.forEach((field, key) => { if (field) { fields.push([key, field as string]) } }); + console.log(fields) return { type: Types.Document, @@ -173,5 +182,4 @@ export class Document extends Field { _id: this.Id } } - } \ No newline at end of file diff --git a/src/fields/DocumentReference.ts b/src/fields/DocumentReference.ts index b3d8e059d..b1edd1dff 100644 --- a/src/fields/DocumentReference.ts +++ b/src/fields/DocumentReference.ts @@ -43,11 +43,11 @@ export class DocumentReference extends Field { return ""; } - ToJson(): { type: Types, data: FIELD_ID, _id: ObjectID } { + ToJson(): { type: Types, data: FIELD_ID, _id: string } { return { type: Types.DocumentReference, data: this.document.Id, - _id: new ObjectID(this.Id) + _id: this.Id } } } \ No newline at end of file diff --git a/src/fields/Field.ts b/src/fields/Field.ts index 4b9d996ac..5a65e35b9 100644 --- a/src/fields/Field.ts +++ b/src/fields/Field.ts @@ -9,7 +9,6 @@ import { ImageField } from "./ImageField"; import { ListField } from "./ListField"; import { Document } from "./Document"; import { Server } from "../client/Server"; -import { ObjectID } from "bson"; export function Cast(field: FieldValue, ctor: { new(): T }): Opt { if (field) { @@ -65,5 +64,5 @@ export abstract class Field { abstract Copy(): Field; - abstract ToJson(): { _id: ObjectID, type: Types, data: any } + abstract ToJson(): { _id: string, type: Types, data: any } } \ No newline at end of file diff --git a/src/fields/ImageField.ts b/src/fields/ImageField.ts index 30fe0970b..12503b2ec 100644 --- a/src/fields/ImageField.ts +++ b/src/fields/ImageField.ts @@ -20,11 +20,11 @@ export class ImageField extends BasicField { return new ImageField(this.Data); } - ToJson(): { type: Types, data: URL, _id: ObjectID } { + ToJson(): { type: Types, data: URL, _id: string } { return { type: Types.Image, data: this.Data, - _id: new ObjectID(this.Id) + _id: this.Id } } } \ No newline at end of file diff --git a/src/fields/Key.ts b/src/fields/Key.ts index ffcbfc8b4..1e878a361 100644 --- a/src/fields/Key.ts +++ b/src/fields/Key.ts @@ -3,6 +3,7 @@ import { Utils } from "../Utils"; import { observable } from "mobx"; import { Types } from "../server/Message"; import { ObjectID } from "bson"; +import { Server } from "../client/Server"; export class Key extends Field { private name: string; @@ -15,6 +16,7 @@ export class Key extends Field { super(id || Utils.GenerateDeterministicGuid(name)); this.name = name; + Server.UpdateField(this) } TrySetValue(value: any): boolean { @@ -33,11 +35,11 @@ export class Key extends Field { return name; } - ToJson(): { type: Types, data: string, _id: ObjectID } { + ToJson(): { type: Types, data: string, _id: string } { return { type: Types.Key, data: this.name, - _id: new ObjectID(this.Id) + _id: this.Id } } } diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts index e98ced902..cf8a1ba8b 100644 --- a/src/fields/ListField.ts +++ b/src/fields/ListField.ts @@ -1,7 +1,6 @@ import { Field, FIELD_ID } from "./Field"; import { BasicField } from "./BasicField"; import { Types } from "../server/Message"; -import { ObjectId } from "bson"; export class ListField extends BasicField { constructor(data: T[] = [], id: FIELD_ID = undefined) { @@ -16,11 +15,11 @@ export class ListField extends BasicField { return new ListField(this.Data); } - ToJson(): { type: Types, data: T[], _id: ObjectId } { + ToJson(): { type: Types, data: T[], _id: string } { return { type: Types.List, data: this.Data, - _id: new ObjectId(this.Id) + _id: this.Id } } } \ No newline at end of file diff --git a/src/fields/NumberField.ts b/src/fields/NumberField.ts index ce07a18b8..7fa9ec2e4 100644 --- a/src/fields/NumberField.ts +++ b/src/fields/NumberField.ts @@ -1,7 +1,6 @@ import { BasicField } from "./BasicField" import { Types } from "../server/Message"; import { FIELD_ID } from "./Field"; -import { ObjectID } from "bson"; export class NumberField extends BasicField { constructor(data: number = 0, id: FIELD_ID = undefined) { @@ -16,9 +15,9 @@ export class NumberField extends BasicField { return new NumberField(this.Data); } - ToJson(): { _id: ObjectID, type: Types, data: number } { + ToJson(): { _id: string, type: Types, data: number } { return { - _id: new ObjectID(this.Id), + _id: this.Id, type: Types.Number, data: this.Data } diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts index a7ea1f2ce..3c6151009 100644 --- a/src/fields/RichTextField.ts +++ b/src/fields/RichTextField.ts @@ -1,7 +1,6 @@ import { BasicField } from "./BasicField"; import { Types } from "../server/Message"; import { FIELD_ID } from "./Field"; -import { ObjectID } from "bson"; export class RichTextField extends BasicField { constructor(data: string = "", id: FIELD_ID = undefined) { @@ -16,11 +15,11 @@ export class RichTextField extends BasicField { return new RichTextField(this.Data); } - ToJson(): { type: Types, data: string, _id: ObjectID } { + ToJson(): { type: Types, data: string, _id: string } { return { type: Types.RichText, data: this.Data, - _id: new ObjectID(this.Id) + _id: this.Id } } diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts index 692b746d1..f2b277298 100644 --- a/src/fields/TextField.ts +++ b/src/fields/TextField.ts @@ -1,7 +1,6 @@ import { BasicField } from "./BasicField" import { FIELD_ID } from "./Field"; import { Types } from "../server/Message"; -import { ObjectID } from "bson"; export class TextField extends BasicField { constructor(data: string = "", id: FIELD_ID = undefined) { @@ -16,11 +15,11 @@ export class TextField extends BasicField { return new TextField(this.Data); } - ToJson(): { type: Types, data: string, _id: ObjectID } { + ToJson(): { type: Types, data: string, _id: string } { return { type: Types.Text, data: this.Data, - _id: new ObjectID(this.Id) + _id: this.Id } } } diff --git a/src/server/Message.ts b/src/server/Message.ts index 0391b6671..f63168223 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -52,9 +52,9 @@ export enum Types { export class DocumentTransfer implements Transferable { readonly type = Types.Document - _id: ObjectId; + _id: string - constructor(readonly obj: { type: Types, data: [string, string][], _id: ObjectId }) { + constructor(readonly obj: { type: Types, data: [string, string][], _id: string }) { this._id = obj._id } } @@ -62,56 +62,56 @@ export class DocumentTransfer implements Transferable { export class ImageTransfer implements Transferable { readonly type = Types.Image - constructor(readonly _id: ObjectId) { } + constructor(readonly _id: string) { } } export class KeyTransfer implements Transferable { name: string - readonly _id: ObjectId + readonly _id: string readonly type = Types.Key constructor(i: string, n: string) { this.name = n - this._id = new ObjectId(i) + this._id = i } } export class ListTransfer implements Transferable { type = Types.List; - constructor(readonly _id: ObjectId) { } + constructor(readonly _id: string) { } } export class NumberTransfer implements Transferable { readonly type = Types.Number - constructor(readonly value: number, readonly _id: ObjectId) { } + constructor(readonly value: number, readonly _id: string) { } } export class TextTransfer implements Transferable { value: string - readonly _id: ObjectId + readonly _id: string readonly type = Types.Text constructor(t: string, i: string) { this.value = t - this._id = new ObjectId(i) + this._id = i } } export class RichTextTransfer implements Transferable { value: string - readonly _id: ObjectId + readonly _id: string readonly type = Types.Text constructor(t: string, i: string) { this.value = t - this._id = new ObjectId(i) + this._id = i } } export interface Transferable { - readonly _id: ObjectId + readonly _id: string readonly type: Types } @@ -119,6 +119,7 @@ export namespace MessageStore { export const Foo = new Message("Foo"); export const Bar = new Message("Bar"); export const AddDocument = new Message("Add Document"); - export const SetField = new Message<{ _id: ObjectId, data: any, type: Types }>("Set Field") - export const GetField = new Message("Get Field") + export const SetField = new Message<{ _id: string, data: any, type: Types }>("Set Field") + export const GetField = new Message("Get Field") + export const GetDocument = new Message("Get Document"); } \ No newline at end of file diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts index 6757615fb..d1de71dbe 100644 --- a/src/server/ServerUtil.ts +++ b/src/server/ServerUtil.ts @@ -12,9 +12,10 @@ import { Utils } from '../Utils'; export class ServerUtils { public static FromJson(json: string): Field { - let obj = JSON.parse(json) + let obj = JSON.parse(JSON.stringify(json)) + console.log(obj) let data: any = obj.data - let id: string = obj.id + let id: string = obj._id let type: Types = obj.type if (!(data && id && type != undefined)) { @@ -40,19 +41,8 @@ export class ServerUtils { let fields: [string, string][] = data as [string, string][] fields.forEach(element => { doc._proxies.set(element[0], element[1]); - let keyId: string = element[0] - let valueId: string = element[1] - Server.GetField(keyId, (key: Field) => { - if (key instanceof Key) { - Server.GetField(valueId, (field: Field) => { - doc.Set(key as Key, field) - }) - } - else { - console.log("how did you get a key that isnt a key wtf") - } - }) }); + console.log(doc._proxies) return doc } return new TextField(data, id) diff --git a/src/server/database.ts b/src/server/database.ts index 72ddbc82c..5844a88a2 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -9,8 +9,8 @@ export class Database { private MongoClient = mongodb.MongoClient; private url = 'mongodb://localhost:27017/Dash'; - public update(id: mongodb.ObjectID, value: any) { - this.MongoClient.connect(this.url, (err, db) => { + public update(id: string, value: any) { + this.MongoClient.connect(this.url, { bufferMaxEntries: 1 }, (err, db) => { let collection = db.db().collection('documents'); collection.update({ _id: id }, { $set: value }); db.close(); @@ -18,7 +18,7 @@ export class Database { } public delete(id: string) { - this.MongoClient.connect(this.url, (err, db) => { + this.MongoClient.connect(this.url, { bufferMaxEntries: 1 }, (err, db) => { let collection = db.db().collection('documents'); collection.remove({ _id: id }); db.close(); @@ -26,32 +26,40 @@ export class Database { } public insert(kvpairs: any) { - this.MongoClient.connect(this.url, (err, db) => { + this.MongoClient.connect(this.url, { bufferMaxEntries: 1 }, (err, db) => { + // console.log(kvpairs) let collection = db.db().collection('documents'); - collection.insertOne(kvpairs, () => { }); + collection.insertOne(kvpairs, (err: any, res: any) => { + if (err) { + // console.log(err) + return + } + // console.log(kvpairs) + // console.log("1 document inserted") + }); db.close(); }); } - public getDocument(id: mongodb.ObjectID): string | undefined { + public getDocument(id: string, fn: (res: any) => void) { var result: JSON; - this.MongoClient.connect(this.url, (err, db) => { + this.MongoClient.connect(this.url, { + bufferMaxEntries: 1 + }, (err, db) => { if (err) { console.log(err) return undefined } let collection = db.db().collection('documents'); - collection.findOne({ _id: Utils.GenerateDeterministicGuid(id.toHexString()) }, (err: any, res: any) => result = res) - console.log(result) + collection.findOne({ _id: id }, (err: any, res: any) => { + result = res + if (!result) { + fn(undefined) + } + fn(result) + }) db.close(); - if (!result) { - console.log("not found") - return undefined - } - console.log("found") - return result; }); - return undefined } public print() { diff --git a/src/server/index.ts b/src/server/index.ts index 98d897d2f..b256b6a82 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -13,6 +13,7 @@ import { FIELD_ID, Field } from '../fields/Field'; import { Database } from './database'; import { ServerUtils } from './ServerUtil'; import { ObjectID } from 'mongodb'; +import { Document } from '../fields/Document'; const config = require('../../webpack.config') const compiler = webpack(config) const port = 1050; // default port to listen @@ -65,22 +66,26 @@ function addDocument(document: Document) { } -function setField(newValue: Transferable) { - console.log(newValue._id) - if (Database.Instance.getDocument(newValue._id)) { - Database.Instance.update(newValue._id, newValue) - } - else { - Database.Instance.insert(newValue) - } +function getField([id, callback]: [string, (result: any) => void]) { + Database.Instance.getDocument(id, (result: any) => { + if (result) { + callback(result) + } + else { + callback(undefined) + } + }) } -function getField([fieldRequest, callback]: [GetFieldArgs, (field: Field) => void]) { - let fieldid: string = fieldRequest.field - let result: string | undefined = Database.Instance.getDocument(new ObjectID(fieldid)) - if (result) { - let fromJson: Field = ServerUtils.FromJson(result) - } +function setField(newValue: Transferable) { + Database.Instance.getDocument(newValue._id, (res: any) => { + if (res) { + Database.Instance.update(newValue._id, newValue) + } + else { + Database.Instance.insert(newValue) + } + }) } server.listen(serverPort); -- cgit v1.2.3-70-g09d2 From 4bcc62fd164c5ee6c4fc50077753ba7d969478e3 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 14 Feb 2019 05:43:09 -0500 Subject: Got almost all of collaboration and server communication working --- src/client/Server.ts | 53 ++++++-- src/client/SocketStub.ts | 23 +++- src/client/documents/Documents.ts | 41 +++--- src/client/views/Main.tsx | 138 +++++++++++---------- .../views/collections/CollectionFreeFormView.tsx | 21 ++-- src/client/views/nodes/DocumentView.tsx | 4 + src/fields/BasicField.ts | 12 +- src/fields/Document.ts | 37 +++--- src/fields/DocumentReference.ts | 4 + src/fields/Field.ts | 14 +-- src/fields/ImageField.ts | 4 +- src/fields/Key.ts | 10 +- src/fields/ListField.ts | 65 +++++++++- src/fields/NumberField.ts | 4 +- src/fields/RichTextField.ts | 4 +- src/fields/TextField.ts | 4 +- src/server/Message.ts | 1 + src/server/ServerUtil.ts | 22 ++-- src/server/database.ts | 25 +++- src/server/index.ts | 27 ++-- 20 files changed, 341 insertions(+), 172 deletions(-) (limited to 'src/server/ServerUtil.ts') diff --git a/src/client/Server.ts b/src/client/Server.ts index fab51ca9c..e9edb7b9c 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -1,34 +1,57 @@ -import { Field, FieldWaiting, FIELD_ID, FIELD_WAITING, FieldValue } from "../fields/Field" +import { Field, FieldWaiting, FIELD_ID, FIELD_WAITING, FieldValue, Opt } from "../fields/Field" import { Key, KeyStore } from "../fields/Key" import { ObservableMap, action } from "mobx"; import { Document } from "../fields/Document" import { SocketStub } from "./SocketStub"; import * as OpenSocket from 'socket.io-client'; import { Utils } from "./../Utils"; -import { MessageStore } from "./../server/Message"; +import { MessageStore, Types } from "./../server/Message"; export class Server { private static ClientFieldsCached: ObservableMap = new ObservableMap(); - static Socket: SocketIOClient.Socket = OpenSocket("http://localhost:1234") + static Socket: SocketIOClient.Socket = OpenSocket("http://localhost:1234"); static GUID: string = Utils.GenerateGuid() + private static Cache: { [id: string]: Field } = {}; + + @action + static updateField(field: { _id: string, data: any, type: Types }) { + if (field._id in Server.Cache) { + const f = Server.Cache[field._id]; + f.UpdateFromServer(field.data); + f.init(() => { }); + } + } + // 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. // 'hackTimeout' is here temporarily for simplicity when debugging things. - public static GetField(fieldid: FIELD_ID, callback: (field: Field) => void = (f) => { }, hackTimeout: number = -1) { + public static GetField(fieldid: FIELD_ID, callback: (field: Opt) => void = (f) => { }, hackTimeout: number = -1) { if (!this.ClientFieldsCached.get(fieldid)) { - this.ClientFieldsCached.set(fieldid, FieldWaiting); + // this.ClientFieldsCached.set(fieldid, FieldWaiting); //simulating a server call with a registered callback action - SocketStub.SEND_FIELD_REQUEST(fieldid, - action((field: Field) => { - callback(Server.cacheField(field)) - })); + SocketStub.SEND_FIELD_REQUEST(fieldid, (field) => { + if (field) { + this.Cache[field.Id] = field; + } + callback(field) + }); } else if (this.ClientFieldsCached.get(fieldid) != FieldWaiting) { callback(this.ClientFieldsCached.get(fieldid) as Field); } return this.ClientFieldsCached.get(fieldid); } + public static GetFields(fieldIds: FIELD_ID[], callback: (fields: { [id: string]: Field }) => any) { + SocketStub.SEND_FIELDS_REQUEST(fieldIds, (fields) => { + for (let key in fields) { + let field = fields[key]; + this.Cache[field.Id] = field; + } + callback(fields) + }); + } + static times = 0; // hack for testing public static GetDocumentField(doc: Document, key: Key) { // let keyId: string = element[0] @@ -46,8 +69,10 @@ export class Server { // }) return this.GetField(doc._proxies.get(key.Id), - action((fieldfromserver: Field) => { - doc.fields.set(key, fieldfromserver); + action((fieldfromserver: Opt) => { + if (fieldfromserver) { + doc.fields.set(key.Id, { key, field: fieldfromserver }); + } })); } @@ -65,9 +90,10 @@ export class Server { public static UpdateField(field: Field) { if (this.lock) { - setTimeout(this.UpdateField, 1000, field) + // setTimeout(this.UpdateField, 1000, field) } this.lock = true + // console.log("updating field " + field.Id) SocketStub.SEND_SET_FIELD(field, (args: any) => { if (this.lock) { this.lock = false @@ -91,4 +117,5 @@ export class Server { } } -Server.Socket.on(MessageStore.Foo.Message, Server.connected); \ No newline at end of file +Server.Socket.on(MessageStore.Foo.Message, Server.connected); +Server.Socket.on(MessageStore.SetField.Message, Server.updateField); \ No newline at end of file diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts index 136c69668..7545a166c 100644 --- a/src/client/SocketStub.ts +++ b/src/client/SocketStub.ts @@ -1,10 +1,11 @@ -import { Field, FIELD_ID } from "../fields/Field" +import { Field, FIELD_ID, Opt } from "../fields/Field" import { Key, KeyStore } from "../fields/Key" import { ObservableMap, action } from "mobx"; import { Document } from "../fields/Document" import { MessageStore, SetFieldArgs, GetFieldArgs, DocumentTransfer, Types } from "../server/Message"; import { Utils } from "../Utils"; import { Server } from "./Server"; +import { ServerUtils } from "../server/ServerUtil"; export class SocketStub { @@ -32,12 +33,28 @@ export class SocketStub { Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(document.ToJson())) } - public static SEND_FIELD_REQUEST(fieldid: FIELD_ID, callback: (field: Field) => void) { + public static SEND_FIELD_REQUEST(fieldid: FIELD_ID, callback: (field: Opt) => void) { if (fieldid) { - Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: Field) => callback(field)) + Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: any) => { + if (field) { + ServerUtils.FromJson(field).init(callback); + } else { + callback(undefined); + } + }) } } + public static SEND_FIELDS_REQUEST(fieldIds: FIELD_ID[], callback: (fields: { [key: string]: Field }) => any) { + Utils.EmitCallback(Server.Socket, MessageStore.GetFields, fieldIds, (fields: any[]) => { + let fieldMap: any = {}; + for (let field of fields) { + fieldMap[field._id] = ServerUtils.FromJson(field); + } + callback(fieldMap) + }); + } + public static SEND_ADD_DOCUMENT_FIELD(doc: Document, key: Key, value: Field) { // Send a serialized version of the field to the server along with the diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f362af392..210e63cd3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -21,6 +21,17 @@ interface DocumentOptions { } export namespace Documents { + export function initProtos(callback: () => void) { + Server.GetFields([collectionProtoId, textProtoId, imageProtoId, schemaProtoId, dockProtoId], (fields) => { + collectionProto = fields[collectionProtoId] as Document; + imageProto = fields[imageProtoId] as Document; + textProto = fields[textProtoId] as Document; + dockProto = fields[dockProtoId] as Document; + schemaProto = fields[schemaProtoId] as Document; + callback() + }); + } + function setupOptions(doc: Document, options: DocumentOptions): void { if (options.x) { doc.SetData(KeyStore.X, options.x, NumberField); @@ -43,9 +54,10 @@ export namespace Documents { } let textProto: Document; + const textProtoId = "textProto"; function GetTextPrototype(): Document { if (!textProto) { - textProto = new Document(); + textProto = new Document(textProtoId); textProto.Set(KeyStore.X, new NumberField(0)); textProto.Set(KeyStore.Y, new NumberField(0)); textProto.Set(KeyStore.Width, new NumberField(300)); @@ -64,9 +76,10 @@ export namespace Documents { } let schemaProto: Document; + const schemaProtoId = "schemaProto"; function GetSchemaPrototype(): Document { if (!schemaProto) { - schemaProto = new Document(); + schemaProto = new Document(schemaProtoId); schemaProto.Set(KeyStore.X, new NumberField(0)); schemaProto.Set(KeyStore.Y, new NumberField(0)); schemaProto.Set(KeyStore.Width, new NumberField(300)); @@ -86,6 +99,7 @@ export namespace Documents { let dockProto: Document; + const dockProtoId = "dockProto"; function GetDockPrototype(): Document { if (!dockProto) { dockProto = new Document(); @@ -107,11 +121,11 @@ export namespace Documents { } - let imageProtoId: FIELD_ID; + let imageProto: Document; + const imageProtoId = "imageProto"; function GetImagePrototype(): Document { - if (imageProtoId === undefined) { - let imageProto = new Document(); - imageProtoId = imageProto.Id; + if (!imageProto) { + imageProto = new Document(imageProtoId); imageProto.Set(KeyStore.Title, new TextField("IMAGE PROTO")); imageProto.Set(KeyStore.X, new NumberField(0)); imageProto.Set(KeyStore.Y, new NumberField(0)); @@ -120,26 +134,23 @@ export namespace Documents { imageProto.Set(KeyStore.Layout, new TextField(ImageBox.LayoutString())); // imageProto.SetField(KeyStore.Layout, new TextField('
')); imageProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); - // Server.AddDocument(imageProto); return imageProto; } - return new Document(); - // return Server.GetField(imageProtoId) as Document; + return imageProto; } export function ImageDocument(url: string, options: DocumentOptions = {}): Document { let doc = GetImagePrototype().MakeDelegate(); setupOptions(doc, options); doc.Set(KeyStore.Data, new ImageField(new URL(url))); - // Server.AddDocument(doc); - // var sdoc = Server.GetField(doc.Id) as Document; return doc; } let collectionProto: Document; - function GetCollectionPrototype(isMainDoc: boolean): Document { + const collectionProtoId = "collectionProto"; + function GetCollectionPrototype(): Document { if (!collectionProto) { - collectionProto = new Document(isMainDoc ? "dash" : undefined); + collectionProto = new Document(collectionProtoId); collectionProto.Set(KeyStore.X, new NumberField(0)); collectionProto.Set(KeyStore.Y, new NumberField(0)); collectionProto.Set(KeyStore.Scale, new NumberField(1)); @@ -153,8 +164,8 @@ export namespace Documents { return collectionProto; } - export function CollectionDocument(documents: Array, options: DocumentOptions = {}, isMainDoc: boolean = false): Document { - let doc = GetCollectionPrototype(isMainDoc).MakeDelegate(); + export function CollectionDocument(documents: Array, options: DocumentOptions = {}, id?: string): Document { + let doc = GetCollectionPrototype().MakeDelegate(id); setupOptions(doc, options); doc.Set(KeyStore.Data, new ListField(documents)); return doc; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 376876ebb..14e60409e 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -60,77 +60,79 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) { // schemaDocs.push(doc2); // const doc7 = Documents.SchemaDocument(schemaDocs) -Utils.EmitCallback(Server.Socket, MessageStore.GetField, "dash", (res: any) => { - console.log("HELLO WORLD") - console.log("RESPONSE: " + res) - let mainContainer: Document = new Document(); - if (res) { - let obj = ServerUtils.FromJson(res) as Document - mainContainer = obj - console.log(mainContainer) - } - else { - const docset: Document[] = []; - let doc4 = Documents.CollectionDocument(docset, { - x: 0, y: 400, title: "mini collection" - }, true); - mainContainer = doc4; - let args = new DocumentTransfer(mainContainer.ToJson()) - Utils.Emit(Server.Socket, MessageStore.AddDocument, args) - } +const mainDocId = "mainDoc"; +Documents.initProtos(() => { + Utils.EmitCallback(Server.Socket, MessageStore.GetField, mainDocId, (res: any) => { + console.log("HELLO WORLD") + console.log("RESPONSE: " + res) + let mainContainer: Document; + if (res) { + let obj = ServerUtils.FromJson(res) as Document + mainContainer = obj + } + else { + const docset: Document[] = []; + let doc4 = Documents.CollectionDocument(docset, { + x: 0, y: 400, title: "mini collection" + }, mainDocId); + mainContainer = doc4; + let args = new DocumentTransfer(mainContainer.ToJson()) + Utils.Emit(Server.Socket, MessageStore.AddDocument, args) + } - let addImageNode = action(() => { - mainContainer.GetList(KeyStore.Data, []).push(Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { - x: 0, y: 300, width: 200, height: 200, title: "added note" - })); - }) - let addTextNode = action(() => { - mainContainer.GetList(KeyStore.Data, []).push(Documents.TextDocument({ - x: 0, y: 300, width: 200, height: 200, title: "added note" - })); - }) - let addColNode = action(() => { - mainContainer.GetList(KeyStore.Data, []).push(Documents.CollectionDocument([], { - x: 0, y: 300, width: 200, height: 200, title: "added note" - })); - }) + let addImageNode = action(() => { + mainContainer.GetList(KeyStore.Data, []).push(Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { + x: 0, y: 300, width: 200, height: 200, title: "added note" + })); + }) + let addTextNode = action(() => { + mainContainer.GetList(KeyStore.Data, []).push(Documents.TextDocument({ + x: 0, y: 300, width: 200, height: 200, title: "added note" + })); + }) + let addColNode = action(() => { + mainContainer.GetList(KeyStore.Data, []).push(Documents.CollectionDocument([], { + x: 0, y: 300, width: 200, height: 200, title: "added note" + })); + }) - let clearDatabase = action(() => { - Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}); - }) + let clearDatabase = action(() => { + Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}); + }) - ReactDOM.render(( -
- - - - - - - -
), - document.getElementById('root')); -}) + ReactDOM.render(( +
+ + + + + + + +
), + document.getElementById('root')); + }) +}); // let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { // x: 650, y: 500, width: 600, height: 600, title: "cat 2" // }); diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 9cf29d000..c7ead2f2f 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -17,7 +17,6 @@ import { FieldWaiting } from "../../../fields/Field"; @observer export class CollectionFreeFormView extends CollectionViewBase { public static LayoutString() { return CollectionViewBase.LayoutString("CollectionFreeFormView"); } - private _containerRef = React.createRef(); private _canvasRef = React.createRef(); private _nodeContainerRef = React.createRef(); private _lastX: number = 0; @@ -51,9 +50,13 @@ export class CollectionFreeFormView extends CollectionViewBase { e.stopPropagation(); } - componentDidMount() { - if (this._containerRef.current) { - DragManager.MakeDropTarget(this._containerRef.current, { + private dropDisposer?: DragManager.DragDropDisposer; + createDropTarget = (ele: HTMLDivElement) => { + if (this.dropDisposer) { + this.dropDisposer(); + } + if (ele) { + this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop } @@ -174,7 +177,11 @@ export class CollectionFreeFormView extends CollectionViewBase { render() { const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; - const value: Document[] = Document.GetList(fieldKey, []); + // const value: Document[] = Document.GetList(fieldKey, []); + const lvalue = Document.GetT>(fieldKey, ListField); + if (!lvalue || lvalue === "") { + return

Error loading collection data

+ } const panx: number = Document.GetNumber(KeyStore.PanX, 0); const pany: number = Document.GetNumber(KeyStore.PanY, 0); const currScale: number = Document.GetNumber(KeyStore.Scale, 1); @@ -189,11 +196,11 @@ export class CollectionFreeFormView extends CollectionViewBase { onContextMenu={(e) => e.preventDefault()} onDrop={this.onDrop} onDragOver={this.onDragOver} - ref={this._containerRef}> + ref={this.createDropTarget}>
- {value.map(doc => { + {lvalue.Data.map(doc => { return (); })}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 730ce62f2..3df351c6c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -128,6 +128,10 @@ export class DocumentView extends React.Component { render() { let bindings = { ...this.props } as any; + let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField); + if (!lkeys || lkeys === "") { + return

Error loading layout keys

; + } for (const key of this.layoutKeys) { bindings[key.Name + "Key"] = key; // this maps string values of the form Key to an actual key Kestore.keyname e.g, "DataKey" => KeyStore.Data } diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts index 4b68ba01f..95f737dea 100644 --- a/src/fields/BasicField.ts +++ b/src/fields/BasicField.ts @@ -3,11 +3,19 @@ import { observable, computed, action } from "mobx"; import { Server } from "../client/Server"; export abstract class BasicField extends Field { - constructor(data: T, id: FIELD_ID = undefined) { + constructor(data: T, save: boolean, id: FIELD_ID = undefined) { super(id); this.data = data; - Server.UpdateField(this) + if (save) { + Server.UpdateField(this) + } + } + + UpdateFromServer(data: any) { + if (this.data !== data) { + this.data = data; + } } @observable diff --git a/src/fields/Document.ts b/src/fields/Document.ts index cb4f6f25c..4bab1299d 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -10,13 +10,22 @@ import { Types } from "../server/Message"; import { ObjectID } from "bson"; export class Document extends Field { - public fields: ObservableMap> = new ObservableMap(); + public fields: ObservableMap }> = new ObservableMap(); public _proxies: ObservableMap = new ObservableMap(); - constructor(id?: string) { + constructor(id?: string, save: boolean = true) { super(id) - Server.UpdateField(this) + if (save) { + Server.UpdateField(this) + } + } + + UpdateFromServer(data: [string, string][]) { + for (const key in data) { + const element = data[key]; + this._proxies.set(element[0], element[1]); + } } @computed @@ -27,25 +36,25 @@ export class Document extends Field { Get(key: Key, ignoreProto: boolean = false): FieldValue { let field: FieldValue; if (ignoreProto) { - if (this.fields.has(key)) { - field = this.fields.get(key); + if (this.fields.has(key.Id)) { + field = this.fields.get(key.Id)!.field; } else if (this._proxies.has(key.Id)) { field = Server.GetDocumentField(this, key); } } else { let doc: FieldValue = this; while (doc && doc != FieldWaiting && field != FieldWaiting) { - if (!doc.fields.has(key)) { + if (!doc.fields.has(key.Id)) { if (doc._proxies.has(key.Id)) { field = Server.GetDocumentField(doc, key); break; } - if ((doc.fields.has(KeyStore.Prototype) || doc._proxies.has(KeyStore.Prototype.Id))) { + if ((doc.fields.has(KeyStore.Prototype.Id) || doc._proxies.has(KeyStore.Prototype.Id))) { doc = doc.GetPrototype(); } else break; } else { - field = doc.fields.get(key); + field = doc.fields.get(key.Id)!.field; break; } } @@ -93,11 +102,11 @@ export class Document extends Field { @action Set(key: Key, field: Field | undefined): void { if (field) { - this.fields.set(key, 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); + this.fields.delete(key.Id); this._proxies.delete(key.Id) // Server.DeleteDocumentField(this, key); } @@ -144,8 +153,8 @@ export class Document extends Field { return protos; } - MakeDelegate(): Document { - let delegate = new Document(); + MakeDelegate(id?: string): Document { + let delegate = new Document(id); delegate.Set(KeyStore.Prototype, this); @@ -167,14 +176,14 @@ export class Document extends Field { } ToJson(): { type: Types, data: [string, string][], _id: string } { - console.log(this.fields) + // console.log(this.fields) let fields: [string, string][] = [] this._proxies.forEach((field, key) => { if (field) { fields.push([key, field as string]) } }); - console.log(fields) + // console.log(fields) return { type: Types.Document, diff --git a/src/fields/DocumentReference.ts b/src/fields/DocumentReference.ts index b1edd1dff..4096cbb92 100644 --- a/src/fields/DocumentReference.ts +++ b/src/fields/DocumentReference.ts @@ -17,6 +17,10 @@ export class DocumentReference extends Field { super(); } + UpdateFromServer() { + + } + Dereference(): FieldValue { return this.document.Get(this.key); } diff --git a/src/fields/Field.ts b/src/fields/Field.ts index 5a65e35b9..853fb9327 100644 --- a/src/fields/Field.ts +++ b/src/fields/Field.ts @@ -1,14 +1,6 @@ import { Utils } from "../Utils"; import { Types } from "../server/Message"; -import { NumberField } from "./NumberField"; -import { TextField } from "./TextField"; -import { RichTextField } from "./RichTextField"; -import { KeyStore, Key } from "./Key"; -import { ImageField } from "./ImageField"; -import { ListField } from "./ListField"; -import { Document } from "./Document"; -import { Server } from "../client/Server"; export function Cast(field: FieldValue, ctor: { new(): T }): Opt { if (field) { @@ -28,6 +20,10 @@ export type FieldValue = Opt | FIELD_WAITING; export abstract class Field { //FieldUpdated: TypedEvent> = new TypedEvent>(); + init(callback: (res: Field) => any) { + callback(this); + } + private id: string; get Id(): string { return this.id; @@ -56,6 +52,8 @@ export abstract class Field { return this.id === other.id; } + abstract UpdateFromServer(serverData: any): void; + abstract ToScriptString(): string; abstract TrySetValue(value: any): boolean; diff --git a/src/fields/ImageField.ts b/src/fields/ImageField.ts index 12503b2ec..aba011199 100644 --- a/src/fields/ImageField.ts +++ b/src/fields/ImageField.ts @@ -4,8 +4,8 @@ import { Types } from "../server/Message"; import { ObjectID } from "bson"; export class ImageField extends BasicField { - constructor(data: URL | undefined = undefined, id: FIELD_ID = undefined) { - super(data == undefined ? new URL("http://cs.brown.edu/~bcz/bob_fettucine.jpg") : data, id); + constructor(data: URL | undefined = undefined, id: FIELD_ID = undefined, save: boolean = true) { + super(data == undefined ? new URL("http://cs.brown.edu/~bcz/bob_fettucine.jpg") : data, save, id); } toString(): string { diff --git a/src/fields/Key.ts b/src/fields/Key.ts index 1e878a361..51d8e093c 100644 --- a/src/fields/Key.ts +++ b/src/fields/Key.ts @@ -12,11 +12,17 @@ export class Key extends Field { return this.name; } - constructor(name: string, id?: string) { + constructor(name: string, id?: string, save: boolean = true) { super(id || Utils.GenerateDeterministicGuid(name)); this.name = name; - Server.UpdateField(this) + if (save) { + Server.UpdateField(this) + } + } + + UpdateFromServer(data: string) { + this.name = data; } TrySetValue(value: any): boolean { diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts index cf8a1ba8b..1585746df 100644 --- a/src/fields/ListField.ts +++ b/src/fields/ListField.ts @@ -1,10 +1,59 @@ -import { Field, FIELD_ID } from "./Field"; +import { Field, FIELD_ID, FieldValue, Opt } from "./Field"; import { BasicField } from "./BasicField"; import { Types } from "../server/Message"; +import { observe, action } from "mobx"; +import { Server } from "../client/Server"; +import { ServerUtils } from "../server/ServerUtil"; export class ListField extends BasicField { - constructor(data: T[] = [], id: FIELD_ID = undefined) { - super(data.slice(), id); + private _proxies: string[] = [] + constructor(data: T[] = [], id: FIELD_ID = undefined, save: boolean = true) { + super(data, save, id); + this.updateProxies(); + if (save) { + Server.UpdateField(this); + } + observe(this.Data, () => { + this.updateProxies() + Server.UpdateField(this); + }) + } + + private updateProxies() { + this._proxies = this.Data.map(field => field.Id); + } + + UpdateFromServer(fields: string[]) { + this._proxies = fields; + } + 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) { + Server.GetFields(this._proxies, action((fields: { [index: string]: Field }) => { + if (!this.arraysEqual(this._proxies, this.Data.map(field => field.Id))) { + + this.Data = this._proxies.map(id => fields[id] as T) + observe(this.Data, () => { + this.updateProxies() + Server.UpdateField(this); + }) + } + callback(this); + })) } ToScriptString(): string { @@ -15,11 +64,17 @@ export class ListField extends BasicField { return new ListField(this.Data); } - ToJson(): { type: Types, data: T[], _id: string } { + ToJson(): { type: Types, data: string[], _id: string } { return { type: Types.List, - data: this.Data, + data: this._proxies, _id: this.Id } } + + static FromJson(id: string, ids: string[]): ListField { + let list = new ListField([], id, false); + list._proxies = ids; + return list + } } \ No newline at end of file diff --git a/src/fields/NumberField.ts b/src/fields/NumberField.ts index 7fa9ec2e4..29e285201 100644 --- a/src/fields/NumberField.ts +++ b/src/fields/NumberField.ts @@ -3,8 +3,8 @@ import { Types } from "../server/Message"; import { FIELD_ID } from "./Field"; export class NumberField extends BasicField { - constructor(data: number = 0, id: FIELD_ID = undefined) { - super(data, id); + constructor(data: number = 0, id: FIELD_ID = undefined, save: boolean = true) { + super(data, save, id); } ToScriptString(): string { diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts index 3c6151009..9783107e3 100644 --- a/src/fields/RichTextField.ts +++ b/src/fields/RichTextField.ts @@ -3,8 +3,8 @@ import { Types } from "../server/Message"; import { FIELD_ID } from "./Field"; export class RichTextField extends BasicField { - constructor(data: string = "", id: FIELD_ID = undefined) { - super(data, id); + constructor(data: string = "", id: FIELD_ID = undefined, save: boolean = true) { + super(data, save, id); } ToScriptString(): string { diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts index f2b277298..efb3c035f 100644 --- a/src/fields/TextField.ts +++ b/src/fields/TextField.ts @@ -3,8 +3,8 @@ import { FIELD_ID } from "./Field"; import { Types } from "../server/Message"; export class TextField extends BasicField { - constructor(data: string = "", id: FIELD_ID = undefined) { - super(data, id); + constructor(data: string = "", id: FIELD_ID = undefined, save: boolean = true) { + super(data, save, id); } ToScriptString(): string { diff --git a/src/server/Message.ts b/src/server/Message.ts index 528ba0cd7..1ec6e25f3 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -121,6 +121,7 @@ export namespace MessageStore { export const AddDocument = new Message("Add Document"); export const SetField = new Message<{ _id: string, data: any, type: Types }>("Set Field") export const GetField = new Message("Get Field") + export const GetFields = new Message("Get Fields") export const GetDocument = new Message("Get Document"); export const DeleteAll = new Message("Delete All"); } \ No newline at end of file diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts index d1de71dbe..03b9751da 100644 --- a/src/server/ServerUtil.ts +++ b/src/server/ServerUtil.ts @@ -11,38 +11,36 @@ import { Types } from './Message'; import { Utils } from '../Utils'; export class ServerUtils { - public static FromJson(json: string): Field { - let obj = JSON.parse(JSON.stringify(json)) - console.log(obj) + public static FromJson(json: any): Field { + let obj = json let data: any = obj.data let id: string = obj._id let type: Types = obj.type - if (!(data && id && type != undefined)) { + if (!(data !== undefined && id && type !== undefined)) { console.log("how did you manage to get an object that doesn't have a data or an id?") return new TextField("Something to fill the space", Utils.GenerateGuid()); } switch (type) { case Types.Number: - return new NumberField(data, id) + return new NumberField(data, id, false) case Types.Text: - return new TextField(data, id) + return new TextField(data, id, false) case Types.RichText: - return new RichTextField(data, id) + return new RichTextField(data, id, false) case Types.Key: - return new Key(data, id) + return new Key(data, id, false) case Types.Image: - return new ImageField(data, id) + return new ImageField(data, id, false) case Types.List: - return new ListField(data, id) + return ListField.FromJson(id, data) case Types.Document: - let doc: Document = new Document(id) + let doc: Document = new Document(id, false) let fields: [string, string][] = data as [string, string][] fields.forEach(element => { doc._proxies.set(element[0], element[1]); }); - console.log(doc._proxies) return doc } return new TextField(data, id) diff --git a/src/server/database.ts b/src/server/database.ts index 282238327..51103520e 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -12,7 +12,9 @@ export class Database { public update(id: string, value: any) { this.MongoClient.connect(this.url, { bufferMaxEntries: 1 }, (err, db) => { let collection = db.db().collection('documents'); - collection.update({ _id: id }, { $set: value }); + collection.update({ _id: id }, { $set: value }, { + upsert: true + }); db.close(); }); } @@ -34,15 +36,12 @@ export class Database { public insert(kvpairs: any) { this.MongoClient.connect(this.url, { bufferMaxEntries: 1 }, (err, db) => { - // console.log(kvpairs) let collection = db.db().collection('documents'); collection.insertOne(kvpairs, (err: any, res: any) => { if (err) { // console.log(err) return } - // console.log(kvpairs) - // console.log("1 document inserted") }); db.close(); }); @@ -69,6 +68,24 @@ export class Database { }); } + public getDocuments(ids: string[], fn: (res: any) => void) { + var result: JSON; + this.MongoClient.connect(this.url, { + bufferMaxEntries: 1 + }, (err, db) => { + if (err) { + console.log(err) + return undefined + } + let collection = db.db().collection('documents'); + let cursor = collection.find({ _id: { "$in": ids } }) + cursor.toArray((err, docs) => { + fn(docs); + }) + db.close(); + }); + } + public print() { console.log("db says hi!") } diff --git a/src/server/index.ts b/src/server/index.ts index ef02ffbfc..812338080 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -14,6 +14,7 @@ import { Database } from './database'; import { ServerUtils } from './ServerUtil'; import { ObjectID } from 'mongodb'; import { Document } from '../fields/Document'; +import * as io from 'socket.io' const config = require('../../webpack.config') const compiler = webpack(config) const port = 1050; // default port to listen @@ -30,6 +31,11 @@ app.get("/hello", (req, res) => { res.send("

Hello

"); }) +app.get("/delete", (req, res) => { + deleteAll(); + res.redirect("/"); +}); + app.use(wdm(compiler, { publicPath: config.output.publicPath })) @@ -41,7 +47,7 @@ app.listen(port, () => { console.log(`server started at http://localhost:${port}`); }) -const server = require("socket.io")(); +const server = io(); interface Map { [key: string]: Client; } @@ -53,8 +59,9 @@ server.on("connection", function (socket: Socket) { Utils.Emit(socket, MessageStore.Foo, "handshooken") Utils.AddServerHandler(socket, MessageStore.Bar, barReceived) - Utils.AddServerHandler(socket, MessageStore.SetField, setField) + Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)) Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField) + Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields) Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteAll) }) @@ -82,15 +89,13 @@ function getField([id, callback]: [string, (result: any) => void]) { }) } -function setField(newValue: Transferable) { - Database.Instance.getDocument(newValue._id, (res: any) => { - if (res) { - Database.Instance.update(newValue._id, newValue) - } - else { - Database.Instance.insert(newValue) - } - }) +function getFields([ids, callback]: [string[], (result: any) => void]) { + Database.Instance.getDocuments(ids, callback); +} + +function setField(socket: Socket, newValue: Transferable) { + Database.Instance.update(newValue._id, newValue) + socket.broadcast.emit(MessageStore.SetField.Message, newValue) } server.listen(serverPort); -- cgit v1.2.3-70-g09d2 From 70a8b4ab8075af4d06efb04c083b3bf11636373e Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 16 Feb 2019 19:12:11 -0500 Subject: Fixed issues with Document updates and changed how FieldView layout string works --- package-lock.json | 188 ++++++++++++++++++++- package.json | 1 + src/Utils.ts | 12 ++ .../views/collections/CollectionDockingView.tsx | 26 ++- .../views/collections/CollectionFreeFormView.tsx | 42 +++-- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionViewBase.tsx | 25 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 10 +- src/client/views/nodes/DocumentView.tsx | 56 +++++- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/fields/Document.ts | 13 +- src/server/ServerUtil.ts | 2 +- 14 files changed, 309 insertions(+), 76 deletions(-) (limited to 'src/server/ServerUtil.ts') diff --git a/package-lock.json b/package-lock.json index 2f0f8d4d0..0140e192f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -936,6 +936,11 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -1109,6 +1114,11 @@ } } }, + "base62": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/base62/-/base62-1.2.8.tgz", + "integrity": "sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA==" + }, "base64-arraybuffer": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", @@ -1750,8 +1760,7 @@ "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" }, "commondir": { "version": "1.0.1", @@ -1759,6 +1768,36 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "commoner": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", + "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=", + "requires": { + "commander": "^2.5.0", + "detective": "^4.3.1", + "glob": "^5.0.15", + "graceful-fs": "^4.1.2", + "iconv-lite": "^0.4.5", + "mkdirp": "^0.5.0", + "private": "^0.1.6", + "q": "^1.1.2", + "recast": "^0.11.17" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -2286,6 +2325,11 @@ } } }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, "del": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", @@ -2371,6 +2415,15 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, + "detective": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "requires": { + "acorn": "^5.2.1", + "defined": "^1.0.0" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -2605,6 +2658,15 @@ "tapable": "^1.0.0" } }, + "envify": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/envify/-/envify-3.4.1.tgz", + "integrity": "sha1-1xIjKejfFoi6dxsSUBkXyc5cvOg=", + "requires": { + "jstransform": "^11.0.3", + "through": "~2.3.4" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -2679,6 +2741,11 @@ "estraverse": "^4.1.1" } }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz", + "integrity": "sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE=" + }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", @@ -3005,6 +3072,25 @@ "websocket-driver": ">=0.5.1" } }, + "fbjs": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.6.1.tgz", + "integrity": "sha1-lja3cF9bqWhNRLcveDISVK/IYPc=", + "requires": { + "core-js": "^1.0.0", + "loose-envify": "^1.0.0", + "promise": "^7.0.3", + "ua-parser-js": "^0.7.9", + "whatwg-fetch": "^0.9.0" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -4202,6 +4288,11 @@ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" }, + "immutable": { + "version": "4.0.0-rc.12", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz", + "integrity": "sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==" + }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -4678,6 +4769,11 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stringify-pretty-compact": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-1.2.0.tgz", + "integrity": "sha512-/11Pj1OyX814QMKO7K8l85SHPTr/KsFxHp8GE2zVa0BtJgGimDjXHfM3FhC7keQdWDea7+nXf+f1de7ATZcZkQ==" + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -4731,6 +4827,25 @@ "verror": "1.10.0" } }, + "jstransform": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.3.tgz", + "integrity": "sha1-CaeJk+CuTU70SH9hVakfYZDLQiM=", + "requires": { + "base62": "^1.1.0", + "commoner": "^0.10.1", + "esprima-fb": "^15001.1.0-dev-harmony-fb", + "object-assign": "^2.0.0", + "source-map": "^0.4.2" + }, + "dependencies": { + "object-assign": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + } + } + }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -4740,6 +4855,27 @@ "promise": "^7.0.1" } }, + "jsx-to-string": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsx-to-string/-/jsx-to-string-1.4.0.tgz", + "integrity": "sha1-Ztw013PaufQP6ZPP+ZQOXaZVtwU=", + "requires": { + "immutable": "^4.0.0-rc.9", + "json-stringify-pretty-compact": "^1.0.1", + "react": "^0.14.0" + }, + "dependencies": { + "react": { + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/react/-/react-0.14.9.tgz", + "integrity": "sha1-kRCmSXxJ1EuhwO3TF67CnC4NkdE=", + "requires": { + "envify": "^3.0.0", + "fbjs": "^0.6.1" + } + } + } + }, "jwa": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.2.0.tgz", @@ -9752,6 +9888,11 @@ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -10042,6 +10183,11 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -10267,6 +10413,29 @@ "readable-stream": "^2.0.2" } }, + "recast": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "requires": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -11617,6 +11786,11 @@ "native-promise-only": "^0.8.1" } }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -11820,6 +11994,11 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.1.tgz", "integrity": "sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA==" }, + "ua-parser-js": { + "version": "0.7.19", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", + "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", @@ -12862,6 +13041,11 @@ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, + "whatwg-fetch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz", + "integrity": "sha1-DjaExsuZlbQ+/J3wPkw2XZX9nMA=" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 3a245cfd0..94bf5a217 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "flexlayout-react": "^0.3.3", "golden-layout": "^1.5.9", "jsonwebtoken": "^8.4.0", + "jsx-to-string": "^1.4.0", "lodash": "^4.17.11", "mobx": "^5.9.0", "mobx-react": "^5.3.5", diff --git a/src/Utils.ts b/src/Utils.ts index ce4f7ac3e..c9c006aa8 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -31,6 +31,18 @@ export class Utils { return { scale, translateX, translateY }; } + public static CopyText(text: string) { + var textArea = document.createElement("textarea"); + textArea.value = text; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { document.execCommand('copy'); } catch (err) { } + + document.body.removeChild(textArea); + } + public static Emit(socket: Socket | SocketIOClient.Socket, message: Message, args: T) { socket.emit(message.Message, args); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index b8e040388..d9e261f55 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -2,7 +2,7 @@ import { observer } from "mobx-react"; import { KeyStore } from "../../../fields/KeyStore"; import React = require("react"); import FlexLayout from "flexlayout-react"; -import { action, observable, computed } from "mobx"; +import { action, computed } from "mobx"; import { Document } from "../../../fields/Document"; import { DocumentView } from "../nodes/DocumentView"; import { ListField } from "../../../fields/ListField"; @@ -13,17 +13,18 @@ import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import * as GoldenLayout from "golden-layout"; import * as ReactDOM from 'react-dom'; import { DragManager } from "../../util/DragManager"; -import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { FieldView } from "../nodes/FieldView"; @observer export class CollectionDockingView extends CollectionViewBase { private static UseGoldenLayout = true; - public static LayoutString() { return CollectionViewBase.LayoutString("CollectionDockingView"); } + public static LayoutString() { return FieldView.LayoutString(CollectionDockingView) } private _containerRef = React.createRef(); @computed private get modelForFlexLayout() { - const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; + const { fieldKey: fieldKey, doc: Document } = this.props; const value: Document[] = Document.GetData(fieldKey, ListField, []); var docs = value.map(doc => { return { type: 'tabset', weight: 50, selected: 0, children: [{ type: "tab", name: doc.Title, component: doc.Id }] }; @@ -39,7 +40,7 @@ export class CollectionDockingView extends CollectionViewBase { } @computed private get modelForGoldenLayout(): any { - const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; + const { fieldKey: fieldKey, doc: Document } = this.props; const value: Document[] = Document.GetData(fieldKey, ListField, []); var docs = value.map(doc => { return { type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc } }; @@ -50,9 +51,6 @@ export class CollectionDockingView extends CollectionViewBase { }, content: [{ type: 'row', content: docs }] }); } - constructor(props: CollectionViewProps) { - super(props); - } componentDidMount: () => void = () => { if (this._containerRef.current && CollectionDockingView.UseGoldenLayout) { @@ -67,8 +65,8 @@ export class CollectionDockingView extends CollectionViewBase { @action - onResize = (event: any) => { - var cur = this.props.ContainingDocumentView!.MainContent.current; + onResize = () => { + var cur = this.props.DocumentViewForField!.MainContent.current; // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed CollectionDockingView.myLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); @@ -91,7 +89,7 @@ export class CollectionDockingView extends CollectionViewBase { if (component === "button") { return ; } - const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; + const { fieldKey: fieldKey, doc: Document } = this.props; const value: Document[] = Document.GetData(fieldKey, ListField, []); for (var i: number = 0; i < value.length; i++) { if (value[i].Id === component) { @@ -195,7 +193,6 @@ export class CollectionDockingView extends CollectionViewBase { goldenLayoutFactory() { CollectionDockingView.myLayout = this.modelForGoldenLayout; - var layout = CollectionDockingView.myLayout; CollectionDockingView.myLayout.on('tabCreated', function (tab: any) { if (CollectionDockingView._dragDiv) { CollectionDockingView._dragDiv.removeChild(CollectionDockingView._dragElement); @@ -251,10 +248,9 @@ export class CollectionDockingView extends CollectionViewBase { render() { - const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; - const value: Document[] = Document.GetData(fieldKey, ListField, []); + const { fieldKey: fieldKey, doc: Document } = this.props; // bcz: not sure why, but I need these to force the flexlayout to update when the collection size changes. - var s = this.props.ContainingDocumentView != undefined ? this.props.ContainingDocumentView!.ScalingToScreenSpace : 1; + var s = this.props.DocumentViewForField != undefined ? this.props.DocumentViewForField!.ScalingToScreenSpace : 1; var w = Document.GetData(KeyStore.Width, NumberField, Number(0)) / s; var h = Document.GetData(KeyStore.Height, NumberField, Number(0)) / s; diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index e3a43aa5b..e6b1d103d 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -5,7 +5,7 @@ import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocum import { DragManager } from "../../util/DragManager"; import "./CollectionFreeFormView.scss"; import { Utils } from "../../../Utils"; -import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; import { SelectionManager } from "../../util/SelectionManager"; import { KeyStore } from "../../../fields/KeyStore"; import { Document } from "../../../fields/Document"; @@ -13,19 +13,17 @@ import { ListField } from "../../../fields/ListField"; import { NumberField } from "../../../fields/NumberField"; import { Documents } from "../../documents/Documents"; import { FieldWaiting } from "../../../fields/Field"; +import { FakeJsxArgs } from "../nodes/DocumentView"; +import { FieldView } from "../nodes/FieldView"; @observer export class CollectionFreeFormView extends CollectionViewBase { - public static LayoutString() { return CollectionViewBase.LayoutString("CollectionFreeFormView"); } + public static LayoutString() { return FieldView.LayoutString(CollectionFreeFormView); } private _canvasRef = React.createRef(); private _nodeContainerRef = React.createRef(); private _lastX: number = 0; private _lastY: number = 0; - constructor(props: CollectionViewProps) { - super(props); - } - @action drop = (e: Event, de: DragManager.DropEvent) => { const doc = de.data["document"]; @@ -38,7 +36,7 @@ export class CollectionFreeFormView extends CollectionViewBase { const xOffset = de.data["xOffset"] as number || 0; const yOffset = de.data["yOffset"] as number || 0; const { scale, translateX, translateY } = Utils.GetScreenTransform(this._canvasRef.current!); - let sscale = this.props.ContainingDocumentView!.props.Document.GetData(KeyStore.Scale, NumberField, Number(1)) + let sscale = this.props.DocumentViewForField!.props.Document.GetData(KeyStore.Scale, NumberField, Number(1)) const screenX = de.x - xOffset; const screenY = de.y - yOffset; const docX = (screenX - translateX) / sscale / scale; @@ -91,11 +89,11 @@ export class CollectionFreeFormView extends CollectionViewBase { if (!e.cancelBubble && this.active) { e.preventDefault(); e.stopPropagation(); - let currScale: number = this.props.ContainingDocumentView!.ScalingToScreenSpace; - let x = this.props.DocumentForCollection.GetData(KeyStore.PanX, NumberField, Number(0)); - let y = this.props.DocumentForCollection.GetData(KeyStore.PanY, NumberField, Number(0)); - this.props.DocumentForCollection.SetData(KeyStore.PanX, x + (e.pageX - this._lastX) / currScale, NumberField); - this.props.DocumentForCollection.SetData(KeyStore.PanY, y + (e.pageY - this._lastY) / currScale, NumberField); + let currScale: number = this.props.DocumentViewForField!.ScalingToScreenSpace; + let x = this.props.doc.GetData(KeyStore.PanX, NumberField, Number(0)); + let y = this.props.doc.GetData(KeyStore.PanY, NumberField, Number(0)); + this.props.doc.SetData(KeyStore.PanX, x + (e.pageX - this._lastX) / currScale, NumberField); + this.props.doc.SetData(KeyStore.PanY, y + (e.pageY - this._lastY) / currScale, NumberField); } this._lastX = e.pageX; this._lastY = e.pageY; @@ -105,7 +103,7 @@ export class CollectionFreeFormView extends CollectionViewBase { onPointerWheel = (e: React.WheelEvent): void => { e.stopPropagation(); - let { LocalX, Ss, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.ContainingDocumentView!.TransformToLocalPoint(e.pageX, e.pageY); + let { LocalX, Ss, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.DocumentViewForField!.TransformToLocalPoint(e.pageX, e.pageY); var deltaScale = (1 - (e.deltaY / 1000)) * Ss; @@ -115,9 +113,9 @@ export class CollectionFreeFormView extends CollectionViewBase { let dx = ContainerX - newContainerX; let dy = ContainerY - newContainerY; - this.props.DocumentForCollection.Set(KeyStore.Scale, new NumberField(deltaScale)); - this.props.DocumentForCollection.SetData(KeyStore.PanX, Panxx + dx, NumberField); - this.props.DocumentForCollection.SetData(KeyStore.PanY, Panyy + dy, NumberField); + this.props.doc.Set(KeyStore.Scale, new NumberField(deltaScale)); + this.props.doc.SetData(KeyStore.PanX, Panxx + dx, NumberField); + this.props.doc.SetData(KeyStore.PanY, Panyy + dy, NumberField); } @action @@ -127,8 +125,8 @@ export class CollectionFreeFormView extends CollectionViewBase { let fReader = new FileReader() let file = e.dataTransfer.items[0].getAsFile(); let that = this; - const panx: number = this.props.DocumentForCollection.GetData(KeyStore.PanX, NumberField, Number(0)); - const pany: number = this.props.DocumentForCollection.GetData(KeyStore.PanY, NumberField, Number(0)); + const panx: number = this.props.doc.GetData(KeyStore.PanX, NumberField, Number(0)); + const pany: number = this.props.doc.GetData(KeyStore.PanY, NumberField, Number(0)); let x = e.pageX - panx let y = e.pageY - pany @@ -138,11 +136,11 @@ export class CollectionFreeFormView extends CollectionViewBase { let doc = Documents.ImageDocument(url, { x: x, y: y }) - let docs = that.props.DocumentForCollection.GetT(KeyStore.Data, ListField); + let docs = that.props.doc.GetT(KeyStore.Data, ListField); if (docs != FieldWaiting) { if (!docs) { docs = new ListField(); - that.props.DocumentForCollection.Set(KeyStore.Data, docs) + that.props.doc.Set(KeyStore.Data, docs) } docs.Data.push(doc); } @@ -159,7 +157,7 @@ export class CollectionFreeFormView extends CollectionViewBase { @action bringToFront(doc: CollectionFreeFormDocumentView) { - const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; + const { fieldKey: fieldKey, doc: Document } = this.props; const value: Document[] = Document.GetList(fieldKey, []); var topmost = value.reduce((topmost, d) => Math.max(d.GetNumber(KeyStore.ZIndex, 0), topmost), -1000); @@ -176,7 +174,7 @@ export class CollectionFreeFormView extends CollectionViewBase { } render() { - const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; + const { fieldKey: fieldKey, doc: Document } = this.props; // const value: Document[] = Document.GetList(fieldKey, []); const lvalue = Document.GetT>(fieldKey, ListField); if (!lvalue || lvalue === "") { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 8a1e21847..9f32ccb72 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -17,7 +17,7 @@ import { Field } from "../../../fields/Field"; @observer export class CollectionSchemaView extends CollectionViewBase { - public static LayoutString() { return CollectionViewBase.LayoutString("CollectionSchemaView"); } + public static LayoutString() { return FieldView.LayoutString(CollectionSchemaView); } @observable selectedIndex = 0; @@ -97,7 +97,7 @@ export class CollectionSchemaView extends CollectionViewBase { } render() { - const { DocumentForCollection: Document, CollectionFieldKey: fieldKey } = this.props; + const { doc: Document, fieldKey: fieldKey } = this.props; const children = Document.GetList(fieldKey, []); const columns = Document.GetList(KS.ColumnsKey, [KS.Title, KS.Data, KS.Author]) diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index 8bb10b743..4fcbb1699 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -10,42 +10,33 @@ import React = require("react"); import { DocumentView } from "../nodes/DocumentView"; import { CollectionDockingView } from "./CollectionDockingView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; - - -export interface CollectionViewProps { - CollectionFieldKey: Key; - DocumentForCollection: Document; - ContainingDocumentView: Opt; -} +import { FieldViewProps } from "../nodes/FieldView"; export const COLLECTION_BORDER_WIDTH = 2; @observer -export class CollectionViewBase extends React.Component { +export class CollectionViewBase extends React.Component { - public static LayoutString(collectionType: string) { - return `<${collectionType} DocumentForCollection={Document} CollectionFieldKey={DataKey} ContainingDocumentView={DocumentView}/>`; - } @computed public get active(): boolean { - var isSelected = (this.props.ContainingDocumentView instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.ContainingDocumentView)); + var isSelected = (this.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.DocumentViewForField)); var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); - var topMost = this.props.ContainingDocumentView != undefined && ( - this.props.ContainingDocumentView.props.ContainingCollectionView == undefined || - this.props.ContainingDocumentView.props.ContainingCollectionView instanceof CollectionDockingView); + var topMost = this.props.DocumentViewForField != undefined && ( + this.props.DocumentViewForField.props.ContainingCollectionView == undefined || + this.props.DocumentViewForField.props.ContainingCollectionView instanceof CollectionDockingView); return isSelected || childSelected || topMost; } @action addDocument = (doc: Document): void => { //TODO This won't create the field if it doesn't already exist - const value = this.props.DocumentForCollection.GetData(this.props.CollectionFieldKey, ListField, new Array()) + const value = this.props.doc.GetData(this.props.fieldKey, ListField, new Array()) value.push(doc); } @action removeDocument = (doc: Document): void => { //TODO This won't create the field if it doesn't already exist - const value = this.props.DocumentForCollection.GetData(this.props.CollectionFieldKey, ListField, new Array()) + const value = this.props.doc.GetData(this.props.fieldKey, ListField, new Array()) if (value.indexOf(doc) !== -1) { value.splice(value.indexOf(doc), 1) diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9e6407768..bfd50da81 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -10,6 +10,7 @@ import { ContextMenu } from "../ContextMenu"; import "./NodeView.scss"; import React = require("react"); import { DocumentView, DocumentViewProps } from "./DocumentView"; +import { Utils } from "../../../Utils"; @observer @@ -188,7 +189,6 @@ export class CollectionFreeFormDocumentView extends DocumentView { if (this.topMost) { ContextMenu.Instance.clearItems() ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) } else { // DocumentViews should stop propogation of this event @@ -198,9 +198,15 @@ export class CollectionFreeFormDocumentView extends DocumentView { ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) ContextMenu.Instance.addItem({ description: "Open Right", event: this.openRight }) ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) SelectionManager.SelectDoc(this, e.ctrlKey); } + + ContextMenu.Instance.addItem({ + description: "Copy ID", event: () => { + Utils.CopyText(this.props.Document.Id) + } + }) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) } render() { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b219f2279..3767d28c6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,7 +1,7 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; -import { Opt, FieldWaiting } from "../../../fields/Field"; +import { Opt, FieldWaiting, Field } from "../../../fields/Field"; import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; @@ -23,6 +23,48 @@ export interface DocumentViewProps { DocumentView: Opt // needed only to set ContainingDocumentView on CollectionViewProps when invoked from JsxParser -- is there a better way? ContainingCollectionView: Opt; } +export interface JsxArgs extends DocumentViewProps { + Keys: { [name: string]: Key } + Fields: { [name: string]: Field } +} + +/* +This function is pretty much a hack that lets us fill out the fields in JsxArgs with something that +jsx-to-string can recover the jsx from +Example usage of this function: + public static LayoutString() { + let args = FakeJsxArgs(["Data"]); + return jsxToString( + , + { useFunctionCode: true, functionNameOnly: true } + ) + } +*/ +export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs { + let Keys: { [name: string]: any } = {} + let Fields: { [name: string]: any } = {} + for (const key of keys) { + let fn = () => { } + Object.defineProperty(fn, "name", { value: key + "Key" }) + Keys[key] = fn; + } + for (const field of fields) { + let fn = () => { } + Object.defineProperty(fn, "name", { value: field }) + Fields[field] = fn; + } + let args: JsxArgs = { + Document: function Document() { }, + DocumentView: function DocumentView() { }, + Keys, + Fields + } as any; + return args; +} + @observer export class DocumentView extends React.Component { @@ -51,9 +93,9 @@ export class DocumentView extends React.Component { @computed public get ScalingToScreenSpace(): number { if (this.props.ContainingCollectionView != undefined && - this.props.ContainingCollectionView.props.ContainingDocumentView != undefined) { - let ss = this.props.ContainingCollectionView.props.DocumentForCollection.GetData(KeyStore.Scale, NumberField, Number(1)); - return this.props.ContainingCollectionView.props.ContainingDocumentView.ScalingToScreenSpace * ss; + this.props.ContainingCollectionView.props.DocumentViewForField != undefined) { + let ss = this.props.ContainingCollectionView.props.doc.GetData(KeyStore.Scale, NumberField, Number(1)); + return this.props.ContainingCollectionView.props.DocumentViewForField.ScalingToScreenSpace * ss; } return 1; } @@ -65,8 +107,8 @@ export class DocumentView extends React.Component { // if this collection view is nested within another collection view, then // first transform the screen point into the parent collection's coordinate space. let { LocalX: parentX, LocalY: parentY } = this.props.ContainingCollectionView != undefined && - this.props.ContainingCollectionView.props.ContainingDocumentView != undefined ? - this.props.ContainingCollectionView.props.ContainingDocumentView.TransformToLocalPoint(screenX, screenY) : + this.props.ContainingCollectionView.props.DocumentViewForField != undefined ? + this.props.ContainingCollectionView.props.DocumentViewForField.TransformToLocalPoint(screenX, screenY) : { LocalX: screenX, LocalY: screenY }; let ContainerX: number = parentX - COLLECTION_BORDER_WIDTH; let ContainerY: number = parentY - COLLECTION_BORDER_WIDTH; @@ -114,7 +156,7 @@ export class DocumentView extends React.Component { // if this collection view is nested within another collection view, then // first transform the local point into the parent collection's coordinate space. - let containingDocView = this.props.ContainingCollectionView != undefined ? this.props.ContainingCollectionView.props.ContainingDocumentView : undefined; + let containingDocView = this.props.ContainingCollectionView != undefined ? this.props.ContainingCollectionView.props.DocumentViewForField : undefined; if (containingDocView != undefined) { let ss = containingDocView.props.Document.GetData(KeyStore.Scale, NumberField, Number(1)); let panxx = containingDocView.props.Document.GetData(KeyStore.PanX, NumberField, Number(0)) + COLLECTION_BORDER_WIDTH * ss; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 12371eb2e..fae124528 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -25,7 +25,7 @@ export interface FieldViewProps { @observer export class FieldView extends React.Component { - public static LayoutString(fieldType: string) { return `<${fieldType} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} />`; } + public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} />`; } @computed get field(): FieldValue { const { doc, fieldKey } = this.props; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 8bc4c902c..39d7bf4f0 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -34,7 +34,7 @@ import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView @observer export class FormattedTextBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString("FormattedTextBox"); } + public static LayoutString() { return FieldView.LayoutString(FormattedTextBox) } private _ref: React.RefObject; private _editorView: Opt; private _reactionDisposer: Opt; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index ab20f140c..013b8b7d3 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -14,7 +14,7 @@ import { observable, action } from 'mobx'; @observer export class ImageBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString("ImageBox"); } + public static LayoutString() { return FieldView.LayoutString(ImageBox) } private _ref: React.RefObject; private _downX: number = 0; private _downY: number = 0; diff --git a/src/fields/Document.ts b/src/fields/Document.ts index fce316b17..3067621be 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -9,7 +9,7 @@ import { Server } from "../client/Server"; import { Types } from "../server/Message"; export class Document extends Field { - public fields: ObservableMap }> = new ObservableMap(); + public fields: ObservableMap = new ObservableMap(); public _proxies: ObservableMap = new ObservableMap(); constructor(id?: string, save: boolean = true) { @@ -45,17 +45,20 @@ export class Document extends Field { } else { let doc: FieldValue = this; while (doc && doc != FieldWaiting && field != FieldWaiting) { - if (!doc.fields.has(key.Id)) { - if (doc._proxies.has(key.Id)) { + let curField = doc.fields.get(key.Id); + let curProxy = doc._proxies.get(key.Id); + if (!curField || (curProxy && curField.field.Id !== curProxy)) { + if (curProxy) { field = Server.GetDocumentField(doc, key); break; } if ((doc.fields.has(KeyStore.Prototype.Id) || doc._proxies.has(KeyStore.Prototype.Id))) { doc = doc.GetPrototype(); - } else + } else { break; + } } else { - field = doc.fields.get(key.Id)!.field; + field = curField.field; break; } } diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts index 03b9751da..46c409ec4 100644 --- a/src/server/ServerUtil.ts +++ b/src/server/ServerUtil.ts @@ -32,7 +32,7 @@ export class ServerUtils { case Types.Key: return new Key(data, id, false) case Types.Image: - return new ImageField(data, id, false) + return new ImageField(new URL(data), id, false) case Types.List: return ListField.FromJson(id, data) case Types.Document: -- cgit v1.2.3-70-g09d2 From 9663623dbe9f54bd3f233c8de7cb9f112f17a7cc Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 23 Feb 2019 04:49:48 -0500 Subject: Added web clippings on drag and drop --- src/client/documents/Documents.ts | 24 +++++++++++++++++++++ .../views/collections/CollectionFreeFormView.tsx | 3 ++- .../views/collections/CollectionViewBase.tsx | 9 ++++++++ src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/FieldView.tsx | 4 ++++ src/client/views/nodes/WebView.tsx | 22 +++++++++++++++++++ src/fields/HtmlField.ts | 25 ++++++++++++++++++++++ src/fields/KeyStore.ts | 1 + src/fields/TextField.ts | 2 +- src/server/Message.ts | 2 +- src/server/ServerUtil.ts | 6 +++++- 11 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 src/client/views/nodes/WebView.tsx create mode 100644 src/fields/HtmlField.ts (limited to 'src/server/ServerUtil.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6ec5aa711..d8e1db0b0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -9,6 +9,8 @@ import { ImageField } from "../../fields/ImageField"; import { ImageBox } from "../views/nodes/ImageBox"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { FieldView } from "../views/nodes/FieldView"; +import { HtmlField } from "../../fields/HtmlField"; +import { WebView } from "../views/nodes/WebView"; export interface DocumentOptions { x?: number; @@ -79,6 +81,28 @@ export namespace Documents { return doc; } + let htmlProto: Document; + const htmlProtoId = "htmlProto"; + function GetHtmlPrototype(): Document { + if (!htmlProto) { + htmlProto = new Document(htmlProtoId); + htmlProto.Set(KeyStore.X, new NumberField(0)); + htmlProto.Set(KeyStore.Y, new NumberField(0)); + htmlProto.Set(KeyStore.Width, new NumberField(300)); + htmlProto.Set(KeyStore.Height, new NumberField(150)); + htmlProto.Set(KeyStore.Layout, new TextField(WebView.LayoutString())); + htmlProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); + } + return htmlProto; + } + + export function HtmlDocument(html: string, options: DocumentOptions = {}): Document { + let doc = GetHtmlPrototype().MakeDelegate(); + setupOptions(doc, options); + doc.Set(KeyStore.Data, new HtmlField(html)); + return doc; + } + let imageProto: Document; const imageProtoId = "imageProto"; function GetImagePrototype(): Document { diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 4799eda97..b031c35a7 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -13,6 +13,7 @@ import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionView } from "../collections/CollectionView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; +import { WebView } from "../nodes/WebView"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; import "./CollectionFreeFormView.scss"; @@ -185,7 +186,7 @@ export class CollectionFreeFormView extends CollectionViewBase { get backgroundView() { return !this.backgroundLayout ? (null) : ( e.preventDefault() let that = this; + let html = e.dataTransfer.getData("text/html"); + let text = e.dataTransfer.getData("text/plain"); + if (html) { + let htmlDoc = Documents.HtmlDocument(html, { ...options }); + htmlDoc.SetText(KeyStore.DocumentText, text); + this.props.addDocument(htmlDoc); + return; + } + for (let i = 0; i < e.dataTransfer.items.length; i++) { let item = e.dataTransfer.items[i]; if (item.kind === "string" && item.type.indexOf("uri") != -1) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a9e211431..ad1328e5d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -12,6 +12,7 @@ import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionView, CollectionViewType } from "../collections/CollectionView"; +import { WebView } from "./WebView"; import { ContextMenu } from "../ContextMenu"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; @@ -196,7 +197,7 @@ export class DocumentView extends React.Component { @computed get mainContent() { return { } else if (field instanceof NumberField) { return

{field.Data}

+ } else if (field instanceof HtmlField) { + return } else if (field != FieldWaiting) { return

{field.GetValue}

} else diff --git a/src/client/views/nodes/WebView.tsx b/src/client/views/nodes/WebView.tsx new file mode 100644 index 000000000..717aa8bf5 --- /dev/null +++ b/src/client/views/nodes/WebView.tsx @@ -0,0 +1,22 @@ +import { FieldViewProps, FieldView } from "./FieldView"; +import { computed } from "mobx"; +import { observer } from "mobx-react"; +import { KeyStore } from "../../../fields/KeyStore"; +import React = require('react') +import { TextField } from "../../../fields/TextField"; +import { HtmlField } from "../../../fields/HtmlField"; +import { RichTextField } from "../../../fields/RichTextField"; + +@observer +export class WebView extends React.Component { + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(WebView, fieldStr) } + + @computed + get html(): string { + return this.props.doc.GetData(KeyStore.Data, HtmlField, "" as string); + } + + render() { + return + } +} \ No newline at end of file diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts new file mode 100644 index 000000000..a07326095 --- /dev/null +++ b/src/fields/HtmlField.ts @@ -0,0 +1,25 @@ +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(): { _id: string; type: Types; data: any; } { + return { + type: Types.Html, + data: this.Data, + _id: this.Id, + } + } +} \ No newline at end of file diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 42e3f6b58..290fa2be7 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -24,4 +24,5 @@ export namespace KeyStore { export const ColumnsKey = new Key("SchemaColumns"); export const Caption = new Key("Caption"); export const ActiveFrame = new Key("ActiveFrame"); + export const DocumentText = new Key("DocumentText"); } diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts index ad96ab6d9..71d8ea310 100644 --- a/src/fields/TextField.ts +++ b/src/fields/TextField.ts @@ -22,4 +22,4 @@ export class TextField extends BasicField { _id: this.Id } } -} +} \ No newline at end of file diff --git a/src/server/Message.ts b/src/server/Message.ts index 7f3190f7f..80fc9a80d 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -45,7 +45,7 @@ export class GetFieldArgs { } export enum Types { - Number, List, Key, Image, Document, Text, RichText, DocumentReference + Number, List, Key, Image, Document, Text, RichText, DocumentReference, Html } export class DocumentTransfer implements Transferable { diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts index 46c409ec4..08e72fdae 100644 --- a/src/server/ServerUtil.ts +++ b/src/server/ServerUtil.ts @@ -9,6 +9,7 @@ import { Document } from './../fields/Document'; import { Server } from './../client/Server'; import { Types } from './Message'; import { Utils } from '../Utils'; +import { HtmlField } from '../fields/HtmlField'; export class ServerUtils { public static FromJson(json: any): Field { @@ -27,6 +28,8 @@ export class ServerUtils { return new NumberField(data, id, false) case Types.Text: return new TextField(data, id, false) + case Types.Html: + return new HtmlField(data, id, false) case Types.RichText: return new RichTextField(data, id, false) case Types.Key: @@ -42,7 +45,8 @@ export class ServerUtils { doc._proxies.set(element[0], element[1]); }); return doc + default: + throw Error("Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here"); } - return new TextField(data, id) } } \ No newline at end of file -- cgit v1.2.3-70-g09d2