diff options
Diffstat (limited to 'src/fields')
| -rw-r--r-- | src/fields/Doc.ts | 56 | ||||
| -rw-r--r-- | src/fields/List.ts | 34 | ||||
| -rw-r--r-- | src/fields/ObjectField.ts | 1 | ||||
| -rw-r--r-- | src/fields/Proxy.ts | 17 | ||||
| -rw-r--r-- | src/fields/Schema.ts | 2 | ||||
| -rw-r--r-- | src/fields/Types.ts | 15 | ||||
| -rw-r--r-- | src/fields/documentSchemas.ts | 3 |
7 files changed, 70 insertions, 58 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 7714ce46d..4512d5c5b 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -7,7 +7,7 @@ import { DocServer } from '../client/DocServer'; import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes'; import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals'; import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper'; -import { undoable } from '../client/util/UndoManager'; +import { undoable, UndoManager } from '../client/util/UndoManager'; import { ClientUtils, incrementTitleCopy } from '../ClientUtils'; import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, Animation, AudioPlay, Brushed, CachedUpdates, DirectLinks, @@ -38,7 +38,7 @@ export namespace Field { */ export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean): string { const isOnDelegate = !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, '')); - const field = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); const valFunc = (field: FieldType): string => { const res = field instanceof ComputedField && showComputedValue @@ -55,7 +55,7 @@ export namespace Field { .trim() .replace(/^new List\((.*)\)$/, '$1'); }; - return !Field.IsField(field) ? (key.startsWith('_') ? '=' : '') : (isOnDelegate ? '=' : '') + valFunc(field); + return !Field.IsField(cfield) ? (key.startsWith('_') ? '=' : '') : (isOnDelegate ? '=' : '') + valFunc(cfield); } export function toScriptString(field: FieldType) { switch (typeof field) { @@ -98,9 +98,11 @@ export namespace Field { export function IsField(field: any, includeUndefined: boolean = false): field is FieldType | undefined { return ['string', 'number', 'boolean'].includes(typeof field) || field instanceof ObjectField || field instanceof RefField || (includeUndefined && field === undefined); } + // eslint-disable-next-line @typescript-eslint/no-shadow export function Copy(field: any) { return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field; } + UndoManager.SetFieldPrinter(toJavascriptString); } export type FieldType = number | string | boolean | ObjectField | RefField; export type Opt<T> = T | undefined; @@ -369,7 +371,7 @@ export class Doc extends RefField { const sameAuthor = this.author === ClientUtils.CurrentUserEmail(); const fprefix = 'fields.'; Object.keys(set ?? {}) - .filter(key => Object.prototype.hasOwnProperty.call(set, key) && key.startsWith(fprefix)) + .filter(key => key.startsWith(fprefix)) .forEach(async key => { const fKey = key.substring(fprefix.length); const fn = async () => { @@ -396,7 +398,7 @@ export class Doc extends RefField { }); const unset = diff.$unset; Object.keys(unset ?? {}) - .filter(key => Object.prototype.hasOwnProperty.call(unset, key) && key.startsWith(fprefix)) + .filter(key => key.startsWith(fprefix)) .forEach(async key => { const fKey = key.substring(7); const fn = () => { @@ -511,15 +513,13 @@ export namespace Doc { */ export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<FieldType>>>, skipUndefineds: boolean = false, isInitializing = false) { isInitializing && (doc[Initializing] = true); - Object.keys(fields) - .filter(key => Object.prototype.hasOwnProperty.call(fields, key)) - .forEach(key => { - const value = (fields as any)[key]; - if (!skipUndefineds || value !== undefined) { - // Do we want to filter out undefineds? - doc[key] = value; - } - }); + Object.keys(fields).forEach(key => { + const value = (fields as any)[key]; + if (!skipUndefineds || value !== undefined) { + // Do we want to filter out undefineds? + doc[key] = value; + } + }); isInitializing && (doc[Initializing] = false); return doc; } @@ -640,7 +640,7 @@ export namespace Doc { export function BestEmbedding(doc: Doc) { const dataDoc = doc[DocData]; const availableEmbeddings = Doc.GetEmbeddings(dataDoc); - const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === ClientUtils.CurrentUserEmail()); + const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(d => !d.embedContainer && d.author === ClientUtils.CurrentUserEmail()); bestEmbedding && Doc.AddEmbedding(doc, doc); return bestEmbedding ?? Doc.MakeEmbedding(doc); } @@ -673,27 +673,27 @@ export namespace Doc { }; const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); const field = ProxyField.WithoutProxy(() => doc[key]); - const copyObjectField = async (field: ObjectField) => { + const copyObjectField = async (objField: ObjectField) => { const list = await Cast(doc[key], listSpec(Doc)); const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc); if (docs !== undefined && docs.length) { const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates))); assignKey(new List<Doc>(clones)); } else { - assignKey(ObjectField.MakeCopy(field)); - if (field instanceof RichTextField) { - if (DocsInTextFieldIds.some(id => field.Data.includes(`"${id}":`))) { + assignKey(ObjectField.MakeCopy(objField)); + if (objField instanceof RichTextField) { + if (DocsInTextFieldIds.some(id => objField.Data.includes(`"${id}":`))) { const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => '(' + exp + ')').join('|') + ')":"([a-z-A-Z0-9_]*)"', 'g'); - const rawdocids = field.Data.match(docidsearch); + const rawdocids = objField.Data.match(docidsearch); const docids = rawdocids?.map((str: string) => DocsInTextFieldIds.reduce((output, exp) => output.replace(new RegExp(`${exp}":`, 'g'), ''), str) .replace(/"/g, '') .trim() ); const results = docids && (await DocServer.GetRefFields(docids)); - const docs = results && Array.from(Object.keys(results)).map(key => DocCast(results[key])); - docs?.map(doc => doc && Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); - rtfs.push({ copy, key, field }); + const rdocs = results && Array.from(Object.keys(results)).map(rkey => DocCast(results[rkey])); + rdocs?.map(d => d && Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); + rtfs.push({ copy, key, field: objField }); } } } @@ -769,7 +769,7 @@ export namespace Doc { export async function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map<string, Doc> = new Map()) { const linkMap = new Map<string, Doc>(); const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = []; - const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates); + const clone = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates); const repaired = new Set<Doc>(); const linkedDocs = Array.from(linkMap.values()); linkedDocs.forEach(link => Doc.AddLink?.(link, true)); @@ -787,8 +787,8 @@ export namespace Doc { copy[key] = new RichTextField(field.Data.replace(docidsearch, replacer).replace(re, replacer2), field.Text); }); const clonedDocs = [...Array.from(cloneMap.values()), ...linkedDocs]; - clonedDocs.forEach(clone => Doc.repairClone(clone, cloneMap, cloneTemplates, repaired)); - return { clone: copy, map: cloneMap, linkMap }; + clonedDocs.forEach(cloneDoc => Doc.repairClone(cloneDoc, cloneMap, cloneTemplates, repaired)); + return { clone, map: cloneMap, linkMap }; } const _pendingMap = new Set<string>(); @@ -1236,9 +1236,9 @@ export namespace Doc { }); } /// if doc is defined, then it is unhighlighted, otherwise all highlighted docs are unhighlighted - export function UnHighlightDoc(doc?: Doc) { + export function UnHighlightDoc(docs?: Doc) { runInAction(() => { - (doc ? [doc] : Array.from(highlightedDocs)).forEach(doc => { + (docs ? [docs] : Array.from(highlightedDocs)).forEach(doc => { highlightedDocs.delete(doc); highlightedDocs.delete(doc[DocData]); doc[Highlight] = doc[DocData][Highlight] = false; diff --git a/src/fields/List.ts b/src/fields/List.ts index f6e0473ea..f97f208fe 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -2,14 +2,12 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { alias, list, serializable } from 'serializr'; import { ScriptingGlobals } from '../client/util/ScriptingGlobals'; import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper'; -import { Field, FieldType } from './Doc'; +import { Field, FieldType, StrListCast } from './Doc'; import { FieldTuples, Self, SelfProxy } from './DocSymbols'; import { Copy, FieldChanged, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols'; import { ObjGetRefFields, ObjectField } from './ObjectField'; import { ProxyField } from './Proxy'; import { RefField } from './RefField'; -import { listSpec } from './Schema'; -import { Cast } from './Types'; import { containedFieldChangedHandler, deleteProperty, getter, setter } from './util'; function toObjectField(field: FieldType) { @@ -223,7 +221,7 @@ class ListImpl<T extends FieldType> extends ObjectField { }, }; static listGetter(target: any, prop: string | symbol, receiver: any): any { - if (ListImpl.listHandlers.hasOwnProperty(prop)) { + if (Object.prototype.hasOwnProperty.call(ListImpl.listHandlers, prop)) { return ListImpl.listHandlers[prop]; } return getter(target, prop, receiver); @@ -252,6 +250,7 @@ class ListImpl<T extends FieldType> extends ObjectField { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, }); + // eslint-disable-next-line no-use-before-define this[SelfProxy] = list as any as List<FieldType>; // bcz: ugh .. don't know how to convince typesecript that list is a List if (fields) { this[SelfProxy].push(...fields); @@ -287,13 +286,13 @@ class ListImpl<T extends FieldType> extends ObjectField { private set __fieldTuples(value) { this[FieldTuples] = value; - for (const key in value) { - const item = value[key]; + Object.keys(value).forEach(key => { + const item = value[Number(key)]; if (item instanceof ObjectField) { item[Parent] = this[Self]; item[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], Number(key), item); } - } + }); } [Copy]() { @@ -306,25 +305,24 @@ class ListImpl<T extends FieldType> extends ObjectField { @observable private [FieldTuples]: StoredType<T>[] = []; private [Self] = this; + // eslint-disable-next-line no-use-before-define private [SelfProxy]: List<FieldType>; // also used in utils.ts even though it won't be found using find all references - [ToJavascriptString]() { - return `[${(this as any).map((field: any) => Field.toScriptString(field))}]`; - } - [ToScriptString]() { - return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`; - } - [ToString]() { - return `[${(this as any).map((field: any) => Field.toString(field))}]`; - } + [ToScriptString]() { return `new List(${this[ToJavascriptString]})`; } // prettier-ignore + [ToJavascriptString]() { return `[${(this as any).map((field: any) => Field.toScriptString(field))}]`; } // prettier-ignore + [ToString]() { return `[${(this as any).map((field: any) => Field.toString(field))}]`; } // prettier-ignore } + +// declare List as a type so you can use it in type declarations, e.g., { l: List, ...} export type List<T extends FieldType> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[]; +// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>() +// eslint-disable-next-line no-redeclare export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as any; ScriptingGlobals.add('List', List); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function compareLists(l1: any, l2: any) { - const L1 = Cast(l1, listSpec('string'), []); - const L2 = Cast(l2, listSpec('string'), []); + const L1 = StrListCast(l1); + const L2 = StrListCast(l2); return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true); }, 'compare two lists'); diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts index 6c70adc1d..231086262 100644 --- a/src/fields/ObjectField.ts +++ b/src/fields/ObjectField.ts @@ -5,6 +5,7 @@ import { RefField } from './RefField'; export abstract class ObjectField { // prettier-ignore public [FieldChanged]?: (diff?: { op: '$addToSet' | '$remFromSet' | '$set'; + // eslint-disable-next-line no-use-before-define items: FieldType[] | undefined; length: number | undefined; hint?: any }, serverOp?: any) => void; diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts index 820d9b6ff..4f8058ce4 100644 --- a/src/fields/Proxy.ts +++ b/src/fields/Proxy.ts @@ -21,7 +21,7 @@ export class ProxyField<T extends RefField> extends ObjectField { constructor(value?: T | string) { super(); if (typeof value === 'string') { - //this.cache = DocServer.GetCachedRefField(value) as any; + // this.cache = DocServer.GetCachedRefField(value) as any; this.fieldId = value; } else if (value) { this.cache = { field: value, p: undefined }; @@ -29,7 +29,7 @@ export class ProxyField<T extends RefField> extends ObjectField { } } - [ToValue](doc: any) { + [ToValue](/* doc: any */) { return ProxyField.toValue(this); } @@ -39,10 +39,10 @@ export class ProxyField<T extends RefField> extends ObjectField { } [ToJavascriptString]() { - return Field.toScriptString(this[ToValue](undefined)?.value); + return Field.toScriptString(this[ToValue]()?.value); } [ToScriptString]() { - return Field.toScriptString(this[ToValue](undefined)?.value); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ? + return Field.toScriptString(this[ToValue]()?.value); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ? } [ToString]() { return 'ProxyField'; @@ -59,7 +59,9 @@ export class ProxyField<T extends RefField> extends ObjectField { return this._cache; } private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) { - runInAction(() => (this._cache = { ...val })); + runInAction(() => { + this._cache = { ...val }; + }); } private failed = false; @@ -78,7 +80,7 @@ export class ProxyField<T extends RefField> extends ObjectField { return this.cache.field ?? this.cache.p; } @computed get needsRequesting(): boolean { - return !this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId) ? true : false; + return !!(!this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId)); } setExternalValuePromise(externalValuePromise: Promise<any>) { @@ -92,6 +94,7 @@ export class ProxyField<T extends RefField> extends ObjectField { } } +// eslint-disable-next-line no-redeclare export namespace ProxyField { let useProxy = true; export function DisableProxyFields() { @@ -115,9 +118,11 @@ export namespace ProxyField { if (useProxy) { return { value: value.value }; } + return undefined; } } +// eslint-disable-next-line no-use-before-define function prefetchValue(proxy: PrefetchProxy<RefField>) { return proxy.value as any; } diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts index 364899dc7..ed603e5de 100644 --- a/src/fields/Schema.ts +++ b/src/fields/Schema.ts @@ -36,7 +36,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu if (prop in schema) { const desc = prop === 'proto' ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should? if (typeof desc === 'object' && 'defaultVal' in desc && 'type' in desc) { - //defaultSpec + // defaultSpec return Cast(field, desc.type, desc.defaultVal); } if (typeof desc === 'function' && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) { diff --git a/src/fields/Types.ts b/src/fields/Types.ts index 6ed94d341..57a310f6d 100644 --- a/src/fields/Types.ts +++ b/src/fields/Types.ts @@ -7,6 +7,7 @@ import { RichTextField } from './RichTextField'; import { ScriptField } from './ScriptField'; import { CsvField, ImageField, WebField } from './URLField'; +// eslint-disable-next-line no-use-before-define export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T; export type DefaultFieldConstructor<T extends FieldType> = { @@ -27,6 +28,7 @@ export type ToType<T extends InterfaceValue> = T extends 'string' : T extends ListSpec<infer U> ? List<U> : // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never; + // eslint-disable-next-line @typescript-eslint/no-unused-vars T extends DefaultFieldConstructor<infer _U> ? never : T extends { new (...args: any[]): List<FieldType> } @@ -37,6 +39,10 @@ export type ToType<T extends InterfaceValue> = T extends 'string' ? R : never; +export interface Interface { + [key: string]: InterfaceValue; + // [key: string]: ToConstructor<Field> | ListSpec<Field[]>; +} export type ToInterface<T extends Interface> = { [P in Exclude<keyof T, 'proto'>]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>; }; @@ -47,10 +53,6 @@ export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never; export type Tail<T extends any[]> = ((...t: T) => any) extends (_: any, ...tail: infer TT) => any ? TT : []; export type HasTail<T extends any[]> = T extends [] | [any] ? false : true; // TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial -export interface Interface { - [key: string]: InterfaceValue; - // [key: string]: ToConstructor<Field> | ListSpec<Field[]>; -} export type WithoutRefField<T extends FieldType> = T extends RefField ? never : T; export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>; @@ -58,13 +60,16 @@ export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>; type WithoutList<T extends FieldType> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T; export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>; +// eslint-disable-next-line no-redeclare export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>; +// eslint-disable-next-line no-redeclare 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; } if (field !== undefined && !(field instanceof Promise)) { if (typeof ctor === 'string') { + // eslint-disable-next-line valid-typeof if (typeof field === ctor) { return field as ToType<T>; } @@ -118,7 +123,9 @@ export function ImageCast(field: FieldResult, defaultVal: ImageField | null = nu } export function FieldValue<T extends FieldType, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>; +// eslint-disable-next-line no-redeclare export function FieldValue<T extends FieldType>(field: FieldResult<T>): Opt<T>; +// eslint-disable-next-line no-redeclare export function FieldValue<T extends FieldType>(field: FieldResult<T>, defaultValue?: T): Opt<T> { return field instanceof Promise || field === undefined ? defaultValue : field; } diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 1cacfe30c..c73689e1f 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -104,7 +104,7 @@ export const documentSchema = createSchema({ export const collectionSchema = createSchema({ childLayoutTemplate: Doc, // layout template to use to render children of a collecion - childLayoutString: 'string', //layout string to use to render children of a collection + childLayoutString: 'string', // layout string to use to render children of a collection childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template) childDontRegisterViews: 'boolean', // whether views made of this document are registered so that they can be found when drawing links onChildClick: ScriptField, // script to run for each child when its clicked @@ -113,4 +113,5 @@ export const collectionSchema = createSchema({ }); export type Document = makeInterface<[typeof documentSchema]>; +// eslint-disable-next-line no-redeclare export const Document = makeInterface(documentSchema); |
