-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstate.ts
More file actions
126 lines (100 loc) · 3.52 KB
/
Copy pathstate.ts
File metadata and controls
126 lines (100 loc) · 3.52 KB
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
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
type StreamingState = {
isStreaming: boolean;
isSendingCustomStreamPreview: boolean;
lastStreamPreviewSend: number;
resendStreamPreviewIntervalId: number | null;
};
type Listener<T> = (value: T) => void;
class CustomStreamPreviewStateManager {
private state: StreamingState = {
isStreaming: false,
isSendingCustomStreamPreview: false,
lastStreamPreviewSend: 0,
resendStreamPreviewIntervalId: null,
};
private listeners = new Set<() => void>();
private fieldListeners: Partial<{
[K in keyof StreamingState]: Set<Listener<any>>;
}> = {};
private selectorListeners = new Set<{
selector: (state: StreamingState) => any;
prevValue: any;
callback: (value: any) => void;
}>();
getState(): StreamingState {
return { ...this.state };
}
setState(partial: Partial<StreamingState>): void {
const prevState = this.state;
const newState = { ...this.state, ...partial };
this.state = newState;
const changed = Object.keys(partial).some(
key => (newState as any)[key] !== (prevState as any)[key]
);
if (!changed) return;
this.listeners.forEach(fn => fn());
for (const key in partial) {
const k = key as keyof StreamingState;
const newVal = newState[k];
const oldVal = prevState[k];
if (newVal !== oldVal) {
const listeners = this.fieldListeners[k];
if (listeners) {
listeners.forEach(fn => fn(newVal));
}
}
}
for (const entry of this.selectorListeners) {
const next = entry.selector(newState);
if (next !== entry.prevValue) {
entry.prevValue = next;
entry.callback(next);
}
}
}
subscribe(callback: (state: StreamingState) => void): () => void {
const wrapper = () => callback(this.getState());
this.listeners.add(wrapper);
return () => this.listeners.delete(wrapper);
}
subscribeToField<K extends keyof StreamingState>(
field: K,
callback: (value: StreamingState[K]) => void
): () => void {
const listeners = this.getOrCreateFieldListeners(field);
listeners.add(callback);
return () => listeners.delete(callback);
}
subscribeWithSelector<T>(
selector: (state: StreamingState) => T,
callback: (value: T) => void
): () => void {
const entry = {
selector,
prevValue: selector(this.state),
callback
};
this.selectorListeners.add(entry);
return () => this.selectorListeners.delete(entry);
}
reset(): void {
this.setState({
isStreaming: false,
isSendingCustomStreamPreview: false,
lastStreamPreviewSend: 0,
resendStreamPreviewIntervalId: null,
});
}
private getOrCreateFieldListeners<K extends keyof StreamingState>(field: K): Set<Listener<StreamingState[K]>> {
if (!this.fieldListeners[field]) {
this.fieldListeners[field] = new Set<Listener<StreamingState[K]>>() as Set<Listener<any>>;
}
return this.fieldListeners[field]! as Set<Listener<StreamingState[K]>>;
}
}
export const CustomStreamPreviewState = new CustomStreamPreviewStateManager();