@plugable-io/react 0.0.5 → 0.0.7

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 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
@@ -579,11 +579,13 @@ function FileImage({
579
579
  className,
580
580
  style,
581
581
  onLoad,
582
- onError
582
+ onError,
583
+ onRefetchNeeded
583
584
  }) {
584
585
  const [imageSrc, setImageSrc] = (0, import_react4.useState)(null);
585
586
  const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
586
587
  const [error, setError] = (0, import_react4.useState)(null);
588
+ const refetchAttemptedRef = (0, import_react4.useRef)(null);
587
589
  (0, import_react4.useEffect)(() => {
588
590
  let isMounted = true;
589
591
  let objectUrl = null;
@@ -599,21 +601,47 @@ function FileImage({
599
601
  return;
600
602
  }
601
603
  if (file.download_url) {
602
- const response = await fetch(file.download_url, {
603
- headers: {
604
- "Cache-Control": "private, max-age=31536000",
605
- "If-None-Match": file.checksum
604
+ let response = null;
605
+ try {
606
+ response = await fetch(file.download_url, {
607
+ headers: {
608
+ "Cache-Control": "private, max-age=31536000",
609
+ "If-None-Match": file.checksum
610
+ }
611
+ });
612
+ if (!response.ok) {
613
+ const downloadUrlKey = `${file.id}-${file.download_url}`;
614
+ if (response.status === 403 && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
615
+ refetchAttemptedRef.current = downloadUrlKey;
616
+ imageCache.delete(cacheKey);
617
+ await onRefetchNeeded();
618
+ return;
619
+ }
620
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
606
621
  }
607
- });
608
- if (!response.ok) {
609
- throw new Error(`Failed to fetch image: ${response.statusText}`);
610
- }
611
- const blob = await response.blob();
612
- objectUrl = URL.createObjectURL(blob);
613
- imageCache.set(cacheKey, objectUrl);
614
- if (isMounted) {
615
- setImageSrc(objectUrl);
616
- setIsLoading(false);
622
+ const blob = await response.blob();
623
+ objectUrl = URL.createObjectURL(blob);
624
+ imageCache.set(cacheKey, objectUrl);
625
+ if (isMounted) {
626
+ setImageSrc(objectUrl);
627
+ setIsLoading(false);
628
+ refetchAttemptedRef.current = null;
629
+ }
630
+ } catch (fetchError) {
631
+ const downloadUrlKey = `${file.id}-${file.download_url}`;
632
+ const isNetworkError = !response && (fetchError.message?.includes("Failed to fetch") || fetchError.message?.includes("CORS") || fetchError.name === "TypeError" || fetchError.message?.includes("network") || fetchError.message?.includes("ERR_FAILED"));
633
+ const is403Error = response?.status === 403;
634
+ if ((isNetworkError || is403Error) && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
635
+ refetchAttemptedRef.current = downloadUrlKey;
636
+ imageCache.delete(cacheKey);
637
+ try {
638
+ await onRefetchNeeded();
639
+ return;
640
+ } catch (refetchError) {
641
+ console.error("Failed to refetch file:", refetchError);
642
+ }
643
+ }
644
+ throw fetchError;
617
645
  }
618
646
  } else {
619
647
  throw new Error("No download URL available for file");
@@ -632,7 +660,7 @@ function FileImage({
632
660
  return () => {
633
661
  isMounted = false;
634
662
  };
635
- }, [file.id, file.checksum, file.download_url, onError]);
663
+ }, [file.id, file.checksum, file.download_url, onError, onRefetchNeeded]);
636
664
  const handleLoad = () => {
637
665
  setIsLoading(false);
638
666
  onLoad?.();
@@ -722,9 +750,10 @@ function clearImageCache() {
722
750
  }
723
751
 
724
752
  // src/components/FilePreview.tsx
753
+ var import_react5 = require("react");
725
754
  var import_jsx_runtime5 = require("react/jsx-runtime");
726
755
  function FilePreview({
727
- file,
756
+ file: initialFile,
728
757
  width = 80,
729
758
  height = 80,
730
759
  className,
@@ -733,6 +762,24 @@ function FilePreview({
733
762
  showExtension = true,
734
763
  renderNonImage
735
764
  }) {
765
+ const { client } = usePlugable();
766
+ const [file, setFile] = (0, import_react5.useState)(initialFile);
767
+ const [isRefetching, setIsRefetching] = (0, import_react5.useState)(false);
768
+ (0, import_react5.useEffect)(() => {
769
+ setFile(initialFile);
770
+ }, [initialFile.id, initialFile.download_url]);
771
+ const handleRefetch = (0, import_react5.useCallback)(async () => {
772
+ if (isRefetching) return;
773
+ try {
774
+ setIsRefetching(true);
775
+ const refreshedFile = await client.get(file.id);
776
+ setFile(refreshedFile);
777
+ } catch (err) {
778
+ console.error("Failed to refetch file:", err);
779
+ } finally {
780
+ setIsRefetching(false);
781
+ }
782
+ }, [file.id, client]);
736
783
  const isImage = file.content_type.startsWith("image/");
737
784
  const containerStyle = {
738
785
  width,
@@ -756,7 +803,8 @@ function FilePreview({
756
803
  objectFit,
757
804
  className,
758
805
  style,
759
- borderRadius: 4
806
+ borderRadius: 4,
807
+ onRefetchNeeded: handleRefetch
760
808
  }
761
809
  );
762
810
  }
package/dist/index.mjs CHANGED
@@ -523,7 +523,7 @@ function FileList({
523
523
  }
524
524
 
525
525
  // src/components/FileImage.tsx
526
- import { useEffect as useEffect2, useState as useState4 } from "react";
526
+ import { useEffect as useEffect2, useState as useState4, useRef as useRef3 } from "react";
527
527
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
528
528
  var imageCache = /* @__PURE__ */ new Map();
529
529
  function FileImage({
@@ -536,11 +536,13 @@ function FileImage({
536
536
  className,
537
537
  style,
538
538
  onLoad,
539
- onError
539
+ onError,
540
+ onRefetchNeeded
540
541
  }) {
541
542
  const [imageSrc, setImageSrc] = useState4(null);
542
543
  const [isLoading, setIsLoading] = useState4(true);
543
544
  const [error, setError] = useState4(null);
545
+ const refetchAttemptedRef = useRef3(null);
544
546
  useEffect2(() => {
545
547
  let isMounted = true;
546
548
  let objectUrl = null;
@@ -556,21 +558,47 @@ function FileImage({
556
558
  return;
557
559
  }
558
560
  if (file.download_url) {
559
- const response = await fetch(file.download_url, {
560
- headers: {
561
- "Cache-Control": "private, max-age=31536000",
562
- "If-None-Match": file.checksum
561
+ let response = null;
562
+ try {
563
+ response = await fetch(file.download_url, {
564
+ headers: {
565
+ "Cache-Control": "private, max-age=31536000",
566
+ "If-None-Match": file.checksum
567
+ }
568
+ });
569
+ if (!response.ok) {
570
+ const downloadUrlKey = `${file.id}-${file.download_url}`;
571
+ if (response.status === 403 && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
572
+ refetchAttemptedRef.current = downloadUrlKey;
573
+ imageCache.delete(cacheKey);
574
+ await onRefetchNeeded();
575
+ return;
576
+ }
577
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
563
578
  }
564
- });
565
- if (!response.ok) {
566
- throw new Error(`Failed to fetch image: ${response.statusText}`);
567
- }
568
- const blob = await response.blob();
569
- objectUrl = URL.createObjectURL(blob);
570
- imageCache.set(cacheKey, objectUrl);
571
- if (isMounted) {
572
- setImageSrc(objectUrl);
573
- setIsLoading(false);
579
+ const blob = await response.blob();
580
+ objectUrl = URL.createObjectURL(blob);
581
+ imageCache.set(cacheKey, objectUrl);
582
+ if (isMounted) {
583
+ setImageSrc(objectUrl);
584
+ setIsLoading(false);
585
+ refetchAttemptedRef.current = null;
586
+ }
587
+ } catch (fetchError) {
588
+ const downloadUrlKey = `${file.id}-${file.download_url}`;
589
+ const isNetworkError = !response && (fetchError.message?.includes("Failed to fetch") || fetchError.message?.includes("CORS") || fetchError.name === "TypeError" || fetchError.message?.includes("network") || fetchError.message?.includes("ERR_FAILED"));
590
+ const is403Error = response?.status === 403;
591
+ if ((isNetworkError || is403Error) && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
592
+ refetchAttemptedRef.current = downloadUrlKey;
593
+ imageCache.delete(cacheKey);
594
+ try {
595
+ await onRefetchNeeded();
596
+ return;
597
+ } catch (refetchError) {
598
+ console.error("Failed to refetch file:", refetchError);
599
+ }
600
+ }
601
+ throw fetchError;
574
602
  }
575
603
  } else {
576
604
  throw new Error("No download URL available for file");
@@ -589,7 +617,7 @@ function FileImage({
589
617
  return () => {
590
618
  isMounted = false;
591
619
  };
592
- }, [file.id, file.checksum, file.download_url, onError]);
620
+ }, [file.id, file.checksum, file.download_url, onError, onRefetchNeeded]);
593
621
  const handleLoad = () => {
594
622
  setIsLoading(false);
595
623
  onLoad?.();
@@ -679,9 +707,10 @@ function clearImageCache() {
679
707
  }
680
708
 
681
709
  // src/components/FilePreview.tsx
710
+ import { useState as useState5, useCallback as useCallback4, useEffect as useEffect3 } from "react";
682
711
  import { jsx as jsx5 } from "react/jsx-runtime";
683
712
  function FilePreview({
684
- file,
713
+ file: initialFile,
685
714
  width = 80,
686
715
  height = 80,
687
716
  className,
@@ -690,6 +719,24 @@ function FilePreview({
690
719
  showExtension = true,
691
720
  renderNonImage
692
721
  }) {
722
+ const { client } = usePlugable();
723
+ const [file, setFile] = useState5(initialFile);
724
+ const [isRefetching, setIsRefetching] = useState5(false);
725
+ useEffect3(() => {
726
+ setFile(initialFile);
727
+ }, [initialFile.id, initialFile.download_url]);
728
+ const handleRefetch = useCallback4(async () => {
729
+ if (isRefetching) return;
730
+ try {
731
+ setIsRefetching(true);
732
+ const refreshedFile = await client.get(file.id);
733
+ setFile(refreshedFile);
734
+ } catch (err) {
735
+ console.error("Failed to refetch file:", err);
736
+ } finally {
737
+ setIsRefetching(false);
738
+ }
739
+ }, [file.id, client]);
693
740
  const isImage = file.content_type.startsWith("image/");
694
741
  const containerStyle = {
695
742
  width,
@@ -713,7 +760,8 @@ function FilePreview({
713
760
  objectFit,
714
761
  className,
715
762
  style,
716
- borderRadius: 4
763
+ borderRadius: 4,
764
+ onRefetchNeeded: handleRefetch
717
765
  }
718
766
  );
719
767
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plugable-io/react",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "React components and hooks for Plugable File Management API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",