aboutsummaryrefslogtreecommitdiff
path: root/src/new_fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/new_fields')
-rw-r--r--src/new_fields/Doc.ts198
-rw-r--r--src/new_fields/ObjectField.ts3
-rw-r--r--src/new_fields/Proxy.ts29
-rw-r--r--src/new_fields/RichTextField.ts2
-rw-r--r--src/new_fields/SchemaHeaderField.ts48
-rw-r--r--src/new_fields/ScriptField.ts12
-rw-r--r--src/new_fields/Types.ts12
-rw-r--r--src/new_fields/URLField.ts3
-rw-r--r--src/new_fields/util.ts46
9 files changed, 268 insertions, 85 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index da4f459e2..d634cf57f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -1,4 +1,4 @@
-import { observable, action } from "mobx";
+import { observable, action, runInAction, ObservableMap } from "mobx";
import { serializable, primitive, map, alias, list, PropSchema, custom } from "serializr";
import { autoObject, SerializationHelper, Deserializable, afterDocDeserialize } from "../client/util/SerializationHelper";
import { DocServer } from "../client/DocServer";
@@ -7,11 +7,13 @@ import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCa
import { listSpec } from "./Schema";
import { ObjectField } from "./ObjectField";
import { RefField, FieldId } from "./RefField";
-import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
+import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id, Copy } from "./FieldSymbols";
+import { scriptingGlobal, CompileScript, Scripting } from "../client/util/Scripting";
import { List } from "./List";
import { DocumentType } from "../client/documents/Documents";
-import { ComputedField } from "./ScriptField";
+import { ComputedField, ScriptField } from "./ScriptField";
+import { PrefetchProxy, ProxyField } from "./Proxy";
+import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -67,6 +69,8 @@ export function DocListCast(field: FieldResult): Doc[] {
export const WidthSym = Symbol("Width");
export const HeightSym = Symbol("Height");
+export const UpdatingFromServer = Symbol("UpdatingFromServer");
+const CachedUpdates = Symbol("Cached updates");
function fetchProto(doc: Doc) {
const proto = doc.proto;
@@ -75,8 +79,6 @@ function fetchProto(doc: Doc) {
}
}
-let updatingFromServer = false;
-
@scriptingGlobal
@Deserializable("Doc", fetchProto).withFields(["id"])
export class Doc extends RefField {
@@ -130,8 +132,10 @@ export class Doc extends RefField {
//{ [key: string]: Field | FieldWaiting | undefined }
private ___fields: any = {};
+ private [UpdatingFromServer]: boolean = false;
+
private [Update] = (diff: any) => {
- if (updatingFromServer) {
+ if (this[UpdatingFromServer]) {
return;
}
DocServer.UpdateField(this[Id], diff);
@@ -146,18 +150,29 @@ export class Doc extends RefField {
return "invalid";
}
+ private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
+
public async [HandleUpdate](diff: any) {
const set = diff.$set;
+ const sameAuthor = this.author === CurrentUserUtils.email;
if (set) {
for (const key in set) {
if (!key.startsWith("fields.")) {
continue;
}
- const value = await SerializationHelper.Deserialize(set[key]);
const fKey = key.substring(7);
- updatingFromServer = true;
- this[fKey] = value;
- updatingFromServer = false;
+ const fn = async () => {
+ const value = await SerializationHelper.Deserialize(set[key]);
+ this[UpdatingFromServer] = true;
+ this[fKey] = value;
+ this[UpdatingFromServer] = false;
+ };
+ if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
+ delete this[CachedUpdates][fKey];
+ await fn();
+ } else {
+ this[CachedUpdates][fKey] = fn;
+ }
}
}
const unset = diff.$unset;
@@ -167,9 +182,17 @@ export class Doc extends RefField {
continue;
}
const fKey = key.substring(7);
- updatingFromServer = true;
- delete this[fKey];
- updatingFromServer = false;
+ const fn = () => {
+ this[UpdatingFromServer] = true;
+ delete this[fKey];
+ this[UpdatingFromServer] = false;
+ };
+ if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
+ delete this[CachedUpdates][fKey];
+ await fn();
+ } else {
+ this[CachedUpdates][fKey] = fn;
+ }
}
}
}
@@ -186,6 +209,21 @@ export namespace Doc {
// return Cast(field, ctor);
// });
// }
+ export function RunCachedUpdate(doc: Doc, field: string) {
+ const update = doc[CachedUpdates][field];
+ if (update) {
+ update();
+ delete doc[CachedUpdates][field];
+ }
+ }
+ export function AddCachedUpdate(doc: Doc, field: string, oldValue: any) {
+ const val = oldValue;
+ doc[CachedUpdates][field] = () => {
+ doc[UpdatingFromServer] = true;
+ doc[field] = val;
+ doc[UpdatingFromServer] = false;
+ };
+ }
export function MakeReadOnly(): { end(): void } {
makeReadOnly();
return {
@@ -196,8 +234,12 @@ export namespace Doc {
}
export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
- const self = doc[Self];
- return getField(self, key, ignoreProto);
+ try {
+ const self = doc[Self];
+ return getField(self, key, ignoreProto);
+ } catch {
+ return doc;
+ }
}
export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
@@ -268,6 +310,10 @@ export namespace Doc {
export function GetProto(doc: Doc) {
return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc);
}
+ export function GetDataDoc(doc: Doc): Doc {
+ let proto = Doc.GetProto(doc);
+ return proto === doc ? proto : Doc.GetDataDoc(proto);
+ }
export function allKeys(doc: Doc): string[] {
const results: Set<string> = new Set;
@@ -336,19 +382,24 @@ export namespace Doc {
return fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc;
}
+ export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
+ let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
+ docExtensionForField.title = fieldKey + ".ext";
+ docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
+ docExtensionForField.type = DocumentType.EXTENSION;
+ let proto: Doc | undefined = doc;
+ while (proto && !Doc.IsPrototype(proto) && proto.proto) {
+ proto = proto.proto;
+ }
+ (proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField);
+ return docExtensionForField;
+ }
+
export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) {
let docExtensionForField = doc[fieldKey + "_ext"] as Doc;
if (docExtensionForField === undefined) {
setTimeout(() => {
- docExtensionForField = new Doc(doc[Id] + fieldKey, true);
- docExtensionForField.title = fieldKey + ".ext";
- docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
- docExtensionForField.type = DocumentType.EXTENSION;
- let proto: Doc | undefined = doc;
- while (proto && !Doc.IsPrototype(proto)) {
- proto = proto.proto;
- }
- (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField;
+ CreateDocumentExtensionForField(doc, fieldKey);
}, 0);
} else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already
docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0);
@@ -356,10 +407,15 @@ export namespace Doc {
}
}
export function MakeAlias(doc: Doc) {
- if (!GetT(doc, "isPrototype", "boolean", true)) {
- return Doc.MakeCopy(doc);
+ let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
+ let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
+ let script = `return renameAlias(self, ${aliasNumber})`;
+ //let script = "StrCast(self.title).replace(/\\([0-9]*\\)/, \"\") + `(${n})`";
+ let compiled = CompileScript(script, { params: { this: "Doc" }, capturedVariables: { self: doc }, typecheck: false });
+ if (compiled.compiled) {
+ alias.title = new ComputedField(compiled);
}
- return Doc.MakeDelegate(doc); // bcz?
+ return alias;
}
//
@@ -403,7 +459,7 @@ export namespace Doc {
export function GetLayoutDataDocPair(doc: Doc, dataDoc: Doc | undefined, fieldKey: string, childDocLayout: Doc) {
let layoutDoc = childDocLayout;
- let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc ? dataDoc : undefined;
+ let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
Doc.UpdateDocumentExtensionForField(resolvedDataDoc, fieldKey);
let fieldExtensionDoc = Doc.resolvedFieldDataDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)), "dummy");
@@ -415,7 +471,7 @@ export namespace Doc {
export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
const copy = new Doc;
Object.keys(doc).forEach(key => {
- const field = doc[key];
+ const field = ProxyField.WithoutProxy(() => doc[key]);
if (key === "proto" && copyProto) {
if (field instanceof Doc) {
copy[key] = Doc.MakeCopy(field);
@@ -426,7 +482,7 @@ export namespace Doc {
} else if (field instanceof ObjectField) {
copy[key] = ObjectField.MakeCopy(field);
} else if (field instanceof Promise) {
- field.then(f => (copy[key] === undefined) && (copy[key] = f)); //TODO what should we do here?
+ debugger; //This shouldn't happend...
} else {
copy[key] = field;
}
@@ -451,7 +507,8 @@ export namespace Doc {
let _applyCount: number = 0;
export function ApplyTemplate(templateDoc: Doc) {
if (!templateDoc) return undefined;
- let otherdoc = new Doc();
+ let datadoc = new Doc();
+ let otherdoc = Doc.MakeDelegate(datadoc);
otherdoc.width = templateDoc[WidthSym]();
otherdoc.height = templateDoc[HeightSym]();
otherdoc.title = templateDoc.title + "(..." + _applyCount++ + ")";
@@ -459,8 +516,30 @@ export namespace Doc {
otherdoc.miniLayout = StrCast(templateDoc.miniLayout);
otherdoc.detailedLayout = otherdoc.layout;
otherdoc.type = DocumentType.TEMPLATE;
+ !templateDoc.nativeWidth && (otherdoc.nativeWidth = 0);
+ !templateDoc.nativeHeight && (otherdoc.nativeHeight = 0);
return otherdoc;
}
+ export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
+ let temp = Doc.MakeDelegate(templateDoc);
+ target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined;
+ target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined;
+ target.width = templateDoc.width;
+ target.height = templateDoc.height;
+ target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
+ Doc.GetProto(target).type = DocumentType.TEMPLATE;
+ if (targetData && targetData.layout === target) {
+ targetData.layout = temp;
+ targetData.miniLayout = StrCast(templateDoc.miniLayout);
+ targetData.detailedLayout = targetData.layout;
+ } else {
+ target.layout = temp;
+ target.miniLayout = StrCast(templateDoc.miniLayout);
+ target.detailedLayout = target.layout;
+ }
+ !templateDoc.nativeWidth && (target.nativeWidth = 0);
+ !templateDoc.nativeHeight && (target.nativeHeight = 0);
+ }
export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
// move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
@@ -493,18 +572,47 @@ export namespace Doc {
setTimeout(() => fieldTemplate.proto = templateDataDoc);
}
- export async function ToggleDetailLayout(d: Doc) {
- let miniLayout = await PromiseValue(d.miniLayout);
- let detailLayout = await PromiseValue(d.detailedLayout);
- d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout);
- if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined;
+ export function ToggleDetailLayout(d: Doc) {
+ runInAction(async () => {
+ let miniLayout = await PromiseValue(d.miniLayout);
+ let detailLayout = await PromiseValue(d.detailedLayout);
+ d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout);
+ if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined;
+ });
}
- export async function UseDetailLayout(d: Doc) {
- let miniLayout = await PromiseValue(d.miniLayout);
- let detailLayout = await PromiseValue(d.detailedLayout);
- if (miniLayout && d.layout === miniLayout && detailLayout) {
- d.layout = detailLayout;
- d.nativeWidth = d.nativeHeight = undefined;
- }
+ export function UseDetailLayout(d: Doc) {
+ runInAction(async () => {
+ let detailLayout = await d.detailedLayout;
+ if (detailLayout) {
+ d.layout = detailLayout;
+ d.nativeWidth = d.nativeHeight = undefined;
+ if (detailLayout instanceof Doc) {
+ let delegDetailLayout = Doc.MakeDelegate(detailLayout);
+ d.layout = delegDetailLayout;
+ delegDetailLayout.layout = await delegDetailLayout.detailedLayout;
+ }
+ }
+ });
+ }
+
+ export class DocBrush {
+ @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+ }
+ const manager = new DocBrush();
+ export function IsBrushed(doc: Doc) {
+ return manager.BrushedDoc.has(doc) || manager.BrushedDoc.has(Doc.GetDataDoc(doc));
}
-} \ No newline at end of file
+ export function IsBrushedDegree(doc: Doc) {
+ return manager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 2 : manager.BrushedDoc.has(doc) ? 1 : 0;
+ }
+ export function BrushDoc(doc: Doc) {
+ manager.BrushedDoc.set(doc, true);
+ manager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
+ }
+ export function UnBrushDoc(doc: Doc) {
+ manager.BrushedDoc.delete(doc);
+ manager.BrushedDoc.delete(Doc.GetDataDoc(doc));
+ }
+}
+Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
+Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); \ No newline at end of file
diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts
index 5f4a6f8fb..65ada91c0 100644
--- a/src/new_fields/ObjectField.ts
+++ b/src/new_fields/ObjectField.ts
@@ -1,6 +1,7 @@
import { Doc } from "./Doc";
import { RefField } from "./RefField";
import { OnUpdate, Parent, Copy, ToScriptString } from "./FieldSymbols";
+import { Scripting } from "../client/util/Scripting";
export abstract class ObjectField {
protected [OnUpdate](diff?: any) { }
@@ -15,3 +16,5 @@ export namespace ObjectField {
return field[Copy]();
}
}
+
+Scripting.addGlobal(ObjectField); \ No newline at end of file
diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts
index b3e8d6467..c6292e37c 100644
--- a/src/new_fields/Proxy.ts
+++ b/src/new_fields/Proxy.ts
@@ -7,6 +7,7 @@ import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
import { Id, Copy, ToScriptString } from "./FieldSymbols";
import { scriptingGlobal } from "../client/util/Scripting";
+import { Plugins } from "./util";
@Deserializable("proxy")
export class ProxyField<T extends RefField> extends ObjectField {
@@ -68,6 +69,34 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
}
+export namespace ProxyField {
+ let useProxy = true;
+ export function DisableProxyFields() {
+ useProxy = false;
+ }
+
+ export function EnableProxyFields() {
+ useProxy = true;
+ }
+
+ export function WithoutProxy<T>(fn: () => T) {
+ DisableProxyFields();
+ try {
+ return fn();
+ } finally {
+ EnableProxyFields();
+ }
+ }
+
+ export function initPlugin() {
+ Plugins.addGetterPlugin((doc, _, value) => {
+ if (useProxy && value instanceof ProxyField) {
+ return { value: value.value() };
+ }
+ });
+ }
+}
+
function prefetchValue(proxy: PrefetchProxy<RefField>) {
return proxy.value() as any;
}
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index 78a3a4067..89799b2af 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -20,6 +20,6 @@ export class RichTextField extends ObjectField {
}
[ToScriptString]() {
- return "invalid";
+ return `new RichTextField("${this.Data}")`;
}
} \ No newline at end of file
diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts
index a6df31e81..7494c9bd1 100644
--- a/src/new_fields/SchemaHeaderField.ts
+++ b/src/new_fields/SchemaHeaderField.ts
@@ -1,12 +1,12 @@
import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, createSimpleSchema, primitive } from "serializr";
+import { serializable, primitive } from "serializr";
import { ObjectField } from "./ObjectField";
import { Copy, ToScriptString, OnUpdate } from "./FieldSymbols";
-import { scriptingGlobal, Scripting } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/Scripting";
import { ColumnType } from "../client/views/collections/CollectionSchemaView";
export const PastelSchemaPalette = new Map<string, string>([
- ["pink1", "#FFB4E8"],
+ // ["pink1", "#FFB4E8"],
["pink2", "#ff9cee"],
["pink3", "#ffccf9"],
["pink4", "#fcc2ff"],
@@ -15,7 +15,7 @@ export const PastelSchemaPalette = new Map<string, string>([
["purple2", "#c5a3ff"],
["purple3", "#d5aaff"],
["purple4", "#ecd4ff"],
- ["purple5", "#fb34ff"],
+ // ["purple5", "#fb34ff"],
["purple6", "#dcd3ff"],
["purple7", "#a79aff"],
["purple8", "#b5b9ff"],
@@ -25,17 +25,18 @@ export const PastelSchemaPalette = new Map<string, string>([
["bluegreen3", "#c4faf8"],
["bluegreen4", "#85e3ff"],
["bluegreen5", "#ace7ff"],
- ["bluegreen6", "#6eb5ff"],
+ // ["bluegreen6", "#6eb5ff"],
["bluegreen7", "#bffcc6"],
["bluegreen8", "#dbffd6"],
["yellow1", "#f3ffe3"],
["yellow2", "#e7ffac"],
["yellow3", "#ffffd1"],
["yellow4", "#fff5ba"],
- ["red1", "#ffc9de"],
+ // ["red1", "#ffc9de"],
["red2", "#ffabab"],
["red3", "#ffbebc"],
["red4", "#ffcbc1"],
+ ["orange1", "#ffd5b3"],
]);
export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)];
@@ -45,20 +46,26 @@ export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.
export class SchemaHeaderField extends ObjectField {
@serializable(primitive())
heading: string;
+ @serializable(primitive())
color: string;
+ @serializable(primitive())
type: number;
+ @serializable(primitive())
+ width: number;
+ @serializable(primitive())
+ collapsed: boolean | undefined;
+ @serializable(primitive())
+ desc: boolean | undefined; // boolean determines sort order, undefined when no sort
- constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType) {
+ constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) {
super();
this.heading = heading;
this.color = color;
- if (type) {
- this.type = type;
- }
- else {
- this.type = 0;
- }
+ this.type = type ? type : 0;
+ this.width = width ? width : -1;
+ this.desc = desc;
+ this.collapsed = collapsed;
}
setHeading(heading: string) {
@@ -76,6 +83,21 @@ export class SchemaHeaderField extends ObjectField {
this[OnUpdate]();
}
+ setWidth(width: number) {
+ this.width = width;
+ this[OnUpdate]();
+ }
+
+ setDesc(desc: boolean | undefined) {
+ this.desc = desc;
+ this[OnUpdate]();
+ }
+
+ setCollapsed(collapsed: boolean | undefined) {
+ this.collapsed = collapsed;
+ this[OnUpdate]();
+ }
+
[Copy]() {
return new SchemaHeaderField(this.heading, this.color, this.type);
}
diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts
index 6d52525b8..83fb52d07 100644
--- a/src/new_fields/ScriptField.ts
+++ b/src/new_fields/ScriptField.ts
@@ -137,9 +137,11 @@ export namespace ComputedField {
}
}
- Plugins.addGetterPlugin((doc, _, value) => {
- if (useComputed && value instanceof ComputedField) {
- return { value: value.value(doc), shouldReturn: true };
- }
- });
+ export function initPlugin() {
+ Plugins.addGetterPlugin((doc, _, value) => {
+ if (useComputed && value instanceof ComputedField) {
+ return { value: value.value(doc), shouldReturn: true };
+ }
+ });
+ }
} \ No newline at end of file
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 565ae2ee3..0ca35fab2 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -2,6 +2,7 @@ import { Field, Opt, FieldResult, Doc } from "./Doc";
import { List } from "./List";
import { RefField } from "./RefField";
import { DateField } from "./DateField";
+import { ScriptField } from "./ScriptField";
export type ToType<T extends InterfaceValue> =
T extends "string" ? string :
@@ -48,9 +49,11 @@ export interface Interface {
}
export type WithoutRefField<T extends Field> = T extends RefField ? never : T;
-export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
-export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
+export type CastCtor = ToConstructor<Field> | ListSpec<Field>;
+
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
if (field instanceof Promise) {
return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal;
}
@@ -84,6 +87,9 @@ export function BoolCast(field: FieldResult, defaultVal: boolean | null = false)
export function DateCast(field: FieldResult) {
return Cast(field, DateField, null);
}
+export function ScriptCast(field: FieldResult) {
+ return Cast(field, ScriptField, null);
+}
type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index d935a61af..b9ad96450 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -42,4 +42,5 @@ export abstract class URLField extends ObjectField {
@scriptingGlobal @Deserializable("image") export class ImageField extends URLField { }
@scriptingGlobal @Deserializable("video") export class VideoField extends URLField { }
@scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { }
-@scriptingGlobal @Deserializable("web") export class WebField extends URLField { } \ No newline at end of file
+@scriptingGlobal @Deserializable("web") export class WebField extends URLField { }
+@scriptingGlobal @Deserializable("youtube") export class YoutubeField extends URLField { }
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index b59ec9b9a..c546e2aac 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -1,12 +1,13 @@
import { UndoManager } from "../client/util/UndoManager";
-import { Doc, Field, FieldResult } from "./Doc";
+import { Doc, Field, FieldResult, UpdatingFromServer } from "./Doc";
import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField } from "./Proxy";
import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
import { action } from "mobx";
import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
-import { ComputedField } from "./ScriptField";
+import { DocServer } from "../client/DocServer";
+import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
@@ -14,7 +15,7 @@ function _readOnlySetter(): never {
export interface GetterResult {
value: FieldResult;
- shouldReturn: boolean;
+ shouldReturn?: boolean;
}
export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined;
const getterPlugins: GetterPlugin[] = [];
@@ -58,18 +59,29 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
delete curValue[Parent];
delete curValue[OnUpdate];
}
- if (value === undefined) {
- delete target.__fields[prop];
- } else {
- target.__fields[prop] = value;
+ const writeMode = DocServer.getFieldWriteMode(prop as string);
+ const fromServer = target[UpdatingFromServer];
+ const sameAuthor = fromServer || (receiver.author === CurrentUserUtils.email);
+ const writeToDoc = sameAuthor || (writeMode !== DocServer.WriteMode.LiveReadonly);
+ const writeToServer = sameAuthor || (writeMode === DocServer.WriteMode.Default);
+ if (writeToDoc) {
+ if (value === undefined) {
+ delete target.__fields[prop];
+ } else {
+ target.__fields[prop] = value;
+ }
+ if (typeof value === "object" && !(value instanceof ObjectField)) debugger;
+ if (writeToServer) {
+ if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } });
+ else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } });
+ } else {
+ DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
+ }
+ UndoManager.AddEvent({
+ redo: () => receiver[prop] = value,
+ undo: () => receiver[prop] = curValue
+ });
}
- if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } });
- if (typeof value === "object" && !(value instanceof ObjectField)) debugger;
- else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } });
- UndoManager.AddEvent({
- redo: () => receiver[prop] = value,
- undo: () => receiver[prop] = curValue
- });
return true;
});
@@ -88,6 +100,9 @@ export function setter(target: any, prop: string | symbol | number, value: any,
}
export function getter(target: any, prop: string | symbol | number, receiver: any): any {
+ if (prop === "then") {//If we're being awaited
+ return undefined;
+ }
if (typeof prop === "symbol") {
return target.__fields[prop] || target[prop];
}
@@ -100,9 +115,6 @@ export function getter(target: any, prop: string | symbol | number, receiver: an
function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any {
receiver = receiver || target[SelfProxy];
let field = target.__fields[prop];
- if (field instanceof ProxyField) {
- return field.value();
- }
for (const plugin of getterPlugins) {
const res = plugin(receiver, prop, field);
if (res === undefined) continue;