diff options
Diffstat (limited to 'src/new_fields')
| -rw-r--r-- | src/new_fields/Doc.ts | 198 | ||||
| -rw-r--r-- | src/new_fields/ObjectField.ts | 3 | ||||
| -rw-r--r-- | src/new_fields/Proxy.ts | 29 | ||||
| -rw-r--r-- | src/new_fields/RichTextField.ts | 2 | ||||
| -rw-r--r-- | src/new_fields/SchemaHeaderField.ts | 48 | ||||
| -rw-r--r-- | src/new_fields/ScriptField.ts | 12 | ||||
| -rw-r--r-- | src/new_fields/Types.ts | 12 | ||||
| -rw-r--r-- | src/new_fields/URLField.ts | 3 | ||||
| -rw-r--r-- | src/new_fields/util.ts | 46 |
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; |
