@sentroy-co/client-sdk 2.4.5 → 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.
- package/dist/http.d.ts +13 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +62 -0
- package/dist/http.js.map +1 -1
- package/dist/react/MediaManager.d.ts +14 -0
- package/dist/react/MediaManager.d.ts.map +1 -1
- package/dist/react/MediaManager.js +21 -14
- package/dist/react/MediaManager.js.map +1 -1
- package/dist/react/crop/CropDialog.d.ts +15 -0
- package/dist/react/crop/CropDialog.d.ts.map +1 -0
- package/dist/react/crop/CropDialog.js +126 -0
- package/dist/react/crop/CropDialog.js.map +1 -0
- package/dist/react/crop/index.d.ts +7 -0
- package/dist/react/crop/index.d.ts.map +1 -0
- package/dist/react/crop/index.js +11 -0
- package/dist/react/crop/index.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/UploadQueuePanel.d.ts +20 -0
- package/dist/react/lib/UploadQueuePanel.d.ts.map +1 -0
- package/dist/react/lib/UploadQueuePanel.js +39 -0
- package/dist/react/lib/UploadQueuePanel.js.map +1 -0
- package/dist/react/lib/use-upload-queue.d.ts +51 -0
- package/dist/react/lib/use-upload-queue.d.ts.map +1 -0
- package/dist/react/lib/use-upload-queue.js +167 -0
- package/dist/react/lib/use-upload-queue.js.map +1 -0
- package/dist/resources/media.d.ts +8 -1
- package/dist/resources/media.d.ts.map +1 -1
- package/dist/resources/media.js +6 -2
- package/dist/resources/media.js.map +1 -1
- package/package.json +10 -1
- package/src/http.ts +85 -0
- package/src/react/MediaManager.tsx +41 -11
- package/src/react/crop/CropDialog.tsx +344 -0
- package/src/react/crop/index.ts +6 -0
- package/src/react/index.ts +10 -0
- package/src/react/lib/UploadQueuePanel.tsx +273 -0
- package/src/react/lib/use-upload-queue.ts +250 -0
- package/src/resources/media.ts +13 -4
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UploadQueuePanel = UploadQueuePanel;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("motion/react");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
function UploadQueuePanel({ entries, onCancel, onRemove, onClearDone, className, }) {
|
|
8
|
+
if (entries.length === 0)
|
|
9
|
+
return null;
|
|
10
|
+
const active = entries.filter((e) => e.status === "queued" || e.status === "uploading").length;
|
|
11
|
+
const done = entries.filter((e) => e.status === "done").length;
|
|
12
|
+
const failed = entries.filter((e) => e.status === "error" || e.status === "canceled").length;
|
|
13
|
+
// Aggregate progress (toplam loaded / toplam total)
|
|
14
|
+
const totalLoaded = entries.reduce((s, e) => s + e.loaded, 0);
|
|
15
|
+
const totalSize = entries.reduce((s, e) => s + e.total, 0);
|
|
16
|
+
const aggPercent = totalSize > 0 ? Math.round((totalLoaded / totalSize) * 100) : 0;
|
|
17
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: [0.22, 1, 0.36, 1] }, className: (0, utils_1.cn)("border-t bg-card/50 backdrop-blur-sm", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3 border-b px-3 py-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 text-xs", children: [active > 0 && ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-1.5 text-foreground", children: [(0, jsx_runtime_1.jsxs)("span", { className: "relative flex size-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-75" }), (0, jsx_runtime_1.jsx)("span", { className: "relative inline-flex size-2 rounded-full bg-blue-500" })] }), active, " uploading"] })), done > 0 && ((0, jsx_runtime_1.jsxs)("span", { className: "text-emerald-600 dark:text-emerald-400", children: [done, " done"] })), failed > 0 && ((0, jsx_runtime_1.jsxs)("span", { className: "text-destructive", children: [failed, " failed"] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [totalSize > 0 && ((0, jsx_runtime_1.jsxs)("span", { className: "tabular-nums text-muted-foreground", children: [(0, utils_1.formatBytes)(totalLoaded), " / ", (0, utils_1.formatBytes)(totalSize), " \u00B7", " ", aggPercent, "%"] })), (done > 0 || failed > 0) && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onClearDone, className: "rounded-md border px-2 py-0.5 text-[10px] hover:bg-muted/50", children: "Clear" }))] })] }), (0, jsx_runtime_1.jsx)("div", { className: "max-h-48 overflow-y-auto py-1", children: (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: entries.map((entry, i) => ((0, jsx_runtime_1.jsx)(UploadRow, { entry: entry, index: i, onCancel: () => onCancel(entry.id), onRemove: () => onRemove(entry.id) }, entry.id))) }) })] }));
|
|
18
|
+
}
|
|
19
|
+
function UploadRow({ entry, index, onCancel, onRemove, }) {
|
|
20
|
+
const percent = entry.total > 0 ? Math.round((entry.loaded / entry.total) * 100) : 0;
|
|
21
|
+
const isTerminal = entry.status === "done" ||
|
|
22
|
+
entry.status === "error" ||
|
|
23
|
+
entry.status === "canceled";
|
|
24
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.motion.div, { layout: true, initial: { opacity: 0, x: -8 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, height: 0 }, transition: {
|
|
25
|
+
duration: 0.22,
|
|
26
|
+
ease: [0.22, 1, 0.36, 1],
|
|
27
|
+
delay: index < 5 ? index * 0.04 : 0,
|
|
28
|
+
}, className: (0, utils_1.cn)("flex items-center gap-3 px-3 py-2", entry.status === "error" && "bg-destructive/5"), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex size-7 shrink-0 items-center justify-center", children: [entry.status === "uploading" && ((0, jsx_runtime_1.jsx)(CircularProgress, { percent: percent })), entry.status === "queued" && ((0, jsx_runtime_1.jsx)("span", { className: "size-2 animate-pulse rounded-full bg-muted-foreground/40" })), entry.status === "done" && (0, jsx_runtime_1.jsx)(CheckmarkAnim, {}), entry.status === "error" && ((0, jsx_runtime_1.jsx)("span", { className: "text-base text-destructive", children: "!" })), entry.status === "canceled" && ((0, jsx_runtime_1.jsx)("span", { className: "text-xs text-muted-foreground", children: "\u00D7" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col", children: [(0, jsx_runtime_1.jsx)("span", { className: "truncate text-xs font-medium", title: entry.file.name, children: entry.file.name }), (0, jsx_runtime_1.jsxs)("span", { className: "text-[10px] tabular-nums text-muted-foreground", children: [entry.status === "uploading" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, utils_1.formatBytes)(entry.loaded), " / ", (0, utils_1.formatBytes)(entry.total), " \u00B7", " ", percent, "%"] })), entry.status === "queued" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, utils_1.formatBytes)(entry.total), " \u00B7 queued"] })), entry.status === "done" && ((0, jsx_runtime_1.jsxs)("span", { className: "text-emerald-600 dark:text-emerald-400", children: ["uploaded \u00B7 ", (0, utils_1.formatBytes)(entry.total)] })), entry.status === "error" && ((0, jsx_runtime_1.jsx)("span", { className: "text-destructive", children: entry.error ?? "failed" })), entry.status === "canceled" && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: "canceled" })] })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: isTerminal ? onRemove : onCancel, className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground", "aria-label": isTerminal ? "Remove from list" : "Cancel upload", children: (0, jsx_runtime_1.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "size-3.5", children: (0, jsx_runtime_1.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) }) })] }));
|
|
29
|
+
}
|
|
30
|
+
function CircularProgress({ percent }) {
|
|
31
|
+
const radius = 10;
|
|
32
|
+
const circumference = 2 * Math.PI * radius;
|
|
33
|
+
const offset = circumference - (percent / 100) * circumference;
|
|
34
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", className: "size-6 -rotate-90", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: radius, fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-muted/40" }), (0, jsx_runtime_1.jsx)(react_1.motion.circle, { cx: "12", cy: "12", r: radius, fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeDasharray: circumference, initial: false, animate: { strokeDashoffset: offset }, transition: { duration: 0.3, ease: "easeOut" }, className: "text-blue-500" })] }));
|
|
35
|
+
}
|
|
36
|
+
function CheckmarkAnim() {
|
|
37
|
+
return ((0, jsx_runtime_1.jsx)(react_1.motion.svg, { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: "size-5 text-emerald-500", initial: { scale: 0.6, opacity: 0 }, animate: { scale: 1, opacity: 1 }, transition: { duration: 0.3, ease: [0.34, 1.56, 0.64, 1] }, children: (0, jsx_runtime_1.jsx)(react_1.motion.path, { d: "M20 6 9 17l-5-5", initial: { pathLength: 0 }, animate: { pathLength: 1 }, transition: { duration: 0.4, ease: "easeOut", delay: 0.05 } }) }));
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=UploadQueuePanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UploadQueuePanel.js","sourceRoot":"","sources":["../../../src/react/lib/UploadQueuePanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Sentroy } from "../..";
|
|
2
|
+
import type { Media } from "../../types";
|
|
3
|
+
/**
|
|
4
|
+
* Tek dosyalık upload entry — UI tarafında queue listesi olarak render edilir.
|
|
5
|
+
*
|
|
6
|
+
* State machine:
|
|
7
|
+
* queued → uploading → done | error | canceled
|
|
8
|
+
* `error` ve `canceled` terminal; `done` terminal + `media` set olur.
|
|
9
|
+
*/
|
|
10
|
+
export interface UploadEntry {
|
|
11
|
+
id: string;
|
|
12
|
+
file: File;
|
|
13
|
+
status: "queued" | "uploading" | "done" | "error" | "canceled";
|
|
14
|
+
loaded: number;
|
|
15
|
+
total: number;
|
|
16
|
+
/** Server response — done iken non-null. */
|
|
17
|
+
media?: Media;
|
|
18
|
+
error?: string;
|
|
19
|
+
/** Per-entry cancel handle (XHR abort'u tetikler). */
|
|
20
|
+
cancel: () => void;
|
|
21
|
+
}
|
|
22
|
+
export interface UseUploadQueueOptions {
|
|
23
|
+
/** Aynı anda kaç dosya yüklensin. Default 3. */
|
|
24
|
+
concurrency?: number;
|
|
25
|
+
/** Dosya başarıyla yüklendiğinde tetiklenir — caller refresh edebilir. */
|
|
26
|
+
onUploaded?: (media: Media) => void;
|
|
27
|
+
}
|
|
28
|
+
export interface UseUploadQueueResult {
|
|
29
|
+
entries: UploadEntry[];
|
|
30
|
+
/** Yeni dosyaları queue'ya ekler ve worker'ı tetikler. */
|
|
31
|
+
enqueue: (bucketSlug: string, files: File[]) => void;
|
|
32
|
+
/** Bir entry'i iptal eder (uploading ise XHR abort, queued ise drop). */
|
|
33
|
+
cancel: (id: string) => void;
|
|
34
|
+
/** Tek bir entry'i listeden temizler (terminal state'tekiler için). */
|
|
35
|
+
remove: (id: string) => void;
|
|
36
|
+
/** Done/error/canceled olanları listeden temizler. */
|
|
37
|
+
clearDone: () => void;
|
|
38
|
+
/** Aktif (queued + uploading) entry sayısı. */
|
|
39
|
+
activeCount: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Upload queue hook — concurrency-pooled, per-entry progress + cancel.
|
|
43
|
+
*
|
|
44
|
+
* Storage app'in `apps/storage/lib/upload-client.ts` + `FileUploader` queue
|
|
45
|
+
* davranışını SDK'ya taşır. SDK consumer'ı (MediaManager veya 3rd-party)
|
|
46
|
+
* `entries` array'ini render eder, `enqueue`/`cancel`/`remove` çağırır.
|
|
47
|
+
*
|
|
48
|
+
* onUploaded: caller refresh için kullanır (örn `setRefreshKey + 1`).
|
|
49
|
+
*/
|
|
50
|
+
export declare function useUploadQueue(client: Sentroy, opts?: UseUploadQueueOptions): UseUploadQueueResult;
|
|
51
|
+
//# sourceMappingURL=use-upload-queue.d.ts.map
|
|
@@ -0,0 +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,CAgMtB"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useUploadQueue = useUploadQueue;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
/**
|
|
6
|
+
* Upload queue hook — concurrency-pooled, per-entry progress + cancel.
|
|
7
|
+
*
|
|
8
|
+
* Storage app'in `apps/storage/lib/upload-client.ts` + `FileUploader` queue
|
|
9
|
+
* davranışını SDK'ya taşır. SDK consumer'ı (MediaManager veya 3rd-party)
|
|
10
|
+
* `entries` array'ini render eder, `enqueue`/`cancel`/`remove` çağırır.
|
|
11
|
+
*
|
|
12
|
+
* onUploaded: caller refresh için kullanır (örn `setRefreshKey + 1`).
|
|
13
|
+
*/
|
|
14
|
+
function useUploadQueue(client, opts = {}) {
|
|
15
|
+
const concurrency = opts.concurrency ?? 3;
|
|
16
|
+
const [entries, setEntries] = (0, react_1.useState)([]);
|
|
17
|
+
const entriesRef = (0, react_1.useRef)([]);
|
|
18
|
+
entriesRef.current = entries;
|
|
19
|
+
const onUploadedRef = (0, react_1.useRef)(opts.onUploaded);
|
|
20
|
+
onUploadedRef.current = opts.onUploaded;
|
|
21
|
+
// Worker pump — queued entry varsa ve aktif < concurrency ise başlat.
|
|
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)([]);
|
|
38
|
+
const updateEntry = (0, react_1.useCallback)((id, patch) => {
|
|
39
|
+
setEntries((prev) => prev.map((e) => (e.id === id ? { ...e, ...patch } : e)));
|
|
40
|
+
}, []);
|
|
41
|
+
pumpRef.current = () => {
|
|
42
|
+
// Slot doluysa veya queue boşsa erken dön.
|
|
43
|
+
if (inFlightRef.current >= concurrency)
|
|
44
|
+
return;
|
|
45
|
+
const nextId = queueRef.current.shift();
|
|
46
|
+
if (!nextId)
|
|
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
|
+
}
|
|
58
|
+
// Mark uploading
|
|
59
|
+
updateEntry(entry.id, { status: "uploading" });
|
|
60
|
+
const bucketSlug = bucketMapRef.current[entry.id];
|
|
61
|
+
if (!bucketSlug) {
|
|
62
|
+
updateEntry(entry.id, { status: "error", error: "No bucket" });
|
|
63
|
+
inFlightRef.current--;
|
|
64
|
+
pumpRef.current?.();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
cancelMapRef.current[entry.id] = () => controller.abort();
|
|
69
|
+
client.media
|
|
70
|
+
.upload(bucketSlug, { body: entry.file, filename: entry.file.name }, {
|
|
71
|
+
onProgress: (loaded, total) => {
|
|
72
|
+
updateEntry(entry.id, { loaded, total });
|
|
73
|
+
},
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
})
|
|
76
|
+
.then((media) => {
|
|
77
|
+
updateEntry(entry.id, {
|
|
78
|
+
status: "done",
|
|
79
|
+
media,
|
|
80
|
+
loaded: entry.file.size,
|
|
81
|
+
total: entry.file.size,
|
|
82
|
+
});
|
|
83
|
+
onUploadedRef.current?.(media);
|
|
84
|
+
})
|
|
85
|
+
.catch((err) => {
|
|
86
|
+
const aborted = err?.message === "Upload aborted";
|
|
87
|
+
updateEntry(entry.id, {
|
|
88
|
+
status: aborted ? "canceled" : "error",
|
|
89
|
+
error: aborted
|
|
90
|
+
? undefined
|
|
91
|
+
: (err?.message ?? "Upload failed"),
|
|
92
|
+
});
|
|
93
|
+
})
|
|
94
|
+
.finally(() => {
|
|
95
|
+
delete cancelMapRef.current[entry.id];
|
|
96
|
+
inFlightRef.current--;
|
|
97
|
+
// Slot açıldı, sıradakini başlat.
|
|
98
|
+
pumpRef.current?.();
|
|
99
|
+
});
|
|
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)
|
|
103
|
+
pumpRef.current?.();
|
|
104
|
+
};
|
|
105
|
+
const bucketMapRef = (0, react_1.useRef)({});
|
|
106
|
+
const cancelMapRef = (0, react_1.useRef)({});
|
|
107
|
+
const enqueue = (0, react_1.useCallback)((bucketSlug, files) => {
|
|
108
|
+
if (files.length === 0)
|
|
109
|
+
return;
|
|
110
|
+
const newEntries = files.map((file) => {
|
|
111
|
+
const id = `up-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
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);
|
|
116
|
+
return {
|
|
117
|
+
id,
|
|
118
|
+
file,
|
|
119
|
+
status: "queued",
|
|
120
|
+
loaded: 0,
|
|
121
|
+
total: file.size,
|
|
122
|
+
cancel: () => {
|
|
123
|
+
const c = cancelMapRef.current[id];
|
|
124
|
+
if (c)
|
|
125
|
+
c();
|
|
126
|
+
else {
|
|
127
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id);
|
|
128
|
+
setEntries((prev) => prev.map((e) => e.id === id && e.status === "queued"
|
|
129
|
+
? { ...e, status: "canceled" }
|
|
130
|
+
: e));
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
setEntries((prev) => [...prev, ...newEntries]);
|
|
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.
|
|
140
|
+
Promise.resolve().then(() => pumpRef.current?.());
|
|
141
|
+
}, []);
|
|
142
|
+
const cancel = (0, react_1.useCallback)((id) => {
|
|
143
|
+
const c = cancelMapRef.current[id];
|
|
144
|
+
if (c)
|
|
145
|
+
c();
|
|
146
|
+
else {
|
|
147
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id);
|
|
148
|
+
setEntries((prev) => prev.map((e) => e.id === id && e.status === "queued"
|
|
149
|
+
? { ...e, status: "canceled" }
|
|
150
|
+
: e));
|
|
151
|
+
}
|
|
152
|
+
}, []);
|
|
153
|
+
const remove = (0, react_1.useCallback)((id) => {
|
|
154
|
+
setEntries((prev) => prev.filter((e) => e.id !== id));
|
|
155
|
+
queueRef.current = queueRef.current.filter((qid) => qid !== id);
|
|
156
|
+
delete bucketMapRef.current[id];
|
|
157
|
+
delete cancelMapRef.current[id];
|
|
158
|
+
}, []);
|
|
159
|
+
const clearDone = (0, react_1.useCallback)(() => {
|
|
160
|
+
setEntries((prev) => prev.filter((e) => e.status !== "done" &&
|
|
161
|
+
e.status !== "error" &&
|
|
162
|
+
e.status !== "canceled"));
|
|
163
|
+
}, []);
|
|
164
|
+
const activeCount = entries.filter((e) => e.status === "queued" || e.status === "uploading").length;
|
|
165
|
+
return { entries, enqueue, cancel, remove, clearDone, activeCount };
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=use-upload-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-upload-queue.js","sourceRoot":"","sources":["../../../src/react/lib/use-upload-queue.ts"],"names":[],"mappings":";;AAsDA,wCAmMC;AAzPD,iCAAqD;AA6CrD;;;;;;;;GAQG;AACH,SAAgB,cAAc,CAC5B,MAAe,EACf,OAA8B,EAAE;IAEhC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAA;IACzC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAgB,EAAE,CAAC,CAAA;IACzD,MAAM,UAAU,GAAG,IAAA,cAAM,EAAgB,EAAE,CAAC,CAAA;IAC5C,UAAU,CAAC,OAAO,GAAG,OAAO,CAAA;IAC5B,MAAM,aAAa,GAAG,IAAA,cAAM,EAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC7C,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,CAAA;IAEvC,sEAAsE;IACtE,MAAM,OAAO,GAAG,IAAA,cAAM,EAAa,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAE5C;;;;;;;OAOG;IACH,MAAM,WAAW,GAAG,IAAA,cAAM,EAAC,CAAC,CAAC,CAAA;IAC7B;;;;OAIG;IACH,MAAM,QAAQ,GAAG,IAAA,cAAM,EAAW,EAAE,CAAC,CAAA;IAErC,MAAM,WAAW,GAAG,IAAA,mBAAW,EAC7B,CAAC,EAAU,EAAE,KAA2B,EAAE,EAAE;QAC1C,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACxD,CAAA;IACH,CAAC,EACD,EAAE,CACH,CAAA;IAED,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE;QACrB,2CAA2C;QAC3C,IAAI,WAAW,CAAC,OAAO,IAAI,WAAW;YAAE,OAAM;QAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QACvC,IAAI,CAAC,MAAM;YAAE,OAAM;QAEnB,gEAAgE;QAChE,+BAA+B;QAC/B,WAAW,CAAC,OAAO,EAAE,CAAA;QAErB,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAA;QAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,gEAAgE;YAChE,WAAW,CAAC,OAAO,EAAE,CAAA;YACrB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAA;YACnB,OAAM;QACR,CAAC;QAED,iBAAiB;QACjB,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;QAE9C,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAA;YAC9D,WAAW,CAAC,OAAO,EAAE,CAAA;YACrB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAA;YACnB,OAAM;QACR,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;QAEzD,MAAM,CAAC,KAAK;aACT,MAAM,CACL,UAAU,EACV,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAC/C;YACE,UAAU,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;gBAC5B,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;YAC1C,CAAC;YACD,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CACF;aACA,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACd,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE;gBACpB,MAAM,EAAE,MAAM;gBACd,KAAK;gBACL,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;gBACvB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;aACvB,CAAC,CAAA;YACF,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACtB,MAAM,OAAO,GACV,GAA4B,EAAE,OAAO,KAAK,gBAAgB,CAAA;YAC7D,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE;gBACpB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO;gBACtC,KAAK,EAAE,OAAO;oBACZ,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,CAAE,GAAa,EAAE,OAAO,IAAI,eAAe,CAAC;aACjD,CAAC,CAAA;QACJ,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,OAAO,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACrC,WAAW,CAAC,OAAO,EAAE,CAAA;YACrB,kCAAkC;YAClC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAA;QACrB,CAAC,CAAC,CAAA;QAEJ,0DAA0D;QAC1D,6DAA6D;QAC7D,IAAI,WAAW,CAAC,OAAO,GAAG,WAAW;YAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAA;IAC5D,CAAC,CAAA;IAED,MAAM,YAAY,GAAG,IAAA,cAAM,EAAyB,EAAE,CAAC,CAAA;IACvD,MAAM,YAAY,GAAG,IAAA,cAAM,EAA6B,EAAE,CAAC,CAAA;IAE3D,MAAM,OAAO,GAAG,IAAA,mBAAW,EACzB,CAAC,UAAkB,EAAE,KAAa,EAAE,EAAE;QACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAC9B,MAAM,UAAU,GAAkB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACnD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;YACvE,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,UAAU,CAAA;YACrC,4DAA4D;YAC5D,yDAAyD;YACzD,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACzB,OAAO;gBACL,EAAE;gBACF,IAAI;gBACJ,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,CAAC;gBACT,KAAK,EAAE,IAAI,CAAC,IAAI;gBAChB,MAAM,EAAE,GAAG,EAAE;oBACX,MAAM,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;oBAClC,IAAI,CAAC;wBAAE,CAAC,EAAE,CAAA;yBACL,CAAC;wBACJ,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAA;wBAC/D,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACb,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;4BAClC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE;4BAC9B,CAAC,CAAC,CAAC,CACN,CACF,CAAA;oBACH,CAAC;gBACH,CAAC;aACF,CAAA;QACH,CAAC,CAAC,CAAA;QACF,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC,CAAA;QAC9C,sEAAsE;QACtE,+DAA+D;QAC/D,mEAAmE;QACnE,4CAA4C;QAC5C,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IACnD,CAAC,EACD,EAAE,CACH,CAAA;IAED,MAAM,MAAM,GAAG,IAAA,mBAAW,EAAC,CAAC,EAAU,EAAE,EAAE;QACxC,MAAM,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAClC,IAAI,CAAC;YAAE,CAAC,EAAE,CAAA;aACL,CAAC;YACJ,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAA;YAC/D,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACb,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;gBAClC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE;gBAC9B,CAAC,CAAC,CAAC,CACN,CACF,CAAA;QACH,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,MAAM,GAAG,IAAA,mBAAW,EAAC,CAAC,EAAU,EAAE,EAAE;QACxC,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;QACrD,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAA;QAC/D,OAAO,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAC/B,OAAO,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACjC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,SAAS,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QACjC,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,MAAM,CACT,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,MAAM,KAAK,MAAM;YACnB,CAAC,CAAC,MAAM,KAAK,OAAO;YACpB,CAAC,CAAC,MAAM,KAAK,UAAU,CAC1B,CACF,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CACzD,CAAC,MAAM,CAAA;IAER,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,CAAA;AACrE,CAAC"}
|
|
@@ -29,7 +29,14 @@ export declare class MediaResource {
|
|
|
29
29
|
* })
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
upload(bucketSlug: string, params: UploadMediaParams
|
|
32
|
+
upload(bucketSlug: string, params: UploadMediaParams, opts?: {
|
|
33
|
+
/** Bytes loaded / total — XHR `upload.onprogress` event'inden gelir.
|
|
34
|
+
* Verilmesse fetch tabanlı path kullanılır (progress yok). */
|
|
35
|
+
onProgress?: (loaded: number, total: number) => void;
|
|
36
|
+
/** AbortController.signal — kullanıcı iptal ettiğinde XHR cancel
|
|
37
|
+
* edilir, promise reject. */
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
}): Promise<Media>;
|
|
33
40
|
/**
|
|
34
41
|
* Delete a file. Cascades through the CDN: S3 objects (original +
|
|
35
42
|
* thumbnails) are removed, then the Media record. If any S3 delete
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/resources/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EACV,KAAK,EACL,eAAe,EACf,eAAe,EACf,iBAAiB,EAClB,MAAM,UAAU,CAAA;AAEjB,qBAAa,aAAa;IACZ,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAEpC,0CAA0C;IACpC,IAAI,CACR,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,eAAe,CAAC;IAO3B,iCAAiC;IAC3B,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAM9D;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/resources/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EACV,KAAK,EACL,eAAe,EACf,eAAe,EACf,iBAAiB,EAClB,MAAM,UAAU,CAAA;AAEjB,qBAAa,aAAa;IACZ,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAEpC,0CAA0C;IACpC,IAAI,CACR,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,eAAe,CAAC;IAO3B,iCAAiC;IAC3B,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAM9D;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,EACzB,IAAI,CAAC,EAAE;QACL;uEAC+D;QAC/D,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;QACpD;sCAC8B;QAC9B,MAAM,CAAC,EAAE,WAAW,CAAA;KACrB,GACA,OAAO,CAAC,KAAK,CAAC;IAsBjB;;;;OAIG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhE;;;;;;;;OAQG;IACG,QAAQ,CACZ,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,GACvC,OAAO,CAAC,IAAI,CAAC;CAejB"}
|
package/dist/resources/media.js
CHANGED
|
@@ -36,7 +36,7 @@ class MediaResource {
|
|
|
36
36
|
* })
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
|
-
async upload(bucketSlug, params) {
|
|
39
|
+
async upload(bucketSlug, params, opts) {
|
|
40
40
|
const form = new FormData();
|
|
41
41
|
const filename = params.filename ||
|
|
42
42
|
params.body.name ||
|
|
@@ -53,7 +53,11 @@ class MediaResource {
|
|
|
53
53
|
form.append("caption", params.caption);
|
|
54
54
|
if (params.tags?.length)
|
|
55
55
|
form.append("tags", params.tags.join(","));
|
|
56
|
-
|
|
56
|
+
const path = `/buckets/${encodeURIComponent(bucketSlug)}/media`;
|
|
57
|
+
if (opts?.onProgress || opts?.signal) {
|
|
58
|
+
return this.http.postFormWithProgress(path, form, opts);
|
|
59
|
+
}
|
|
60
|
+
return this.http.postForm(path, form);
|
|
57
61
|
}
|
|
58
62
|
/**
|
|
59
63
|
* Delete a file. Cascades through the CDN: S3 objects (original +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/resources/media.ts"],"names":[],"mappings":";;;AAQA,MAAa,aAAa;IACJ;IAApB,YAAoB,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAExC,0CAA0C;IAC1C,KAAK,CAAC,IAAI,CACR,UAAkB,EAClB,MAAwB;QAExB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAClB,YAAY,kBAAkB,CAAC,UAAU,CAAC,QAAQ,EAClD,MAA6C,CAC9C,CAAA;IACH,CAAC;IAED,iCAAiC;IACjC,KAAK,CAAC,GAAG,CAAC,UAAkB,EAAE,OAAe;QAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAClB,YAAY,kBAAkB,CAAC,UAAU,CAAC,UAAU,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAClF,CAAA;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,MAAM,CACV,UAAkB,EAClB,MAAyB;
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/resources/media.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentroy-co/client-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"description": "TypeScript SDK for the Sentroy platform — REST resources + React components.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
"types": "./dist/types.d.ts",
|
|
20
20
|
"import": "./dist/types.js",
|
|
21
21
|
"require": "./dist/types.js"
|
|
22
|
+
},
|
|
23
|
+
"./react/crop": {
|
|
24
|
+
"types": "./dist/react/crop/index.d.ts",
|
|
25
|
+
"import": "./dist/react/crop/index.js",
|
|
26
|
+
"require": "./dist/react/crop/index.js"
|
|
22
27
|
}
|
|
23
28
|
},
|
|
24
29
|
"files": [
|
|
@@ -71,5 +76,9 @@
|
|
|
71
76
|
"react": "^19.0.0",
|
|
72
77
|
"react-dom": "^19.0.0",
|
|
73
78
|
"typescript": "^5.5.4"
|
|
79
|
+
},
|
|
80
|
+
"dependencies": {
|
|
81
|
+
"motion": "^12.38.0",
|
|
82
|
+
"react-easy-crop": "^5.5.7"
|
|
74
83
|
}
|
|
75
84
|
}
|
package/src/http.ts
CHANGED
|
@@ -166,4 +166,89 @@ export class HttpClient {
|
|
|
166
166
|
clearTimeout(timer)
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* XHR-tabanlı multipart upload — `fetch` progress event veremiyor, XHR
|
|
172
|
+
* `upload.onprogress` ile bytes loaded/total event'i tetikler. Caller
|
|
173
|
+
* `onProgress` ve `signal` (AbortController) verir.
|
|
174
|
+
*
|
|
175
|
+
* Cookie auth (`token` undefined) için `withCredentials = true`. Timeout
|
|
176
|
+
* uygulanmaz (büyük dosyalar için 30sn'de patlayıp kaybolmasın); caller
|
|
177
|
+
* istiyorsa `signal` ile kendi timeout'unu kurar.
|
|
178
|
+
*/
|
|
179
|
+
async postFormWithProgress<T>(
|
|
180
|
+
path: string,
|
|
181
|
+
form: FormData,
|
|
182
|
+
opts: {
|
|
183
|
+
onProgress?: (loaded: number, total: number) => void
|
|
184
|
+
signal?: AbortSignal
|
|
185
|
+
} = {},
|
|
186
|
+
): Promise<T> {
|
|
187
|
+
const url = this.buildUrl(path)
|
|
188
|
+
return new Promise<T>((resolve, reject) => {
|
|
189
|
+
const xhr = new XMLHttpRequest()
|
|
190
|
+
xhr.open("POST", url, true)
|
|
191
|
+
if (this.token) {
|
|
192
|
+
xhr.setRequestHeader("Authorization", `Bearer ${this.token}`)
|
|
193
|
+
} else {
|
|
194
|
+
xhr.withCredentials = true
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (opts.signal) {
|
|
198
|
+
if (opts.signal.aborted) {
|
|
199
|
+
reject(new SentroyError(0, null, "Upload aborted"))
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
opts.signal.addEventListener(
|
|
203
|
+
"abort",
|
|
204
|
+
() => {
|
|
205
|
+
try {
|
|
206
|
+
xhr.abort()
|
|
207
|
+
} catch {
|
|
208
|
+
/* noop */
|
|
209
|
+
}
|
|
210
|
+
reject(new SentroyError(0, null, "Upload aborted"))
|
|
211
|
+
},
|
|
212
|
+
{ once: true },
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (opts.onProgress) {
|
|
217
|
+
xhr.upload.addEventListener("progress", (e) => {
|
|
218
|
+
if (e.lengthComputable) {
|
|
219
|
+
opts.onProgress!(e.loaded, e.total)
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
xhr.addEventListener("load", () => {
|
|
225
|
+
let json: { data?: T; error?: string } = {}
|
|
226
|
+
try {
|
|
227
|
+
json = JSON.parse(xhr.responseText) as { data?: T; error?: string }
|
|
228
|
+
} catch {
|
|
229
|
+
/* fall through with empty object */
|
|
230
|
+
}
|
|
231
|
+
if (xhr.status >= 200 && xhr.status < 300 && json.data !== undefined) {
|
|
232
|
+
resolve(json.data)
|
|
233
|
+
} else {
|
|
234
|
+
reject(
|
|
235
|
+
new SentroyError(
|
|
236
|
+
xhr.status,
|
|
237
|
+
json,
|
|
238
|
+
json.error || `Upload failed with status ${xhr.status}`,
|
|
239
|
+
),
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
xhr.addEventListener("error", () =>
|
|
245
|
+
reject(new SentroyError(0, null, "Upload network error")),
|
|
246
|
+
)
|
|
247
|
+
xhr.addEventListener("timeout", () =>
|
|
248
|
+
reject(new SentroyError(0, null, "Upload timed out")),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
xhr.send(form)
|
|
252
|
+
})
|
|
253
|
+
}
|
|
169
254
|
}
|
|
@@ -9,6 +9,8 @@ import type { Sentroy } from ".."
|
|
|
9
9
|
import type { Bucket, Media } from "../types"
|
|
10
10
|
import { Lightbox } from "./lib/Lightbox"
|
|
11
11
|
import { useMediaList } from "./lib/use-media-list"
|
|
12
|
+
import { useUploadQueue } from "./lib/use-upload-queue"
|
|
13
|
+
import { UploadQueuePanel } from "./lib/UploadQueuePanel"
|
|
12
14
|
import { pickPresetThumbnailUrl } from "../thumbnails"
|
|
13
15
|
import {
|
|
14
16
|
cn,
|
|
@@ -97,6 +99,20 @@ export interface MediaManagerProps {
|
|
|
97
99
|
showDetailsPane?: boolean
|
|
98
100
|
/** Toolbar'da bucket selector görünsün mü (default true). */
|
|
99
101
|
showBucketSelector?: boolean
|
|
102
|
+
/**
|
|
103
|
+
* Yükleme öncesi her dosya için çağrılan async hook. Caller dosyayı
|
|
104
|
+
* dönüştürebilir (örn. image crop), `null` döndürerek skip edebilir.
|
|
105
|
+
* Verilmezse dosyalar olduğu gibi yüklenir.
|
|
106
|
+
*
|
|
107
|
+
* @example Image crop
|
|
108
|
+
* ```ts
|
|
109
|
+
* preprocessFile={async (file) => {
|
|
110
|
+
* if (!file.type.startsWith("image/")) return file
|
|
111
|
+
* return await openCropDialog(file) // ya orijinal File ya cropped File
|
|
112
|
+
* }}
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
preprocessFile?: (file: File) => Promise<File | null>
|
|
100
116
|
}
|
|
101
117
|
|
|
102
118
|
const KIND_FILTERS: Array<{ value: MediaKind | "all"; label: string }> = [
|
|
@@ -126,6 +142,7 @@ export function MediaManager(props: MediaManagerProps) {
|
|
|
126
142
|
classNames: cls = {},
|
|
127
143
|
showDetailsPane = true,
|
|
128
144
|
showBucketSelector = true,
|
|
145
|
+
preprocessFile,
|
|
129
146
|
} = props
|
|
130
147
|
|
|
131
148
|
// ── Bucket state ───────────────────────────────────────────────────────
|
|
@@ -237,24 +254,29 @@ export function MediaManager(props: MediaManagerProps) {
|
|
|
237
254
|
|
|
238
255
|
// ── Upload ─────────────────────────────────────────────────────────────
|
|
239
256
|
const fileInputRef = useRef<HTMLInputElement | null>(null)
|
|
240
|
-
const [uploading, setUploading] = useState(false)
|
|
241
257
|
const [dragOver, setDragOver] = useState(false)
|
|
242
258
|
|
|
259
|
+
// Queue: per-file progress + abort + concurrency-pooled. Pre-upload hook
|
|
260
|
+
// (`preprocessFile`) image crop gibi caller-side transform yapabilir;
|
|
261
|
+
// null dönerse dosya skip, File dönerse o swap'lenir.
|
|
262
|
+
const queue = useUploadQueue(client, {
|
|
263
|
+
concurrency: 3,
|
|
264
|
+
onUploaded: () => setRefreshKey((k) => k + 1),
|
|
265
|
+
})
|
|
266
|
+
const uploading = queue.activeCount > 0
|
|
267
|
+
|
|
243
268
|
const uploadFiles = useCallback(
|
|
244
269
|
async (files: FileList | File[]) => {
|
|
245
270
|
if (!activeBucketSlug) return
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
setRefreshKey((k) => k + 1)
|
|
253
|
-
} finally {
|
|
254
|
-
setUploading(false)
|
|
271
|
+
const incoming = Array.from(files)
|
|
272
|
+
const processed: File[] = []
|
|
273
|
+
for (const file of incoming) {
|
|
274
|
+
const out = preprocessFile ? await preprocessFile(file) : file
|
|
275
|
+
if (out) processed.push(out)
|
|
255
276
|
}
|
|
277
|
+
if (processed.length > 0) queue.enqueue(activeBucketSlug, processed)
|
|
256
278
|
},
|
|
257
|
-
[
|
|
279
|
+
[activeBucketSlug, queue, preprocessFile],
|
|
258
280
|
)
|
|
259
281
|
|
|
260
282
|
// ── Delete ─────────────────────────────────────────────────────────────
|
|
@@ -638,6 +660,14 @@ export function MediaManager(props: MediaManagerProps) {
|
|
|
638
660
|
/>
|
|
639
661
|
)}
|
|
640
662
|
|
|
663
|
+
{/* Upload queue panel — entries varsa görünür, alttan slide-in */}
|
|
664
|
+
<UploadQueuePanel
|
|
665
|
+
entries={queue.entries}
|
|
666
|
+
onCancel={queue.cancel}
|
|
667
|
+
onRemove={queue.remove}
|
|
668
|
+
onClearDone={queue.clearDone}
|
|
669
|
+
/>
|
|
670
|
+
|
|
641
671
|
{/* Lightbox */}
|
|
642
672
|
{lightboxIdx !== null && visibleItems[lightboxIdx] && (
|
|
643
673
|
<Lightbox
|