@sentroy-co/client-sdk 2.5.0 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-upload-queue.d.ts","sourceRoot":"","sources":["../../../src/react/lib/use-upload-queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AACpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAExC;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAA;IAC9D,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,4CAA4C;IAC5C,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,0DAA0D;IAC1D,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAA;IACpD,yEAAyE;IACzE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,uEAAuE;IACvE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,sDAAsD;IACtD,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,EACf,IAAI,GAAE,qBAA0B,GAC/B,oBAAoB,
|
|
1
|
+
{"version":3,"file":"use-upload-queue.d.ts","sourceRoot":"","sources":["../../../src/react/lib/use-upload-queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AACpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAExC;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAA;IAC9D,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,4CAA4C;IAC5C,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,0DAA0D;IAC1D,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAA;IACpD,yEAAyE;IACzE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,uEAAuE;IACvE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,sDAAsD;IACtD,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,EACf,IAAI,GAAE,qBAA0B,GAC/B,oBAAoB,CAgMtB"}
|
|
@@ -20,61 +20,86 @@ function useUploadQueue(client, opts = {}) {
|
|
|
20
20
|
onUploadedRef.current = opts.onUploaded;
|
|
21
21
|
// Worker pump — queued entry varsa ve aktif < concurrency ise başlat.
|
|
22
22
|
const pumpRef = (0, react_1.useRef)(() => { });
|
|
23
|
+
/**
|
|
24
|
+
* Aktif (in-flight) upload sayısını synchronous olarak takip eden ref.
|
|
25
|
+
* `entries` state setEntries ile async güncellendiğinden, recursive
|
|
26
|
+
* `pump` çağrılarında `entries.filter(e => e.status === "uploading")`
|
|
27
|
+
* eski listeyi görür → aynı queued entry birden çok kez başlatılır
|
|
28
|
+
* (Chrome side `ERR_INSUFFICIENT_RESOURCES`). Ref ile incre/decre
|
|
29
|
+
* synchronous; concurrency limiti gerçekten devreye girer.
|
|
30
|
+
*/
|
|
31
|
+
const inFlightRef = (0, react_1.useRef)(0);
|
|
32
|
+
/**
|
|
33
|
+
* Henüz başlatılmamış queued entry id'lerinin sıralı listesi. setEntries
|
|
34
|
+
* async olduğu için listeden seçim yapmak yarış koşulu üretir; ref
|
|
35
|
+
* üzerinden FIFO push/shift hem deterministik hem hızlı.
|
|
36
|
+
*/
|
|
37
|
+
const queueRef = (0, react_1.useRef)([]);
|
|
23
38
|
const updateEntry = (0, react_1.useCallback)((id, patch) => {
|
|
24
39
|
setEntries((prev) => prev.map((e) => (e.id === id ? { ...e, ...patch } : e)));
|
|
25
40
|
}, []);
|
|
26
41
|
pumpRef.current = () => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (active >= concurrency)
|
|
42
|
+
// Slot doluysa veya queue boşsa erken dön.
|
|
43
|
+
if (inFlightRef.current >= concurrency)
|
|
30
44
|
return;
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
45
|
+
const nextId = queueRef.current.shift();
|
|
46
|
+
if (!nextId)
|
|
33
47
|
return;
|
|
48
|
+
// Slot'u synchronously rezerve et — bir sonraki pump çağrısı bu
|
|
49
|
+
// entry'i tekrar shift edemez.
|
|
50
|
+
inFlightRef.current++;
|
|
51
|
+
const entry = entriesRef.current.find((e) => e.id === nextId);
|
|
52
|
+
if (!entry) {
|
|
53
|
+
// Cancel öncesi remove edilmiş; slot'u iade et ve pump'a devam.
|
|
54
|
+
inFlightRef.current--;
|
|
55
|
+
pumpRef.current?.();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
34
58
|
// Mark uploading
|
|
35
|
-
updateEntry(
|
|
36
|
-
|
|
37
|
-
// gerekmez); enqueue closure'unda capture edilir, ayrı bucket map.
|
|
38
|
-
const bucketSlug = bucketMapRef.current[next.id];
|
|
59
|
+
updateEntry(entry.id, { status: "uploading" });
|
|
60
|
+
const bucketSlug = bucketMapRef.current[entry.id];
|
|
39
61
|
if (!bucketSlug) {
|
|
40
|
-
updateEntry(
|
|
62
|
+
updateEntry(entry.id, { status: "error", error: "No bucket" });
|
|
63
|
+
inFlightRef.current--;
|
|
41
64
|
pumpRef.current?.();
|
|
42
65
|
return;
|
|
43
66
|
}
|
|
44
67
|
const controller = new AbortController();
|
|
45
|
-
cancelMapRef.current[
|
|
68
|
+
cancelMapRef.current[entry.id] = () => controller.abort();
|
|
46
69
|
client.media
|
|
47
|
-
.upload(bucketSlug, { body:
|
|
70
|
+
.upload(bucketSlug, { body: entry.file, filename: entry.file.name }, {
|
|
48
71
|
onProgress: (loaded, total) => {
|
|
49
|
-
updateEntry(
|
|
72
|
+
updateEntry(entry.id, { loaded, total });
|
|
50
73
|
},
|
|
51
74
|
signal: controller.signal,
|
|
52
75
|
})
|
|
53
76
|
.then((media) => {
|
|
54
|
-
updateEntry(
|
|
77
|
+
updateEntry(entry.id, {
|
|
55
78
|
status: "done",
|
|
56
79
|
media,
|
|
57
|
-
loaded:
|
|
58
|
-
total:
|
|
80
|
+
loaded: entry.file.size,
|
|
81
|
+
total: entry.file.size,
|
|
59
82
|
});
|
|
60
83
|
onUploadedRef.current?.(media);
|
|
61
84
|
})
|
|
62
85
|
.catch((err) => {
|
|
63
86
|
const aborted = err?.message === "Upload aborted";
|
|
64
|
-
updateEntry(
|
|
87
|
+
updateEntry(entry.id, {
|
|
65
88
|
status: aborted ? "canceled" : "error",
|
|
66
89
|
error: aborted
|
|
67
90
|
? undefined
|
|
68
|
-
: err?.message ?? "Upload failed",
|
|
91
|
+
: (err?.message ?? "Upload failed"),
|
|
69
92
|
});
|
|
70
93
|
})
|
|
71
94
|
.finally(() => {
|
|
72
|
-
delete cancelMapRef.current[
|
|
73
|
-
|
|
95
|
+
delete cancelMapRef.current[entry.id];
|
|
96
|
+
inFlightRef.current--;
|
|
97
|
+
// Slot açıldı, sıradakini başlat.
|
|
74
98
|
pumpRef.current?.();
|
|
75
99
|
});
|
|
76
|
-
// Aynı tick'te
|
|
77
|
-
|
|
100
|
+
// Aynı tick'te kalan slot'ları doldur — concurrency artık
|
|
101
|
+
// synchronously inFlightRef ile guard'lı, çift başlatma yok.
|
|
102
|
+
if (inFlightRef.current < concurrency)
|
|
78
103
|
pumpRef.current?.();
|
|
79
104
|
};
|
|
80
105
|
const bucketMapRef = (0, react_1.useRef)({});
|
|
@@ -85,6 +110,9 @@ function useUploadQueue(client, opts = {}) {
|
|
|
85
110
|
const newEntries = files.map((file) => {
|
|
86
111
|
const id = `up-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
87
112
|
bucketMapRef.current[id] = bucketSlug;
|
|
113
|
+
// FIFO queue — pump bu sıradan shift eder; setEntries async
|
|
114
|
+
// güncellemesinden bağımsız synchronous source-of-truth.
|
|
115
|
+
queueRef.current.push(id);
|
|
88
116
|
return {
|
|
89
117
|
id,
|
|
90
118
|
file,
|
|
@@ -95,28 +123,36 @@ function useUploadQueue(client, opts = {}) {
|
|
|
95
123
|
const c = cancelMapRef.current[id];
|
|
96
124
|
if (c)
|
|
97
125
|
c();
|
|
98
|
-
else
|
|
126
|
+
else {
|
|
127
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id);
|
|
99
128
|
setEntries((prev) => prev.map((e) => e.id === id && e.status === "queued"
|
|
100
129
|
? { ...e, status: "canceled" }
|
|
101
130
|
: e));
|
|
131
|
+
}
|
|
102
132
|
},
|
|
103
133
|
};
|
|
104
134
|
});
|
|
105
135
|
setEntries((prev) => [...prev, ...newEntries]);
|
|
106
|
-
// pump on next tick — state update'ten sonra entriesRef güncel olsun
|
|
136
|
+
// pump on next tick — state update'ten sonra entriesRef güncel olsun.
|
|
137
|
+
// pumpRef kendi içinde sequential pump zincirini sürdürür (her
|
|
138
|
+
// başarılı slot rezervasyonundan sonra bir dahaki pump'ı çağırır),
|
|
139
|
+
// dolayısıyla burada tek tetikleme yeterli.
|
|
107
140
|
Promise.resolve().then(() => pumpRef.current?.());
|
|
108
141
|
}, []);
|
|
109
142
|
const cancel = (0, react_1.useCallback)((id) => {
|
|
110
143
|
const c = cancelMapRef.current[id];
|
|
111
144
|
if (c)
|
|
112
145
|
c();
|
|
113
|
-
else
|
|
146
|
+
else {
|
|
147
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id);
|
|
114
148
|
setEntries((prev) => prev.map((e) => e.id === id && e.status === "queued"
|
|
115
149
|
? { ...e, status: "canceled" }
|
|
116
150
|
: e));
|
|
151
|
+
}
|
|
117
152
|
}, []);
|
|
118
153
|
const remove = (0, react_1.useCallback)((id) => {
|
|
119
154
|
setEntries((prev) => prev.filter((e) => e.id !== id));
|
|
155
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id);
|
|
120
156
|
delete bucketMapRef.current[id];
|
|
121
157
|
delete cancelMapRef.current[id];
|
|
122
158
|
}, []);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-upload-queue.js","sourceRoot":"","sources":["../../../src/react/lib/use-upload-queue.ts"],"names":[],"mappings":";;AAsDA,
|
|
1
|
+
{"version":3,"file":"use-upload-queue.js","sourceRoot":"","sources":["../../../src/react/lib/use-upload-queue.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -66,6 +66,22 @@ export function useUploadQueue(
|
|
|
66
66
|
// Worker pump — queued entry varsa ve aktif < concurrency ise başlat.
|
|
67
67
|
const pumpRef = useRef<() => void>(() => {})
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Aktif (in-flight) upload sayısını synchronous olarak takip eden ref.
|
|
71
|
+
* `entries` state setEntries ile async güncellendiğinden, recursive
|
|
72
|
+
* `pump` çağrılarında `entries.filter(e => e.status === "uploading")`
|
|
73
|
+
* eski listeyi görür → aynı queued entry birden çok kez başlatılır
|
|
74
|
+
* (Chrome side `ERR_INSUFFICIENT_RESOURCES`). Ref ile incre/decre
|
|
75
|
+
* synchronous; concurrency limiti gerçekten devreye girer.
|
|
76
|
+
*/
|
|
77
|
+
const inFlightRef = useRef(0)
|
|
78
|
+
/**
|
|
79
|
+
* Henüz başlatılmamış queued entry id'lerinin sıralı listesi. setEntries
|
|
80
|
+
* async olduğu için listeden seçim yapmak yarış koşulu üretir; ref
|
|
81
|
+
* üzerinden FIFO push/shift hem deterministik hem hızlı.
|
|
82
|
+
*/
|
|
83
|
+
const queueRef = useRef<string[]>([])
|
|
84
|
+
|
|
69
85
|
const updateEntry = useCallback(
|
|
70
86
|
(id: string, patch: Partial<UploadEntry>) => {
|
|
71
87
|
setEntries((prev) =>
|
|
@@ -76,65 +92,77 @@ export function useUploadQueue(
|
|
|
76
92
|
)
|
|
77
93
|
|
|
78
94
|
pumpRef.current = () => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
// Slot doluysa veya queue boşsa erken dön.
|
|
96
|
+
if (inFlightRef.current >= concurrency) return
|
|
97
|
+
const nextId = queueRef.current.shift()
|
|
98
|
+
if (!nextId) return
|
|
99
|
+
|
|
100
|
+
// Slot'u synchronously rezerve et — bir sonraki pump çağrısı bu
|
|
101
|
+
// entry'i tekrar shift edemez.
|
|
102
|
+
inFlightRef.current++
|
|
103
|
+
|
|
104
|
+
const entry = entriesRef.current.find((e) => e.id === nextId)
|
|
105
|
+
if (!entry) {
|
|
106
|
+
// Cancel öncesi remove edilmiş; slot'u iade et ve pump'a devam.
|
|
107
|
+
inFlightRef.current--
|
|
108
|
+
pumpRef.current?.()
|
|
109
|
+
return
|
|
110
|
+
}
|
|
84
111
|
|
|
85
112
|
// Mark uploading
|
|
86
|
-
updateEntry(
|
|
113
|
+
updateEntry(entry.id, { status: "uploading" })
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
// gerekmez); enqueue closure'unda capture edilir, ayrı bucket map.
|
|
90
|
-
const bucketSlug = bucketMapRef.current[next.id]
|
|
115
|
+
const bucketSlug = bucketMapRef.current[entry.id]
|
|
91
116
|
if (!bucketSlug) {
|
|
92
|
-
updateEntry(
|
|
117
|
+
updateEntry(entry.id, { status: "error", error: "No bucket" })
|
|
118
|
+
inFlightRef.current--
|
|
93
119
|
pumpRef.current?.()
|
|
94
120
|
return
|
|
95
121
|
}
|
|
96
122
|
|
|
97
123
|
const controller = new AbortController()
|
|
98
|
-
cancelMapRef.current[
|
|
124
|
+
cancelMapRef.current[entry.id] = () => controller.abort()
|
|
99
125
|
|
|
100
126
|
client.media
|
|
101
127
|
.upload(
|
|
102
128
|
bucketSlug,
|
|
103
|
-
{ body:
|
|
129
|
+
{ body: entry.file, filename: entry.file.name },
|
|
104
130
|
{
|
|
105
131
|
onProgress: (loaded, total) => {
|
|
106
|
-
updateEntry(
|
|
132
|
+
updateEntry(entry.id, { loaded, total })
|
|
107
133
|
},
|
|
108
134
|
signal: controller.signal,
|
|
109
135
|
},
|
|
110
136
|
)
|
|
111
137
|
.then((media) => {
|
|
112
|
-
updateEntry(
|
|
138
|
+
updateEntry(entry.id, {
|
|
113
139
|
status: "done",
|
|
114
140
|
media,
|
|
115
|
-
loaded:
|
|
116
|
-
total:
|
|
141
|
+
loaded: entry.file.size,
|
|
142
|
+
total: entry.file.size,
|
|
117
143
|
})
|
|
118
144
|
onUploadedRef.current?.(media)
|
|
119
145
|
})
|
|
120
146
|
.catch((err: unknown) => {
|
|
121
147
|
const aborted =
|
|
122
148
|
(err as { message?: string })?.message === "Upload aborted"
|
|
123
|
-
updateEntry(
|
|
149
|
+
updateEntry(entry.id, {
|
|
124
150
|
status: aborted ? "canceled" : "error",
|
|
125
151
|
error: aborted
|
|
126
152
|
? undefined
|
|
127
|
-
: (err as Error)?.message ?? "Upload failed",
|
|
153
|
+
: ((err as Error)?.message ?? "Upload failed"),
|
|
128
154
|
})
|
|
129
155
|
})
|
|
130
156
|
.finally(() => {
|
|
131
|
-
delete cancelMapRef.current[
|
|
132
|
-
|
|
157
|
+
delete cancelMapRef.current[entry.id]
|
|
158
|
+
inFlightRef.current--
|
|
159
|
+
// Slot açıldı, sıradakini başlat.
|
|
133
160
|
pumpRef.current?.()
|
|
134
161
|
})
|
|
135
162
|
|
|
136
|
-
// Aynı tick'te
|
|
137
|
-
|
|
163
|
+
// Aynı tick'te kalan slot'ları doldur — concurrency artık
|
|
164
|
+
// synchronously inFlightRef ile guard'lı, çift başlatma yok.
|
|
165
|
+
if (inFlightRef.current < concurrency) pumpRef.current?.()
|
|
138
166
|
}
|
|
139
167
|
|
|
140
168
|
const bucketMapRef = useRef<Record<string, string>>({})
|
|
@@ -146,6 +174,9 @@ export function useUploadQueue(
|
|
|
146
174
|
const newEntries: UploadEntry[] = files.map((file) => {
|
|
147
175
|
const id = `up-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
148
176
|
bucketMapRef.current[id] = bucketSlug
|
|
177
|
+
// FIFO queue — pump bu sıradan shift eder; setEntries async
|
|
178
|
+
// güncellemesinden bağımsız synchronous source-of-truth.
|
|
179
|
+
queueRef.current.push(id)
|
|
149
180
|
return {
|
|
150
181
|
id,
|
|
151
182
|
file,
|
|
@@ -155,7 +186,8 @@ export function useUploadQueue(
|
|
|
155
186
|
cancel: () => {
|
|
156
187
|
const c = cancelMapRef.current[id]
|
|
157
188
|
if (c) c()
|
|
158
|
-
else
|
|
189
|
+
else {
|
|
190
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id)
|
|
159
191
|
setEntries((prev) =>
|
|
160
192
|
prev.map((e) =>
|
|
161
193
|
e.id === id && e.status === "queued"
|
|
@@ -163,11 +195,15 @@ export function useUploadQueue(
|
|
|
163
195
|
: e,
|
|
164
196
|
),
|
|
165
197
|
)
|
|
198
|
+
}
|
|
166
199
|
},
|
|
167
200
|
}
|
|
168
201
|
})
|
|
169
202
|
setEntries((prev) => [...prev, ...newEntries])
|
|
170
|
-
// pump on next tick — state update'ten sonra entriesRef güncel olsun
|
|
203
|
+
// pump on next tick — state update'ten sonra entriesRef güncel olsun.
|
|
204
|
+
// pumpRef kendi içinde sequential pump zincirini sürdürür (her
|
|
205
|
+
// başarılı slot rezervasyonundan sonra bir dahaki pump'ı çağırır),
|
|
206
|
+
// dolayısıyla burada tek tetikleme yeterli.
|
|
171
207
|
Promise.resolve().then(() => pumpRef.current?.())
|
|
172
208
|
},
|
|
173
209
|
[],
|
|
@@ -176,7 +212,8 @@ export function useUploadQueue(
|
|
|
176
212
|
const cancel = useCallback((id: string) => {
|
|
177
213
|
const c = cancelMapRef.current[id]
|
|
178
214
|
if (c) c()
|
|
179
|
-
else
|
|
215
|
+
else {
|
|
216
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id)
|
|
180
217
|
setEntries((prev) =>
|
|
181
218
|
prev.map((e) =>
|
|
182
219
|
e.id === id && e.status === "queued"
|
|
@@ -184,10 +221,12 @@ export function useUploadQueue(
|
|
|
184
221
|
: e,
|
|
185
222
|
),
|
|
186
223
|
)
|
|
224
|
+
}
|
|
187
225
|
}, [])
|
|
188
226
|
|
|
189
227
|
const remove = useCallback((id: string) => {
|
|
190
228
|
setEntries((prev) => prev.filter((e) => e.id !== id))
|
|
229
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id)
|
|
191
230
|
delete bucketMapRef.current[id]
|
|
192
231
|
delete cancelMapRef.current[id]
|
|
193
232
|
}, [])
|