@plugable-io/react 0.0.4 → 0.0.6
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/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +45 -6
- package/dist/index.mjs +47 -8
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -92,8 +92,9 @@ interface FileImageProps {
|
|
|
92
92
|
style?: CSSProperties;
|
|
93
93
|
onLoad?: () => void;
|
|
94
94
|
onError?: (error: Error) => void;
|
|
95
|
+
onRefetchNeeded?: () => Promise<void>;
|
|
95
96
|
}
|
|
96
|
-
declare function FileImage({ file, width, height, objectFit, borderRadius, alt, className, style, onLoad, onError, }: FileImageProps): react_jsx_runtime.JSX.Element;
|
|
97
|
+
declare function FileImage({ file, width, height, objectFit, borderRadius, alt, className, style, onLoad, onError, onRefetchNeeded, }: FileImageProps): react_jsx_runtime.JSX.Element;
|
|
97
98
|
declare function clearImageCache(): void;
|
|
98
99
|
|
|
99
100
|
interface FilePreviewProps {
|
|
@@ -106,7 +107,7 @@ interface FilePreviewProps {
|
|
|
106
107
|
showExtension?: boolean;
|
|
107
108
|
renderNonImage?: (file: FileObject) => React.ReactNode;
|
|
108
109
|
}
|
|
109
|
-
declare function FilePreview({ file, width, height, className, style, objectFit, showExtension, renderNonImage, }: FilePreviewProps): react_jsx_runtime.JSX.Element;
|
|
110
|
+
declare function FilePreview({ file: initialFile, width, height, className, style, objectFit, showExtension, renderNonImage, }: FilePreviewProps): react_jsx_runtime.JSX.Element;
|
|
110
111
|
|
|
111
112
|
interface UseFilesOptions {
|
|
112
113
|
metadata?: Record<string, any>;
|
package/dist/index.d.ts
CHANGED
|
@@ -92,8 +92,9 @@ interface FileImageProps {
|
|
|
92
92
|
style?: CSSProperties;
|
|
93
93
|
onLoad?: () => void;
|
|
94
94
|
onError?: (error: Error) => void;
|
|
95
|
+
onRefetchNeeded?: () => Promise<void>;
|
|
95
96
|
}
|
|
96
|
-
declare function FileImage({ file, width, height, objectFit, borderRadius, alt, className, style, onLoad, onError, }: FileImageProps): react_jsx_runtime.JSX.Element;
|
|
97
|
+
declare function FileImage({ file, width, height, objectFit, borderRadius, alt, className, style, onLoad, onError, onRefetchNeeded, }: FileImageProps): react_jsx_runtime.JSX.Element;
|
|
97
98
|
declare function clearImageCache(): void;
|
|
98
99
|
|
|
99
100
|
interface FilePreviewProps {
|
|
@@ -106,7 +107,7 @@ interface FilePreviewProps {
|
|
|
106
107
|
showExtension?: boolean;
|
|
107
108
|
renderNonImage?: (file: FileObject) => React.ReactNode;
|
|
108
109
|
}
|
|
109
|
-
declare function FilePreview({ file, width, height, className, style, objectFit, showExtension, renderNonImage, }: FilePreviewProps): react_jsx_runtime.JSX.Element;
|
|
110
|
+
declare function FilePreview({ file: initialFile, width, height, className, style, objectFit, showExtension, renderNonImage, }: FilePreviewProps): react_jsx_runtime.JSX.Element;
|
|
110
111
|
|
|
111
112
|
interface UseFilesOptions {
|
|
112
113
|
metadata?: Record<string, any>;
|
package/dist/index.js
CHANGED
|
@@ -414,6 +414,8 @@ function useFiles({
|
|
|
414
414
|
const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
|
|
415
415
|
const [page, setPage] = (0, import_react3.useState)(startPage);
|
|
416
416
|
const [hasNext, setHasNext] = (0, import_react3.useState)(false);
|
|
417
|
+
const previousParamsRef = (0, import_react3.useRef)(null);
|
|
418
|
+
const isInitialMountRef = (0, import_react3.useRef)(true);
|
|
417
419
|
const effectiveStaleTime = staleTime ?? providerStaleTime;
|
|
418
420
|
const metadataKey = JSON.stringify(metadata);
|
|
419
421
|
const stableMetadata = (0, import_react3.useMemo)(() => metadata, [metadataKey]);
|
|
@@ -482,16 +484,23 @@ function useFiles({
|
|
|
482
484
|
}
|
|
483
485
|
}, [client, stableMetadata, mediaType, perPage, orderBy, orderDirection, getCache, setCache, effectiveStaleTime]);
|
|
484
486
|
(0, import_react3.useEffect)(() => {
|
|
487
|
+
const paramsChanged = previousParamsRef.current !== null && previousParamsRef.current !== paramsKeyWithPage;
|
|
488
|
+
const isInitialMount = isInitialMountRef.current;
|
|
489
|
+
previousParamsRef.current = paramsKeyWithPage;
|
|
490
|
+
if (isInitialMount) {
|
|
491
|
+
isInitialMountRef.current = false;
|
|
492
|
+
}
|
|
485
493
|
const cachedEntry = getCache(paramsKeyWithPage);
|
|
494
|
+
const shouldFetch = paramsChanged || isInitialMount && autoLoad;
|
|
486
495
|
if (cachedEntry) {
|
|
487
496
|
const age = Date.now() - cachedEntry.timestamp;
|
|
488
497
|
const isStale = age > effectiveStaleTime;
|
|
489
498
|
setFiles(cachedEntry.files);
|
|
490
499
|
setHasNext(cachedEntry.paging.has_next_page);
|
|
491
|
-
if (isStale &&
|
|
500
|
+
if (paramsChanged || isStale && shouldFetch) {
|
|
492
501
|
fetchFiles(page, true);
|
|
493
502
|
}
|
|
494
|
-
} else if (
|
|
503
|
+
} else if (shouldFetch) {
|
|
495
504
|
fetchFiles(page, true);
|
|
496
505
|
}
|
|
497
506
|
}, [paramsKeyWithPage, autoLoad, getCache, effectiveStaleTime, fetchFiles, page]);
|
|
@@ -570,11 +579,13 @@ function FileImage({
|
|
|
570
579
|
className,
|
|
571
580
|
style,
|
|
572
581
|
onLoad,
|
|
573
|
-
onError
|
|
582
|
+
onError,
|
|
583
|
+
onRefetchNeeded
|
|
574
584
|
}) {
|
|
575
585
|
const [imageSrc, setImageSrc] = (0, import_react4.useState)(null);
|
|
576
586
|
const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
|
|
577
587
|
const [error, setError] = (0, import_react4.useState)(null);
|
|
588
|
+
const refetchAttemptedRef = (0, import_react4.useRef)(null);
|
|
578
589
|
(0, import_react4.useEffect)(() => {
|
|
579
590
|
let isMounted = true;
|
|
580
591
|
let objectUrl = null;
|
|
@@ -597,6 +608,13 @@ function FileImage({
|
|
|
597
608
|
}
|
|
598
609
|
});
|
|
599
610
|
if (!response.ok) {
|
|
611
|
+
const downloadUrlKey = `${file.id}-${file.download_url}`;
|
|
612
|
+
if (response.status === 403 && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
|
|
613
|
+
refetchAttemptedRef.current = downloadUrlKey;
|
|
614
|
+
imageCache.delete(cacheKey);
|
|
615
|
+
await onRefetchNeeded();
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
600
618
|
throw new Error(`Failed to fetch image: ${response.statusText}`);
|
|
601
619
|
}
|
|
602
620
|
const blob = await response.blob();
|
|
@@ -605,6 +623,7 @@ function FileImage({
|
|
|
605
623
|
if (isMounted) {
|
|
606
624
|
setImageSrc(objectUrl);
|
|
607
625
|
setIsLoading(false);
|
|
626
|
+
refetchAttemptedRef.current = null;
|
|
608
627
|
}
|
|
609
628
|
} else {
|
|
610
629
|
throw new Error("No download URL available for file");
|
|
@@ -623,7 +642,7 @@ function FileImage({
|
|
|
623
642
|
return () => {
|
|
624
643
|
isMounted = false;
|
|
625
644
|
};
|
|
626
|
-
}, [file.id, file.checksum, file.download_url, onError]);
|
|
645
|
+
}, [file.id, file.checksum, file.download_url, onError, onRefetchNeeded]);
|
|
627
646
|
const handleLoad = () => {
|
|
628
647
|
setIsLoading(false);
|
|
629
648
|
onLoad?.();
|
|
@@ -713,9 +732,10 @@ function clearImageCache() {
|
|
|
713
732
|
}
|
|
714
733
|
|
|
715
734
|
// src/components/FilePreview.tsx
|
|
735
|
+
var import_react5 = require("react");
|
|
716
736
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
717
737
|
function FilePreview({
|
|
718
|
-
file,
|
|
738
|
+
file: initialFile,
|
|
719
739
|
width = 80,
|
|
720
740
|
height = 80,
|
|
721
741
|
className,
|
|
@@ -724,6 +744,24 @@ function FilePreview({
|
|
|
724
744
|
showExtension = true,
|
|
725
745
|
renderNonImage
|
|
726
746
|
}) {
|
|
747
|
+
const { client } = usePlugable();
|
|
748
|
+
const [file, setFile] = (0, import_react5.useState)(initialFile);
|
|
749
|
+
const [isRefetching, setIsRefetching] = (0, import_react5.useState)(false);
|
|
750
|
+
(0, import_react5.useEffect)(() => {
|
|
751
|
+
setFile(initialFile);
|
|
752
|
+
}, [initialFile.id, initialFile.download_url]);
|
|
753
|
+
const handleRefetch = (0, import_react5.useCallback)(async () => {
|
|
754
|
+
if (isRefetching) return;
|
|
755
|
+
try {
|
|
756
|
+
setIsRefetching(true);
|
|
757
|
+
const refreshedFile = await client.get(file.id);
|
|
758
|
+
setFile(refreshedFile);
|
|
759
|
+
} catch (err) {
|
|
760
|
+
console.error("Failed to refetch file:", err);
|
|
761
|
+
} finally {
|
|
762
|
+
setIsRefetching(false);
|
|
763
|
+
}
|
|
764
|
+
}, [file.id, client]);
|
|
727
765
|
const isImage = file.content_type.startsWith("image/");
|
|
728
766
|
const containerStyle = {
|
|
729
767
|
width,
|
|
@@ -747,7 +785,8 @@ function FilePreview({
|
|
|
747
785
|
objectFit,
|
|
748
786
|
className,
|
|
749
787
|
style,
|
|
750
|
-
borderRadius: 4
|
|
788
|
+
borderRadius: 4,
|
|
789
|
+
onRefetchNeeded: handleRefetch
|
|
751
790
|
}
|
|
752
791
|
);
|
|
753
792
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -355,7 +355,7 @@ function Dropzone({
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
// src/hooks/useFiles.ts
|
|
358
|
-
import { useState as useState3, useCallback as useCallback3, useEffect, useMemo as useMemo2 } from "react";
|
|
358
|
+
import { useState as useState3, useCallback as useCallback3, useEffect, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
359
359
|
function useFiles({
|
|
360
360
|
metadata,
|
|
361
361
|
startPage = 1,
|
|
@@ -371,6 +371,8 @@ function useFiles({
|
|
|
371
371
|
const [isLoading, setIsLoading] = useState3(false);
|
|
372
372
|
const [page, setPage] = useState3(startPage);
|
|
373
373
|
const [hasNext, setHasNext] = useState3(false);
|
|
374
|
+
const previousParamsRef = useRef2(null);
|
|
375
|
+
const isInitialMountRef = useRef2(true);
|
|
374
376
|
const effectiveStaleTime = staleTime ?? providerStaleTime;
|
|
375
377
|
const metadataKey = JSON.stringify(metadata);
|
|
376
378
|
const stableMetadata = useMemo2(() => metadata, [metadataKey]);
|
|
@@ -439,16 +441,23 @@ function useFiles({
|
|
|
439
441
|
}
|
|
440
442
|
}, [client, stableMetadata, mediaType, perPage, orderBy, orderDirection, getCache, setCache, effectiveStaleTime]);
|
|
441
443
|
useEffect(() => {
|
|
444
|
+
const paramsChanged = previousParamsRef.current !== null && previousParamsRef.current !== paramsKeyWithPage;
|
|
445
|
+
const isInitialMount = isInitialMountRef.current;
|
|
446
|
+
previousParamsRef.current = paramsKeyWithPage;
|
|
447
|
+
if (isInitialMount) {
|
|
448
|
+
isInitialMountRef.current = false;
|
|
449
|
+
}
|
|
442
450
|
const cachedEntry = getCache(paramsKeyWithPage);
|
|
451
|
+
const shouldFetch = paramsChanged || isInitialMount && autoLoad;
|
|
443
452
|
if (cachedEntry) {
|
|
444
453
|
const age = Date.now() - cachedEntry.timestamp;
|
|
445
454
|
const isStale = age > effectiveStaleTime;
|
|
446
455
|
setFiles(cachedEntry.files);
|
|
447
456
|
setHasNext(cachedEntry.paging.has_next_page);
|
|
448
|
-
if (isStale &&
|
|
457
|
+
if (paramsChanged || isStale && shouldFetch) {
|
|
449
458
|
fetchFiles(page, true);
|
|
450
459
|
}
|
|
451
|
-
} else if (
|
|
460
|
+
} else if (shouldFetch) {
|
|
452
461
|
fetchFiles(page, true);
|
|
453
462
|
}
|
|
454
463
|
}, [paramsKeyWithPage, autoLoad, getCache, effectiveStaleTime, fetchFiles, page]);
|
|
@@ -514,7 +523,7 @@ function FileList({
|
|
|
514
523
|
}
|
|
515
524
|
|
|
516
525
|
// src/components/FileImage.tsx
|
|
517
|
-
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
526
|
+
import { useEffect as useEffect2, useState as useState4, useRef as useRef3 } from "react";
|
|
518
527
|
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
519
528
|
var imageCache = /* @__PURE__ */ new Map();
|
|
520
529
|
function FileImage({
|
|
@@ -527,11 +536,13 @@ function FileImage({
|
|
|
527
536
|
className,
|
|
528
537
|
style,
|
|
529
538
|
onLoad,
|
|
530
|
-
onError
|
|
539
|
+
onError,
|
|
540
|
+
onRefetchNeeded
|
|
531
541
|
}) {
|
|
532
542
|
const [imageSrc, setImageSrc] = useState4(null);
|
|
533
543
|
const [isLoading, setIsLoading] = useState4(true);
|
|
534
544
|
const [error, setError] = useState4(null);
|
|
545
|
+
const refetchAttemptedRef = useRef3(null);
|
|
535
546
|
useEffect2(() => {
|
|
536
547
|
let isMounted = true;
|
|
537
548
|
let objectUrl = null;
|
|
@@ -554,6 +565,13 @@ function FileImage({
|
|
|
554
565
|
}
|
|
555
566
|
});
|
|
556
567
|
if (!response.ok) {
|
|
568
|
+
const downloadUrlKey = `${file.id}-${file.download_url}`;
|
|
569
|
+
if (response.status === 403 && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
|
|
570
|
+
refetchAttemptedRef.current = downloadUrlKey;
|
|
571
|
+
imageCache.delete(cacheKey);
|
|
572
|
+
await onRefetchNeeded();
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
557
575
|
throw new Error(`Failed to fetch image: ${response.statusText}`);
|
|
558
576
|
}
|
|
559
577
|
const blob = await response.blob();
|
|
@@ -562,6 +580,7 @@ function FileImage({
|
|
|
562
580
|
if (isMounted) {
|
|
563
581
|
setImageSrc(objectUrl);
|
|
564
582
|
setIsLoading(false);
|
|
583
|
+
refetchAttemptedRef.current = null;
|
|
565
584
|
}
|
|
566
585
|
} else {
|
|
567
586
|
throw new Error("No download URL available for file");
|
|
@@ -580,7 +599,7 @@ function FileImage({
|
|
|
580
599
|
return () => {
|
|
581
600
|
isMounted = false;
|
|
582
601
|
};
|
|
583
|
-
}, [file.id, file.checksum, file.download_url, onError]);
|
|
602
|
+
}, [file.id, file.checksum, file.download_url, onError, onRefetchNeeded]);
|
|
584
603
|
const handleLoad = () => {
|
|
585
604
|
setIsLoading(false);
|
|
586
605
|
onLoad?.();
|
|
@@ -670,9 +689,10 @@ function clearImageCache() {
|
|
|
670
689
|
}
|
|
671
690
|
|
|
672
691
|
// src/components/FilePreview.tsx
|
|
692
|
+
import { useState as useState5, useCallback as useCallback4, useEffect as useEffect3 } from "react";
|
|
673
693
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
674
694
|
function FilePreview({
|
|
675
|
-
file,
|
|
695
|
+
file: initialFile,
|
|
676
696
|
width = 80,
|
|
677
697
|
height = 80,
|
|
678
698
|
className,
|
|
@@ -681,6 +701,24 @@ function FilePreview({
|
|
|
681
701
|
showExtension = true,
|
|
682
702
|
renderNonImage
|
|
683
703
|
}) {
|
|
704
|
+
const { client } = usePlugable();
|
|
705
|
+
const [file, setFile] = useState5(initialFile);
|
|
706
|
+
const [isRefetching, setIsRefetching] = useState5(false);
|
|
707
|
+
useEffect3(() => {
|
|
708
|
+
setFile(initialFile);
|
|
709
|
+
}, [initialFile.id, initialFile.download_url]);
|
|
710
|
+
const handleRefetch = useCallback4(async () => {
|
|
711
|
+
if (isRefetching) return;
|
|
712
|
+
try {
|
|
713
|
+
setIsRefetching(true);
|
|
714
|
+
const refreshedFile = await client.get(file.id);
|
|
715
|
+
setFile(refreshedFile);
|
|
716
|
+
} catch (err) {
|
|
717
|
+
console.error("Failed to refetch file:", err);
|
|
718
|
+
} finally {
|
|
719
|
+
setIsRefetching(false);
|
|
720
|
+
}
|
|
721
|
+
}, [file.id, client]);
|
|
684
722
|
const isImage = file.content_type.startsWith("image/");
|
|
685
723
|
const containerStyle = {
|
|
686
724
|
width,
|
|
@@ -704,7 +742,8 @@ function FilePreview({
|
|
|
704
742
|
objectFit,
|
|
705
743
|
className,
|
|
706
744
|
style,
|
|
707
|
-
borderRadius: 4
|
|
745
|
+
borderRadius: 4,
|
|
746
|
+
onRefetchNeeded: handleRefetch
|
|
708
747
|
}
|
|
709
748
|
);
|
|
710
749
|
}
|