aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/DocServer.ts72
-rw-r--r--src/client/util/SerializationHelper.ts73
2 files changed, 135 insertions, 10 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
new file mode 100644
index 000000000..9a3e122e8
--- /dev/null
+++ b/src/client/DocServer.ts
@@ -0,0 +1,72 @@
+import * as OpenSocket from 'socket.io-client';
+import { MessageStore, Types } from "./../server/Message";
+import { Opt, FieldWaiting, RefField, HandleUpdate } from '../fields/NewDoc';
+import { Utils } from '../Utils';
+import { SerializationHelper } from './util/SerializationHelper';
+
+export namespace DocServer {
+ const _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
+ const _socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`);
+ const GUID: string = Utils.GenerateGuid();
+
+ export async function GetRefField(id: string): Promise<Opt<RefField>> {
+ let cached = _cache[id];
+ if (cached === undefined) {
+ const prom = Utils.EmitCallback(_socket, MessageStore.GetRefField, id).then(fieldJson => {
+ const field = fieldJson === undefined ? fieldJson : SerializationHelper.Deserialize(fieldJson);
+ if (field) {
+ _cache[id] = field;
+ } else {
+ delete _cache[id];
+ }
+ return field;
+ });
+ _cache[id] = prom;
+ return prom;
+ } else if (cached instanceof Promise) {
+ return cached;
+ } else {
+ return cached;
+ }
+ }
+
+ export function UpdateField(id: string, diff: any) {
+ Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
+ }
+
+ export function CreateField(initialState: any) {
+ if (!("id" in initialState)) {
+ throw new Error("Can't create a field on the server without an id");
+ }
+ Utils.Emit(_socket, MessageStore.CreateField, initialState);
+ }
+
+ function respondToUpdate(diff: any) {
+ const id = diff.id;
+ if (id === undefined) {
+ return;
+ }
+ const field = _cache[id];
+ const update = (f: Opt<RefField>) => {
+ if (f === undefined) {
+ return;
+ }
+ const handler = f[HandleUpdate];
+ if (handler) {
+ handler(diff);
+ }
+ };
+ if (field instanceof Promise) {
+ field.then(update);
+ } else {
+ update(field);
+ }
+ }
+
+ function connected(message: string) {
+ _socket.emit(MessageStore.Bar.Message, GUID);
+ }
+
+ Utils.AddServerHandler(_socket, MessageStore.Foo, connected);
+ Utils.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate);
+} \ No newline at end of file
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index 656101c95..7273c3fe4 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,9 +1,13 @@
-import { PropSchema, serialize, deserialize, custom } from "serializr";
+import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr";
import { Field } from "../../fields/NewDoc";
-export class SerializationHelper {
+export namespace SerializationHelper {
+ let serializing: number = 0;
+ export function IsSerializing() {
+ return serializing > 0;
+ }
- public static Serialize(obj: Field): any {
+ export function Serialize(obj: Field): any {
if (!obj) {
return null;
}
@@ -12,16 +16,18 @@ export class SerializationHelper {
return obj;
}
+ serializing += 1;
if (!(obj.constructor.name in reverseMap)) {
throw Error(`type '${obj.constructor.name}' not registered. Make sure you register it using a @Deserializable decorator`);
}
const json = serialize(obj);
json.__type = reverseMap[obj.constructor.name];
+ serializing -= 1;
return json;
}
- public static Deserialize(obj: any): any {
+ export function Deserialize(obj: any): any {
if (!obj) {
return null;
}
@@ -30,6 +36,7 @@ export class SerializationHelper {
return obj;
}
+ serializing += 1;
if (!obj.__type) {
throw Error("No property 'type' found in JSON.");
}
@@ -38,32 +45,78 @@ export class SerializationHelper {
throw Error(`type '${obj.__type}' not registered. Make sure you register it using a @Deserializable decorator`);
}
- return deserialize(serializationTypes[obj.__type], obj);
+ const value = deserialize(serializationTypes[obj.__type], obj);
+ serializing -= 1;
+ return value;
}
}
let serializationTypes: { [name: string]: any } = {};
let reverseMap: { [ctor: string]: string } = {};
-export function Deserializable(name: string): Function;
+export interface DeserializableOpts {
+ (constructor: Function): void;
+ withFields(fields: string[]): Function;
+}
+
+export function Deserializable(name: string): DeserializableOpts;
export function Deserializable(constructor: Function): void;
-export function Deserializable(constructor: Function | string): Function | void {
+export function Deserializable(constructor: Function | string): DeserializableOpts | void {
function addToMap(name: string, ctor: Function) {
if (!(name in serializationTypes)) {
- serializationTypes[name] = constructor;
+ serializationTypes[name] = ctor;
reverseMap[ctor.name] = name;
} else {
throw new Error(`Name ${name} has already been registered as deserializable`);
}
}
if (typeof constructor === "string") {
- return (ctor: Function) => {
+ return Object.assign((ctor: Function) => {
addToMap(constructor, ctor);
- };
+ }, { withFields: Deserializable.withFields });
}
addToMap(constructor.name, constructor);
}
+export namespace Deserializable {
+ export function withFields(fields: string[]) {
+ return function (constructor: { new(...fields: any[]): any }) {
+ Deserializable(constructor);
+ let schema = getDefaultModelSchema(constructor);
+ if (schema) {
+ schema.factory = context => {
+ const args = fields.map(key => context.json[key]);
+ return new constructor(...args);
+ };
+ // TODO A modified version of this would let us not reassign fields that we're passing into the constructor later on in deserializing
+ // fields.forEach(field => {
+ // if (field in schema.props) {
+ // let propSchema = schema.props[field];
+ // if (propSchema === false) {
+ // return;
+ // } else if (propSchema === true) {
+ // propSchema = primitive();
+ // }
+ // schema.props[field] = custom(propSchema.serializer,
+ // () => {
+ // return SKIP;
+ // });
+ // }
+ // });
+ } else {
+ schema = {
+ props: {},
+ factory: context => {
+ const args = fields.map(key => context.json[key]);
+ return new constructor(...args);
+ }
+ };
+ setDefaultModelSchema(constructor, schema);
+ }
+ };
+ }
+}
+
export function autoObject(): PropSchema {
return custom(
(s) => SerializationHelper.Serialize(s),