aboutsummaryrefslogtreecommitdiff
path: root/src/fields/Proxy.ts
blob: 07553f17c8688dcae6bb5adacd93fb072ac3d710 (plain)
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
import { Deserializable } from "../client/util/SerializationHelper";
import { FieldWaiting } from "./Doc";
import { primitive, serializable } from "serializr";
import { observable, action, runInAction } from "mobx";
import { DocServer } from "../client/DocServer";
import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols";
import { scriptingGlobal } from "../client/util/Scripting";
import { Plugins } from "./util";

@Deserializable("proxy")
export class ProxyField<T extends RefField> extends ObjectField {
    constructor();
    constructor(value: T);
    constructor(fieldId: string);
    constructor(value?: T | string) {
        super();
        this.fieldId = typeof value === "string" ? value : (value?.[Id] ?? "");
    }

    [Copy]() { return new ProxyField<T>((this.cache ?? this.fieldId) as T); }
    [ToScriptString]() { return "invalid"; }
    [ToString]() { return "ProxyField"; }

    @serializable(primitive())
    readonly fieldId: string = "";
    private failed = false;
    private promise?: Promise<any>;

    private get cache(): T | undefined { return DocServer.GetCachedRefField(this.fieldId) as T }

    value(): T | undefined | FieldWaiting<T> {
        if (this.cache) return this.cache;
        if (this.failed) return undefined;
        if (!this.promise) {
            this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => {
                this.promise = undefined;
                if (field === undefined) this.failed = true;
                return field;
            }));
        }
        return this.promise as any;
    }
    promisedValue(): string { return !this.cache && !this.failed && !this.promise ? this.fieldId : ""; }
    setPromise(promise: any) {
        if (this.cache === undefined) this.promise = promise;
    }
}

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;
}

@scriptingGlobal
@Deserializable("prefetch_proxy", prefetchValue)
export class PrefetchProxy<T extends RefField> extends ProxyField<T> {
}