-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.ts
137 lines (110 loc) · 4.33 KB
/
index.ts
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
125
126
127
128
129
130
131
132
133
134
135
136
137
/// <reference types="@nodecg/types/augment-window" />
import NodeCG from '@nodecg/types';
import clone from 'clone';
import { computed, DeepReadonly, isRef, reactive, readonly, ref, Ref, watch } from 'vue-demi';
interface ReactiveReplicant<T> {
data: T | undefined,
oldData: DeepReadonly<T | undefined>,
changed: boolean,
save: () => void,
revert: () => void,
loadDefault: () => void
}
export function useReplicant<T>(name: string, namespace: string | undefined, opts: NodeCG.Replicant.Options<T> = {}) {
return useReplicantRaw(name, namespace, opts).reactiveReplicant
}
function useReplicantRaw<T>(name: string, namespace: string | NodeCG.Replicant.Options<T> | undefined, opts: NodeCG.Replicant.Options<T> | undefined) {
if (isRef(name)) {
console.warn(`Tried to create a StaticReplicant using a reactive name (${name.value})`)
throw Error(`Tried to create a StaticReplicant using a reactive name (${name.value})`)
}
if (opts === undefined && typeof namespace !== 'string') {
opts = namespace;
}
let rep = typeof namespace === 'string' ? nodecg.Replicant<T>(name, namespace, opts) : nodecg.Replicant<T>(name, opts)
// Vue wants to unwrap nested objects when declared in refs
const newVal = ref<T>();
const oldVal = ref<T>();
newVal.value = clone(opts && "defaultValue" in opts ? opts.defaultValue : undefined)
oldVal.value = clone(opts && "defaultValue" in opts ? opts.defaultValue : undefined)
const changed = ref(false)
const upToDate = ref(true)
function checkChanged() {
changed.value = JSON.stringify(newVal.value) !== JSON.stringify(oldVal.value)
}
watch(newVal, checkChanged, { deep: true })
const listener = (newRepVal: T | undefined, oldRepVal: T | undefined) => {
oldVal.value = clone(newRepVal)
if (!oldRepVal) changed.value = false
if (changed.value) {
checkChanged()
upToDate.value = !changed.value
} else {
newVal.value = clone(oldVal.value)
upToDate.value = true
}
}
rep.on('change', listener)
function save() {
rep.value = newVal.value
}
function revert() {
newVal.value = clone(oldVal.value)
}
function loadDefault() {
newVal.value = clone(opts && "defaultValue" in opts ? opts.defaultValue : undefined)
}
const reactiveReplicant: ReactiveReplicant<T> = reactive({
data: newVal,
oldData: readonly(oldVal),
changed,
save,
revert,
loadDefault
})
return {
reactiveReplicant,
listener
}
}
export function useDynamicReplicant<T>(name: Ref<string>, namespace: string, opts: NodeCG.Replicant.Options<T> | undefined) {
if (!isRef(name)) {
console.warn(`Tried to create a DynamicReplicant using a static name (${name})`)
throw Error(`Tried to create a DynamicReplicant using a static name (${name})`)
}
const repRef: Ref<{ reactiveReplicant: ReactiveReplicant<T>, listener: (newRepVal: T | undefined, oldRepVal: T | undefined) => void } | null> = ref(null)
function setReplicant(oldName?: string) {
// remove old listeners when we change the name to prevent potential memory leaks
if (oldName && repRef.value) nodecg.Replicant<T>(oldName).removeListener('change', repRef.value.listener)
if (!(typeof name.value === 'string' || typeof name.value === 'number')) {
repRef.value = null
} else {
repRef.value = useReplicantRaw<T>(name.value, namespace, opts)
}
}
setReplicant()
watch(name, (_, oldName) => {
setReplicant(oldName)
})
return computed(() => repRef.value?.reactiveReplicant)
}
export function useAssetReplicant(name: string, namespace: string) {
const rep = nodecg.Replicant<NodeCG.AssetFile[]>(`assets:${name}`, namespace)
const newVal: Ref<NodeCG.AssetFile[]> = ref([])
rep.on('change', (newRepVal: NodeCG.AssetFile[] | null | undefined) => {
newVal.value = clone(newRepVal ?? [])
})
return newVal
}
/**
* @deprecated Renamed to useReplicant
*/
export const ReactiveReplicant = useReplicant
/**
* @deprecated Renamed to useDynamicReplicant
*/
export const DynamicReactiveReplicant = useDynamicReplicant
/**
* @deprecated Renamed to useAssetReplicant
*/
export const AssetReplicant = useAssetReplicant