aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/LinkManager.ts2
-rw-r--r--src/fields/List.ts20
-rw-r--r--src/fields/Proxy.ts68
3 files changed, 37 insertions, 53 deletions
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index eacfa5506..588664dec 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -65,7 +65,7 @@ export class LinkManager {
};
const watchUserLinkDB = (userLinkDBDoc: Doc) => {
LinkManager.links.push(...DocListCast(userLinkDBDoc.data));
- const toRealField = (field: Field) => (field instanceof ProxyField ? field.value() : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
+ const toRealField = (field: Field) => (field instanceof ProxyField ? field.value : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
if (userLinkDBDoc.data) {
observe(
userLinkDBDoc.data,
diff --git a/src/fields/List.ts b/src/fields/List.ts
index edaa16003..1e1adc7a8 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -210,7 +210,7 @@ function toObjectField(field: Field) {
}
function toRealField(field: Field) {
- return field instanceof ProxyField ? field.value() : field;
+ return field instanceof ProxyField ? field.value : field;
}
function listGetter(target: any, prop: string | number | symbol, receiver: any): any {
@@ -271,25 +271,17 @@ class ListImpl<T extends Field> extends ObjectField {
// this requests all ProxyFields at the same time to avoid the overhead
// of separate network requests and separate updates to the React dom.
private __realFields() {
- const promised = this.__fields.filter(f => f instanceof ProxyField && f.promisedValue()).map(f => ({ field: f as any, promisedFieldId: f instanceof ProxyField ? f.promisedValue() : '' }));
+ const unrequested = this.__fields.filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
// if we find any ProxyFields that don't have a current value, then
// start the server request for all of them
- if (promised.length) {
- const batchPromise = DocServer.GetRefFields(promised.map(p => p.promisedFieldId));
+ if (unrequested.length) {
+ const batchPromise = DocServer.GetRefFields(unrequested.map(p => p.fieldId));
// as soon as we get the fields from the server, set all the list values in one
// action to generate one React dom update.
- batchPromise.then(
- action(pfields => {
- for (let i = 0; i < promised.length; i++) {
- promised[i].field.setValue(pfields[promised[i].promisedFieldId]);
- }
- })
- );
+ const allSetPromise = batchPromise.then(action(pfields => unrequested.map(toReq => toReq.setValue(pfields[toReq.fieldId]))));
// we also have to mark all lists items with this promise so that any calls to them
// will await the batch request and return the requested field value.
- // This assumes the handler for 'promise' in the call above being invoked before the
- // handler for 'promise' in the lines below.
- promised.forEach(p => p.field.setPromise(batchPromise.then(pfields => pfields[p.promisedFieldId])));
+ unrequested.forEach(p => p.setExternalValuePromise(allSetPromise));
}
return this.__fields.map(toRealField);
}
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 3aafacd96..1266e6e3c 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -1,7 +1,7 @@
import { Deserializable } from '../client/util/SerializationHelper';
-import { FieldWaiting } from './Doc';
+import { FieldWaiting, Opt } from './Doc';
import { primitive, serializable } from 'serializr';
-import { observable, action, runInAction } from 'mobx';
+import { observable, action, runInAction, computed } from 'mobx';
import { DocServer } from '../client/DocServer';
import { RefField } from './RefField';
import { ObjectField } from './ObjectField';
@@ -10,8 +10,8 @@ import { scriptingGlobal } from '../client/util/ScriptingGlobals';
import { Plugins } from './util';
function deserializeProxy(field: any) {
- if (!field.cache) {
- field.cache = DocServer.GetCachedRefField(field.fieldId) as any;
+ if (!field.cache.field) {
+ field.cache = { field: DocServer.GetCachedRefField(field.fieldId) as any, p: undefined };
}
}
@Deserializable('proxy', deserializeProxy)
@@ -25,13 +25,13 @@ export class ProxyField<T extends RefField> extends ObjectField {
//this.cache = DocServer.GetCachedRefField(value) as any;
this.fieldId = value;
} else if (value) {
- this.cache = value;
+ this.cache = { field: value, p: undefined };
this.fieldId = value[Id];
}
}
[Copy]() {
- if (this.cache) return new ProxyField<T>(this.cache);
+ if (this.cache.field) return new ProxyField<T>(this.cache.field);
return new ProxyField<T>(this.fieldId);
}
@@ -48,48 +48,40 @@ export class ProxyField<T extends RefField> extends ObjectField {
// This getter/setter and nested object thing is
// because mobx doesn't play well with observable proxies
@observable.ref
- private _cache: { readonly field: T | undefined } = { field: undefined };
- private get cache(): T | undefined {
- return this._cache.field;
+ private _cache: { readonly field: T | undefined; p: FieldWaiting<T> | undefined } = { field: undefined, p: undefined };
+ private get cache(): { field: T | undefined; p: FieldWaiting<T> | undefined } {
+ return this._cache;
}
- private set cache(field: T | undefined) {
- this._cache = { field };
+ private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
+ runInAction(() => (this._cache = { ...val }));
}
private failed = false;
- private promise?: Promise<any>;
- @action
- value(): T | undefined | FieldWaiting<T> {
- if (this.cache) return this.cache;
+ @computed get value(): T | undefined | FieldWaiting<T> {
+ if (this.cache.field) return this.cache.field;
if (this.failed) return undefined;
- const cached = DocServer.GetCachedRefField(this.fieldId) as T;
- if (cached !== undefined) {
- //setTimeout(action(() => (this.cache = cached))); // can't do this because it triggers too many invalidations while rendering.
- } else if (!this.promise) {
- this.promise = DocServer.GetRefField(this.fieldId).then(
- action((field: any) => {
- this.promise = undefined;
- this.cache = field;
- this.failed = field === undefined;
- return field;
- })
- ) as FieldWaiting<T>;
+ this.cache.field = DocServer.GetCachedRefField(this.fieldId) as T;
+ if (!this.cache.field && !this.cache.p) {
+ this.cache = {
+ field: undefined,
+ p: DocServer.GetRefField(this.fieldId).then(val => this.setValue(val as T)) as FieldWaiting<T>,
+ };
}
- return cached ?? this.promise;
+ return this.cache.field ?? this.cache.p;
}
- promisedValue(): string {
- return !this.cache && !this.failed && !this.promise && !DocServer.GetCachedRefField(this.fieldId) ? this.fieldId : '';
+ @computed get needsRequesting(): boolean {
+ return !this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId) ? true : false;
}
- setPromise(promise: any) {
- this.promise = promise;
+
+ setExternalValuePromise(externalValuePromise: Promise<any>) {
+ this.cache.p = externalValuePromise.then(() => this.value) as FieldWaiting<T>;
}
@action
- setValue(field: any) {
- this.promise = undefined;
- this.cache = field;
- if (field === undefined) this.failed = true;
+ setValue(field: Opt<T>) {
+ this.cache = { field, p: undefined };
+ this.failed = field === undefined;
return field;
}
}
@@ -116,14 +108,14 @@ export namespace ProxyField {
export function initPlugin() {
Plugins.addGetterPlugin((doc, _, value) => {
if (useProxy && value instanceof ProxyField) {
- return { value: value.value() };
+ return { value: value.value };
}
});
}
}
function prefetchValue(proxy: PrefetchProxy<RefField>) {
- return proxy.value() as any;
+ return proxy.value as any;
}
@scriptingGlobal