aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTyler Schicke <tyler_schicke@brown.edu>2019-04-30 00:11:27 -0400
committerTyler Schicke <tyler_schicke@brown.edu>2019-04-30 00:11:27 -0400
commit877b104a61d2ab072e3b6a006168ec03e2c46365 (patch)
tree649ed24c4d493f571671e5041b86e4f994dde649 /src
parented17a25d4bfd0e2c71f9f381645aa74419953d2d (diff)
Mostly fixed lists
Diffstat (limited to 'src')
-rw-r--r--src/client/util/UndoManager.ts4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx1
-rw-r--r--src/debug/Test.tsx8
-rw-r--r--src/new_fields/Doc.ts10
-rw-r--r--src/new_fields/List.ts192
-rw-r--r--src/new_fields/util.ts8
6 files changed, 179 insertions, 44 deletions
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index f7c3e5a7b..0b5280c4a 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -141,10 +141,10 @@ export namespace UndoManager {
});
//TODO Make this return the return value
- export function RunInBatch(fn: () => void, batchName: string) {
+ export function RunInBatch<T>(fn: () => T, batchName: string) {
let batch = StartBatch(batchName);
try {
- runInAction(fn);
+ return runInAction(fn);
} finally {
batch.end();
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dcded7648..d796bd8d5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -272,6 +272,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
get views() {
let curPage = FieldValue(this.Document.curPage, -1);
let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
+ if (!FieldValue(doc)) return prev;
var page = Cast(doc.page, "number", -1);
if (page === curPage || page === -1) {
let minim = Cast(doc.isMinimized, "boolean");
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 7415d4b28..04ef00722 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -61,14 +61,12 @@ class Test extends React.Component {
assert(test2.testDoc === undefined);
test2.url = 35;
assert(test2.url === 35);
- const l = new List<number>();
+ const l = new List<Doc>();
//TODO push, and other array functions don't go through the proxy
- l.push(1);
+ l.push(doc2);
//TODO currently length, and any other string fields will get serialized
- l.length = 3;
- l[2] = 5;
+ doc.list = l;
console.log(l.slice());
- console.log(SerializationHelper.Serialize(l));
}
render() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index d15b6309d..6ddb0df89 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -3,7 +3,7 @@ import { serializable, primitive, map, alias, list } from "serializr";
import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper";
import { Utils } from "../Utils";
import { DocServer } from "../client/DocServer";
-import { setter, getter, getField, updateFunction } from "./util";
+import { setter, getter, getField, updateFunction, deleteProperty } from "./util";
import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types";
import { UndoManager, undoBatch } from "../client/util/UndoManager";
import { listSpec } from "./Schema";
@@ -34,7 +34,7 @@ export class Doc extends RefField {
set: setter,
get: getter,
ownKeys: target => Object.keys(target.__fields),
- deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); },
+ deleteProperty: deleteProperty,
defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
});
if (!id || forceSave) {
@@ -151,8 +151,8 @@ export namespace Doc {
}
export function MakeLink(source: Doc, target: Doc): Doc {
- let linkDoc = new Doc;
- UndoManager.RunInBatch(() => {
+ return UndoManager.RunInBatch(() => {
+ let linkDoc = new Doc;
linkDoc.title = "New Link";
linkDoc.linkDescription = "";
linkDoc.linkTags = "Default";
@@ -171,8 +171,8 @@ export namespace Doc {
source.linkedToDocs = linkedTo = new List<Doc>();
}
linkedTo.push(linkDoc);
+ return linkDoc;
}, "make link");
- return linkDoc;
}
export function MakeDelegate(doc: Doc): Doc;
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index e4a80f7a1..ec1bf44a9 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -1,21 +1,173 @@
import { Deserializable, autoObject } from "../client/util/SerializationHelper";
import { Field, Update, Self } from "./Doc";
-import { setter, getter } from "./util";
+import { setter, getter, deleteProperty } from "./util";
import { serializable, alias, list } from "serializr";
import { observable, observe, IArrayChange, IArraySplice, IObservableArray, Lambda, reaction } from "mobx";
import { ObjectField, OnUpdate } from "./ObjectField";
+import { RefField } from "./RefField";
+import { ProxyField } from "./Proxy";
const listHandlers: any = {
- push(...items: any[]) {
- // console.log("push");
- // console.log(...items);
- return this[Self].__fields.push(...items);
+ /// Mutator methods
+ copyWithin() {
+ throw new Error("copyWithin not supported yet");
+ },
+ fill(value: any, start?: number, end?: number) {
+ if (value instanceof RefField) {
+ throw new Error("fill with RefFields not supported yet");
+ }
+ const res = this[Self].__fields.fill(value, start, end);
+ this[Update]();
+ return res;
},
pop(): any {
- return this[Self].__fields.pop();
+ const field = toRealField(this[Self].__fields.pop());
+ this[Update]();
+ return field;
+ },
+ push(...items: any[]) {
+ items = items.map(toObjectField);
+ const res = this[Self].__fields.push(...items);
+ this[Update]();
+ return res;
+ },
+ reverse() {
+ const res = this[Self].__fields.reverse();
+ this[Update]();
+ return res;
+ },
+ shift() {
+ const res = toRealField(this[Self].__fields.shift());
+ this[Update]();
+ return res;
+ },
+ sort(cmpFunc: any) {
+ const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
+ this[Update]();
+ return res;
+ },
+ splice(start: number, deleteCount: number, ...items: any[]) {
+ items = items.map(toObjectField);
+ const res = this[Self].__fields.splice(start, deleteCount, ...items);
+ this[Update]();
+ return res.map(toRealField);
+ },
+ unshift(...items: any[]) {
+ items = items.map(toObjectField);
+ const res = this[Self].__fields.unshift(...items);
+ this[Update]();
+ return res;
+
+ },
+ /// Accessor methods
+ concat(...items: any[]) {
+ return this[Self].__fields.map(toRealField).concat(...items);
+ },
+ includes(valueToFind: any, fromIndex: number) {
+ const fields = this[Self].__fields;
+ if (valueToFind instanceof RefField) {
+ return fields.map(toRealField).includes(valueToFind, fromIndex);
+ } else {
+ return fields.includes(valueToFind, fromIndex);
+ }
+ },
+ indexOf(valueToFind: any, fromIndex: number) {
+ const fields = this[Self].__fields;
+ if (valueToFind instanceof RefField) {
+ return fields.map(toRealField).indexOf(valueToFind, fromIndex);
+ } else {
+ return fields.indexOf(valueToFind, fromIndex);
+ }
+ },
+ join(separator: any) {
+ return this[Self].__fields.map(toRealField).join(separator);
+ },
+ lastIndexOf(valueToFind: any, fromIndex: number) {
+ const fields = this[Self].__fields;
+ if (valueToFind instanceof RefField) {
+ return fields.map(toRealField).lastIndexOf(valueToFind, fromIndex);
+ } else {
+ return fields.lastIndexOf(valueToFind, fromIndex);
+ }
+ },
+ slice(begin: number, end: number) {
+ return this[Self].__fields.slice(begin, end).map(toRealField);
+ },
+
+ /// Iteration methods
+ entries() {
+ return this[Self].__fields.map(toRealField).entries();
+ },
+ every(callback: any, thisArg: any) {
+ return this[Self].__fields.map(toRealField).every(callback, thisArg);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ },
+ filter(callback: any, thisArg: any) {
+ return this[Self].__fields.map(toRealField).filter(callback, thisArg);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ },
+ find(callback: any, thisArg: any) {
+ return this[Self].__fields.map(toRealField).find(callback, thisArg);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ },
+ findIndex(callback: any, thisArg: any) {
+ return this[Self].__fields.map(toRealField).findIndex(callback, thisArg);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ },
+ forEach(callback: any, thisArg: any) {
+ return this[Self].__fields.map(toRealField).forEach(callback, thisArg);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ },
+ map(callback: any, thisArg: any) {
+ return this[Self].__fields.map(toRealField).map(callback, thisArg);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ },
+ reduce(callback: any, initialValue: any) {
+ return this[Self].__fields.map(toRealField).reduce(callback, initialValue);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+ },
+ reduceRight(callback: any, initialValue: any) {
+ return this[Self].__fields.map(toRealField).reduceRight(callback, initialValue);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+ },
+ some(callback: any, thisArg: any) {
+ return this[Self].__fields.map(toRealField).some(callback, thisArg);
+ // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+ // If we don't want to support the array parameter, we should use this version instead
+ // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ },
+ values() {
+ return this[Self].__fields.map(toRealField).values();
+ },
+ [Symbol.iterator]() {
+ return this[Self].__fields.map(toRealField).values();
}
};
+function toObjectField(field: Field) {
+ return field instanceof RefField ? new ProxyField(field) : field;
+}
+
+function toRealField(field: Field) {
+ return field instanceof ProxyField ? field.value() : field;
+}
+
function listGetter(target: any, prop: string | number | symbol, receiver: any): any {
if (listHandlers.hasOwnProperty(prop)) {
return listHandlers[prop];
@@ -38,36 +190,15 @@ interface ListIndexUpdate<T> {
type ListUpdate<T> = ListSpliceUpdate<T> | ListIndexUpdate<T>;
-const ObserveDisposer = Symbol("Observe Disposer");
-
-function listObserver<T extends Field>(this: ListImpl<T>, change: IArrayChange<T> | IArraySplice<T>) {
- if (change.type === "splice") {
- this[Update]({
- index: change.index,
- removedCount: change.removedCount,
- added: change.added,
- type: change.type
- });
- } else {
- //This should already be handled by the getter for the Proxy
- // this[Update]({
- // index: change.index,
- // newValue: change.newValue,
- // type: change.type
- // });
- }
-}
-
@Deserializable("list")
class ListImpl<T extends Field> extends ObjectField {
constructor(fields: T[] = []) {
super();
this.___fields = fields;
- this[ObserveDisposer] = observe(this.__fields as IObservableArray<T>, listObserver.bind(this));
const list = new Proxy<this>(this, {
set: setter,
- get: getter,
- deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); },
+ get: listGetter,
+ deleteProperty: deleteProperty,
defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
});
return list;
@@ -82,8 +213,6 @@ class ListImpl<T extends Field> extends ObjectField {
private set __fields(value) {
this.___fields = value;
- this[ObserveDisposer]();
- this[ObserveDisposer] = observe(this.__fields as IObservableArray<T>, listObserver.bind(this));
}
// @serializable(alias("fields", list(autoObject())))
@@ -97,7 +226,6 @@ class ListImpl<T extends Field> extends ObjectField {
update && update();
}
- private [ObserveDisposer]: Lambda;
private [Self] = this;
}
export type List<T extends Field> = ListImpl<T> & T[];
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 511820115..128817ab8 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -78,6 +78,14 @@ export function getField(target: any, prop: string | number, ignoreProto: boolea
return field;
}
+export function deleteProperty(target: any, prop: string | number | symbol) {
+ if (typeof prop === "symbol") {
+ delete target[prop];
+ return true;
+ }
+ throw new Error("Currently properties can't be deleted from documents, assign to undefined instead");
+}
+
export function updateFunction(target: any, prop: any, value: any) {
return (diff?: any) => {
if (!diff) diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } };