1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
import { Deserializable } from '../client/util/SerializationHelper';
import { Field, FieldWaiting, Opt } from './Doc';
import { primitive, serializable } from 'serializr';
import { observable, action, runInAction, computed } from 'mobx';
import { DocServer } from '../client/DocServer';
import { RefField } from './RefField';
import { ObjectField } from './ObjectField';
import { Id, Copy, ToScriptString, ToString, ToValue } from './FieldSymbols';
import { scriptingGlobal } from '../client/util/ScriptingGlobals';
function deserializeProxy(field: any) {
if (!field.cache.field) {
field.cache = { field: DocServer.GetCachedRefField(field.fieldId) as any, p: undefined };
}
}
@Deserializable('proxy', deserializeProxy)
export class ProxyField<T extends RefField> extends ObjectField {
constructor();
constructor(value: T);
constructor(fieldId: string);
constructor(value?: T | string) {
super();
if (typeof value === 'string') {
//this.cache = DocServer.GetCachedRefField(value) as any;
this.fieldId = value;
} else if (value) {
this.cache = { field: value, p: undefined };
this.fieldId = value[Id];
}
}
[ToValue](doc: any) {
return ProxyField.toValue(this);
}
[Copy]() {
if (this.cache.field) return new ProxyField<T>(this.cache.field);
return new ProxyField<T>(this.fieldId);
}
[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' ?
}
[ToString]() {
return 'ProxyField';
}
@serializable(primitive())
readonly fieldId: string = '';
// 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; p: FieldWaiting<T> | undefined } = { field: undefined, p: undefined };
private get cache(): { field: T | undefined; p: FieldWaiting<T> | undefined } {
return this._cache;
}
private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
runInAction(() => (this._cache = { ...val }));
}
private failed = false;
@computed get value(): T | undefined | FieldWaiting<T> {
if (this.cache.field) return this.cache.field;
if (this.failed) return undefined;
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 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;
}
setExternalValuePromise(externalValuePromise: Promise<any>) {
this.cache.p = externalValuePromise.then(() => this.value) as FieldWaiting<T>;
}
@action
setValue(field: Opt<T>) {
this.cache = { field, p: undefined };
this.failed = field === undefined;
return field;
}
}
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 toValue(value: any) {
if (useProxy) {
return { value: value.value };
}
}
}
function prefetchValue(proxy: PrefetchProxy<RefField>) {
return proxy.value as any;
}
@scriptingGlobal
@Deserializable('prefetch_proxy', prefetchValue)
export class PrefetchProxy<T extends RefField> extends ProxyField<T> {}
|