@plugable-io/react 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -238,6 +238,37 @@ function ImageGallery() {
238
238
  }
239
239
  ```
240
240
 
241
+ ### Ordering Files
242
+
243
+ ```tsx
244
+ function SortedFileList() {
245
+ const [orderBy, setOrderBy] = useState<'created_at' | 'name' | 'byte_size'>('created_at');
246
+ const [orderDirection, setOrderDirection] = useState<'asc' | 'desc'>('desc');
247
+
248
+ const { files, isLoading } = useFiles({
249
+ orderBy,
250
+ orderDirection,
251
+ perPage: 20,
252
+ });
253
+
254
+ return (
255
+ <div>
256
+ <select value={orderBy} onChange={(e) => setOrderBy(e.target.value as any)}>
257
+ <option value="created_at">Date</option>
258
+ <option value="name">Name</option>
259
+ <option value="byte_size">Size</option>
260
+ </select>
261
+ <button onClick={() => setOrderDirection(orderDirection === 'asc' ? 'desc' : 'asc')}>
262
+ {orderDirection === 'asc' ? '↑' : '↓'}
263
+ </button>
264
+ {files.map((file) => (
265
+ <div key={file.id}>{file.name}</div>
266
+ ))}
267
+ </div>
268
+ );
269
+ }
270
+ ```
271
+
241
272
  ### Manual Loading
242
273
 
243
274
  ```tsx
@@ -268,6 +299,8 @@ function LazyFileList() {
268
299
  | `perPage` | `number` | `20` | Files per page |
269
300
  | `startPage` | `number` | `1` | Initial page number |
270
301
  | `autoLoad` | `boolean` | `true` | Fetch on mount |
302
+ | `orderBy` | `'created_at' \| 'name' \| 'byte_size'` | `'created_at'` | Field to order by |
303
+ | `orderDirection` | `'asc' \| 'desc'` | `'desc'` | Sort direction |
271
304
 
272
305
  ### Return Value
273
306
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React, { ReactNode, CSSProperties } from 'react';
3
- import { BucketClient, FileObject } from '@plugable-io/js';
3
+ import { BucketClient, FileObject, ListResponse } from '@plugable-io/js';
4
4
  export { FileObject, SearchOptions, UpdateOptions } from '@plugable-io/js';
5
5
 
6
6
  type AuthProvider = 'clerk' | 'supabase' | 'firebase';
@@ -11,9 +11,15 @@ interface PlugableProviderProps {
11
11
  authProvider?: AuthProvider;
12
12
  clerkJWTTemplate?: string;
13
13
  baseUrl?: string;
14
+ staleTime?: number;
14
15
  }
15
16
  type PlugableEventType = 'file.uploaded' | 'file.deleted';
16
17
  type EventHandler = (data?: any) => void;
18
+ interface CacheEntry {
19
+ files: FileObject[];
20
+ paging: ListResponse['paging'];
21
+ timestamp: number;
22
+ }
17
23
  interface PlugableContextValue {
18
24
  client: BucketClient;
19
25
  bucketId: string;
@@ -21,8 +27,12 @@ interface PlugableContextValue {
21
27
  emit: (event: PlugableEventType, data?: any) => void;
22
28
  getToken: () => Promise<string> | string;
23
29
  baseUrl?: string;
30
+ staleTime: number;
31
+ getCache: (key: string) => CacheEntry | undefined;
32
+ setCache: (key: string, entry: CacheEntry) => void;
33
+ invalidateCache: (pattern?: string) => void;
24
34
  }
25
- declare function PlugableProvider({ bucketId, children, getToken, authProvider, clerkJWTTemplate, baseUrl, }: PlugableProviderProps): react_jsx_runtime.JSX.Element;
35
+ declare function PlugableProvider({ bucketId, children, getToken, authProvider, clerkJWTTemplate, baseUrl, staleTime, }: PlugableProviderProps): react_jsx_runtime.JSX.Element;
26
36
  declare function usePlugable(): PlugableContextValue;
27
37
 
28
38
  interface DropzoneProps {
@@ -104,6 +114,9 @@ interface UseFilesOptions {
104
114
  perPage?: number;
105
115
  mediaType?: string;
106
116
  autoLoad?: boolean;
117
+ orderBy?: 'created_at' | 'name' | 'byte_size';
118
+ orderDirection?: 'asc' | 'desc';
119
+ staleTime?: number;
107
120
  }
108
121
  interface UseFilesResult {
109
122
  files: FileObject[];
@@ -118,6 +131,6 @@ interface UseFilesResult {
118
131
  setPage: (page: number) => void;
119
132
  refresh: () => Promise<void>;
120
133
  }
121
- declare function useFiles({ metadata, startPage, perPage, mediaType, autoLoad, }?: UseFilesOptions): UseFilesResult;
134
+ declare function useFiles({ metadata, startPage, perPage, mediaType, autoLoad, orderBy, orderDirection, staleTime, }?: UseFilesOptions): UseFilesResult;
122
135
 
123
136
  export { type AuthProvider, Dropzone, type DropzoneProps, type DropzoneRenderProps, FileImage, type FileImageProps, FileList, type FileListProps, type FileListRenderProps, FilePreview, type FilePreviewProps, PlugableProvider, type PlugableProviderProps, type UseFilesOptions, type UseFilesResult, clearImageCache, useFiles, usePlugable };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React, { ReactNode, CSSProperties } from 'react';
3
- import { BucketClient, FileObject } from '@plugable-io/js';
3
+ import { BucketClient, FileObject, ListResponse } from '@plugable-io/js';
4
4
  export { FileObject, SearchOptions, UpdateOptions } from '@plugable-io/js';
5
5
 
6
6
  type AuthProvider = 'clerk' | 'supabase' | 'firebase';
@@ -11,9 +11,15 @@ interface PlugableProviderProps {
11
11
  authProvider?: AuthProvider;
12
12
  clerkJWTTemplate?: string;
13
13
  baseUrl?: string;
14
+ staleTime?: number;
14
15
  }
15
16
  type PlugableEventType = 'file.uploaded' | 'file.deleted';
16
17
  type EventHandler = (data?: any) => void;
18
+ interface CacheEntry {
19
+ files: FileObject[];
20
+ paging: ListResponse['paging'];
21
+ timestamp: number;
22
+ }
17
23
  interface PlugableContextValue {
18
24
  client: BucketClient;
19
25
  bucketId: string;
@@ -21,8 +27,12 @@ interface PlugableContextValue {
21
27
  emit: (event: PlugableEventType, data?: any) => void;
22
28
  getToken: () => Promise<string> | string;
23
29
  baseUrl?: string;
30
+ staleTime: number;
31
+ getCache: (key: string) => CacheEntry | undefined;
32
+ setCache: (key: string, entry: CacheEntry) => void;
33
+ invalidateCache: (pattern?: string) => void;
24
34
  }
25
- declare function PlugableProvider({ bucketId, children, getToken, authProvider, clerkJWTTemplate, baseUrl, }: PlugableProviderProps): react_jsx_runtime.JSX.Element;
35
+ declare function PlugableProvider({ bucketId, children, getToken, authProvider, clerkJWTTemplate, baseUrl, staleTime, }: PlugableProviderProps): react_jsx_runtime.JSX.Element;
26
36
  declare function usePlugable(): PlugableContextValue;
27
37
 
28
38
  interface DropzoneProps {
@@ -104,6 +114,9 @@ interface UseFilesOptions {
104
114
  perPage?: number;
105
115
  mediaType?: string;
106
116
  autoLoad?: boolean;
117
+ orderBy?: 'created_at' | 'name' | 'byte_size';
118
+ orderDirection?: 'asc' | 'desc';
119
+ staleTime?: number;
107
120
  }
108
121
  interface UseFilesResult {
109
122
  files: FileObject[];
@@ -118,6 +131,6 @@ interface UseFilesResult {
118
131
  setPage: (page: number) => void;
119
132
  refresh: () => Promise<void>;
120
133
  }
121
- declare function useFiles({ metadata, startPage, perPage, mediaType, autoLoad, }?: UseFilesOptions): UseFilesResult;
134
+ declare function useFiles({ metadata, startPage, perPage, mediaType, autoLoad, orderBy, orderDirection, staleTime, }?: UseFilesOptions): UseFilesResult;
122
135
 
123
136
  export { type AuthProvider, Dropzone, type DropzoneProps, type DropzoneRenderProps, FileImage, type FileImageProps, FileList, type FileListProps, type FileListRenderProps, FilePreview, type FilePreviewProps, PlugableProvider, type PlugableProviderProps, type UseFilesOptions, type UseFilesResult, clearImageCache, useFiles, usePlugable };
package/dist/index.js CHANGED
@@ -100,9 +100,12 @@ function PlugableProvider({
100
100
  getToken,
101
101
  authProvider,
102
102
  clerkJWTTemplate,
103
- baseUrl
103
+ baseUrl,
104
+ staleTime = 5 * 60 * 1e3
105
+ // Default 5 minutes
104
106
  }) {
105
107
  const listenersRef = (0, import_react.useRef)({});
108
+ const [cache, setCacheState] = (0, import_react.useState)(/* @__PURE__ */ new Map());
106
109
  const client = (0, import_react.useMemo)(() => {
107
110
  if (!getToken && !authProvider) {
108
111
  throw new Error(
@@ -128,6 +131,34 @@ function PlugableProvider({
128
131
  }, []);
129
132
  const emit = (0, import_react.useCallback)((event, data) => {
130
133
  listenersRef.current[event]?.forEach((handler) => handler(data));
134
+ if (event === "file.uploaded" || event === "file.deleted") {
135
+ setCacheState(/* @__PURE__ */ new Map());
136
+ }
137
+ }, []);
138
+ const getCache = (0, import_react.useCallback)((key) => {
139
+ return cache.get(key);
140
+ }, [cache]);
141
+ const setCache = (0, import_react.useCallback)((key, entry) => {
142
+ setCacheState((prev) => {
143
+ const next = new Map(prev);
144
+ next.set(key, entry);
145
+ return next;
146
+ });
147
+ }, []);
148
+ const invalidateCache = (0, import_react.useCallback)((pattern) => {
149
+ if (!pattern) {
150
+ setCacheState(/* @__PURE__ */ new Map());
151
+ return;
152
+ }
153
+ setCacheState((prev) => {
154
+ const next = new Map(prev);
155
+ for (const key of prev.keys()) {
156
+ if (key.includes(pattern)) {
157
+ next.delete(key);
158
+ }
159
+ }
160
+ return next;
161
+ });
131
162
  }, []);
132
163
  const value = (0, import_react.useMemo)(
133
164
  () => ({
@@ -136,9 +167,13 @@ function PlugableProvider({
136
167
  on,
137
168
  emit,
138
169
  getToken: client.tokenGetter,
139
- baseUrl
170
+ baseUrl,
171
+ staleTime,
172
+ getCache,
173
+ setCache,
174
+ invalidateCache
140
175
  }),
141
- [client, bucketId, on, emit, baseUrl]
176
+ [client, bucketId, on, emit, baseUrl, staleTime, getCache, setCache, invalidateCache]
142
177
  );
143
178
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlugableContext.Provider, { value, children });
144
179
  }
@@ -369,16 +404,48 @@ function useFiles({
369
404
  startPage = 1,
370
405
  perPage = 20,
371
406
  mediaType,
372
- autoLoad = true
407
+ autoLoad = true,
408
+ orderBy,
409
+ orderDirection,
410
+ staleTime
373
411
  } = {}) {
374
- const { client, on } = usePlugable();
412
+ const { client, on, getCache, setCache, staleTime: providerStaleTime } = usePlugable();
375
413
  const [files, setFiles] = (0, import_react3.useState)([]);
376
414
  const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
377
415
  const [page, setPage] = (0, import_react3.useState)(startPage);
378
416
  const [hasNext, setHasNext] = (0, import_react3.useState)(false);
417
+ const effectiveStaleTime = staleTime ?? providerStaleTime;
379
418
  const metadataKey = JSON.stringify(metadata);
380
419
  const stableMetadata = (0, import_react3.useMemo)(() => metadata, [metadataKey]);
381
- const fetchFiles = (0, import_react3.useCallback)(async (pageNum) => {
420
+ const paramsKeyWithPage = (0, import_react3.useMemo)(() => JSON.stringify({
421
+ metadata: stableMetadata,
422
+ mediaType,
423
+ perPage,
424
+ orderBy,
425
+ orderDirection,
426
+ page
427
+ }), [stableMetadata, mediaType, perPage, orderBy, orderDirection, page]);
428
+ const fetchFiles = (0, import_react3.useCallback)(async (pageNum, skipCache = false) => {
429
+ if (!skipCache) {
430
+ const cacheKey = JSON.stringify({
431
+ metadata: stableMetadata,
432
+ mediaType,
433
+ perPage,
434
+ orderBy,
435
+ orderDirection,
436
+ page: pageNum
437
+ });
438
+ const cachedEntry = getCache(cacheKey);
439
+ if (cachedEntry) {
440
+ const age = Date.now() - cachedEntry.timestamp;
441
+ const isStale = age > effectiveStaleTime;
442
+ if (!isStale) {
443
+ setFiles(cachedEntry.files);
444
+ setHasNext(cachedEntry.paging.has_next_page);
445
+ return;
446
+ }
447
+ }
448
+ }
382
449
  setIsLoading(true);
383
450
  try {
384
451
  const options = {
@@ -386,9 +453,24 @@ function useFiles({
386
453
  media_type: mediaType,
387
454
  page: pageNum,
388
455
  per_page: perPage,
389
- with_download_url: true
456
+ with_download_url: true,
457
+ ...orderBy && { order_by: orderBy },
458
+ ...orderDirection && { order_direction: orderDirection }
390
459
  };
391
460
  const response = await client.list(options);
461
+ const cacheKey = JSON.stringify({
462
+ metadata: stableMetadata,
463
+ mediaType,
464
+ perPage,
465
+ orderBy,
466
+ orderDirection,
467
+ page: pageNum
468
+ });
469
+ setCache(cacheKey, {
470
+ files: response.files,
471
+ paging: response.paging,
472
+ timestamp: Date.now()
473
+ });
392
474
  setFiles(response.files);
393
475
  setHasNext(response.paging.has_next_page);
394
476
  } catch (err) {
@@ -398,15 +480,24 @@ function useFiles({
398
480
  } finally {
399
481
  setIsLoading(false);
400
482
  }
401
- }, [client, stableMetadata, mediaType, perPage]);
483
+ }, [client, stableMetadata, mediaType, perPage, orderBy, orderDirection, getCache, setCache, effectiveStaleTime]);
402
484
  (0, import_react3.useEffect)(() => {
403
- if (autoLoad) {
404
- fetchFiles(page);
485
+ const cachedEntry = getCache(paramsKeyWithPage);
486
+ if (cachedEntry) {
487
+ const age = Date.now() - cachedEntry.timestamp;
488
+ const isStale = age > effectiveStaleTime;
489
+ setFiles(cachedEntry.files);
490
+ setHasNext(cachedEntry.paging.has_next_page);
491
+ if (isStale && autoLoad) {
492
+ fetchFiles(page, true);
493
+ }
494
+ } else if (autoLoad) {
495
+ fetchFiles(page, true);
405
496
  }
406
- }, [fetchFiles, page, autoLoad]);
497
+ }, [paramsKeyWithPage, autoLoad, getCache, effectiveStaleTime, fetchFiles, page]);
407
498
  (0, import_react3.useEffect)(() => {
408
499
  const unsubscribe = on("file.uploaded", () => {
409
- fetchFiles(page);
500
+ fetchFiles(page, true);
410
501
  });
411
502
  return unsubscribe;
412
503
  }, [on, fetchFiles, page]);
@@ -419,7 +510,7 @@ function useFiles({
419
510
  setPage((p) => Math.max(1, p - 1));
420
511
  }, []);
421
512
  const refresh = (0, import_react3.useCallback)(async () => {
422
- await fetchFiles(page);
513
+ await fetchFiles(page, true);
423
514
  }, [fetchFiles, page]);
424
515
  return {
425
516
  files,
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/PlugableProvider.tsx
2
- import { createContext, useContext, useMemo, useCallback, useRef } from "react";
2
+ import { createContext, useContext, useMemo, useCallback, useRef, useState } from "react";
3
3
  import { BucketClient } from "@plugable-io/js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  var PlugableContext = createContext(null);
@@ -57,9 +57,12 @@ function PlugableProvider({
57
57
  getToken,
58
58
  authProvider,
59
59
  clerkJWTTemplate,
60
- baseUrl
60
+ baseUrl,
61
+ staleTime = 5 * 60 * 1e3
62
+ // Default 5 minutes
61
63
  }) {
62
64
  const listenersRef = useRef({});
65
+ const [cache, setCacheState] = useState(/* @__PURE__ */ new Map());
63
66
  const client = useMemo(() => {
64
67
  if (!getToken && !authProvider) {
65
68
  throw new Error(
@@ -85,6 +88,34 @@ function PlugableProvider({
85
88
  }, []);
86
89
  const emit = useCallback((event, data) => {
87
90
  listenersRef.current[event]?.forEach((handler) => handler(data));
91
+ if (event === "file.uploaded" || event === "file.deleted") {
92
+ setCacheState(/* @__PURE__ */ new Map());
93
+ }
94
+ }, []);
95
+ const getCache = useCallback((key) => {
96
+ return cache.get(key);
97
+ }, [cache]);
98
+ const setCache = useCallback((key, entry) => {
99
+ setCacheState((prev) => {
100
+ const next = new Map(prev);
101
+ next.set(key, entry);
102
+ return next;
103
+ });
104
+ }, []);
105
+ const invalidateCache = useCallback((pattern) => {
106
+ if (!pattern) {
107
+ setCacheState(/* @__PURE__ */ new Map());
108
+ return;
109
+ }
110
+ setCacheState((prev) => {
111
+ const next = new Map(prev);
112
+ for (const key of prev.keys()) {
113
+ if (key.includes(pattern)) {
114
+ next.delete(key);
115
+ }
116
+ }
117
+ return next;
118
+ });
88
119
  }, []);
89
120
  const value = useMemo(
90
121
  () => ({
@@ -93,9 +124,13 @@ function PlugableProvider({
93
124
  on,
94
125
  emit,
95
126
  getToken: client.tokenGetter,
96
- baseUrl
127
+ baseUrl,
128
+ staleTime,
129
+ getCache,
130
+ setCache,
131
+ invalidateCache
97
132
  }),
98
- [client, bucketId, on, emit, baseUrl]
133
+ [client, bucketId, on, emit, baseUrl, staleTime, getCache, setCache, invalidateCache]
99
134
  );
100
135
  return /* @__PURE__ */ jsx(PlugableContext.Provider, { value, children });
101
136
  }
@@ -108,7 +143,7 @@ function usePlugable() {
108
143
  }
109
144
 
110
145
  // src/components/Dropzone.tsx
111
- import React, { useCallback as useCallback2, useState } from "react";
146
+ import React, { useCallback as useCallback2, useState as useState2 } from "react";
112
147
  import { BucketClient as BucketClient2 } from "@plugable-io/js";
113
148
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
114
149
  function Dropzone({
@@ -134,10 +169,10 @@ function Dropzone({
134
169
  }
135
170
  return defaultClient;
136
171
  }, [_bucketId, defaultClient, getToken, baseUrl]);
137
- const [isDragActive, setIsDragActive] = useState(false);
138
- const [isUploading, setIsUploading] = useState(false);
139
- const [uploadProgress, setUploadProgress] = useState({});
140
- const [uploadedFiles, setUploadedFiles] = useState([]);
172
+ const [isDragActive, setIsDragActive] = useState2(false);
173
+ const [isUploading, setIsUploading] = useState2(false);
174
+ const [uploadProgress, setUploadProgress] = useState2({});
175
+ const [uploadedFiles, setUploadedFiles] = useState2([]);
141
176
  const fileInputRef = React.useRef(null);
142
177
  const uploadFiles = useCallback2(
143
178
  async (files) => {
@@ -320,22 +355,54 @@ function Dropzone({
320
355
  }
321
356
 
322
357
  // src/hooks/useFiles.ts
323
- import { useState as useState2, useCallback as useCallback3, useEffect, useMemo as useMemo2 } from "react";
358
+ import { useState as useState3, useCallback as useCallback3, useEffect, useMemo as useMemo2 } from "react";
324
359
  function useFiles({
325
360
  metadata,
326
361
  startPage = 1,
327
362
  perPage = 20,
328
363
  mediaType,
329
- autoLoad = true
364
+ autoLoad = true,
365
+ orderBy,
366
+ orderDirection,
367
+ staleTime
330
368
  } = {}) {
331
- const { client, on } = usePlugable();
332
- const [files, setFiles] = useState2([]);
333
- const [isLoading, setIsLoading] = useState2(false);
334
- const [page, setPage] = useState2(startPage);
335
- const [hasNext, setHasNext] = useState2(false);
369
+ const { client, on, getCache, setCache, staleTime: providerStaleTime } = usePlugable();
370
+ const [files, setFiles] = useState3([]);
371
+ const [isLoading, setIsLoading] = useState3(false);
372
+ const [page, setPage] = useState3(startPage);
373
+ const [hasNext, setHasNext] = useState3(false);
374
+ const effectiveStaleTime = staleTime ?? providerStaleTime;
336
375
  const metadataKey = JSON.stringify(metadata);
337
376
  const stableMetadata = useMemo2(() => metadata, [metadataKey]);
338
- const fetchFiles = useCallback3(async (pageNum) => {
377
+ const paramsKeyWithPage = useMemo2(() => JSON.stringify({
378
+ metadata: stableMetadata,
379
+ mediaType,
380
+ perPage,
381
+ orderBy,
382
+ orderDirection,
383
+ page
384
+ }), [stableMetadata, mediaType, perPage, orderBy, orderDirection, page]);
385
+ const fetchFiles = useCallback3(async (pageNum, skipCache = false) => {
386
+ if (!skipCache) {
387
+ const cacheKey = JSON.stringify({
388
+ metadata: stableMetadata,
389
+ mediaType,
390
+ perPage,
391
+ orderBy,
392
+ orderDirection,
393
+ page: pageNum
394
+ });
395
+ const cachedEntry = getCache(cacheKey);
396
+ if (cachedEntry) {
397
+ const age = Date.now() - cachedEntry.timestamp;
398
+ const isStale = age > effectiveStaleTime;
399
+ if (!isStale) {
400
+ setFiles(cachedEntry.files);
401
+ setHasNext(cachedEntry.paging.has_next_page);
402
+ return;
403
+ }
404
+ }
405
+ }
339
406
  setIsLoading(true);
340
407
  try {
341
408
  const options = {
@@ -343,9 +410,24 @@ function useFiles({
343
410
  media_type: mediaType,
344
411
  page: pageNum,
345
412
  per_page: perPage,
346
- with_download_url: true
413
+ with_download_url: true,
414
+ ...orderBy && { order_by: orderBy },
415
+ ...orderDirection && { order_direction: orderDirection }
347
416
  };
348
417
  const response = await client.list(options);
418
+ const cacheKey = JSON.stringify({
419
+ metadata: stableMetadata,
420
+ mediaType,
421
+ perPage,
422
+ orderBy,
423
+ orderDirection,
424
+ page: pageNum
425
+ });
426
+ setCache(cacheKey, {
427
+ files: response.files,
428
+ paging: response.paging,
429
+ timestamp: Date.now()
430
+ });
349
431
  setFiles(response.files);
350
432
  setHasNext(response.paging.has_next_page);
351
433
  } catch (err) {
@@ -355,15 +437,24 @@ function useFiles({
355
437
  } finally {
356
438
  setIsLoading(false);
357
439
  }
358
- }, [client, stableMetadata, mediaType, perPage]);
440
+ }, [client, stableMetadata, mediaType, perPage, orderBy, orderDirection, getCache, setCache, effectiveStaleTime]);
359
441
  useEffect(() => {
360
- if (autoLoad) {
361
- fetchFiles(page);
442
+ const cachedEntry = getCache(paramsKeyWithPage);
443
+ if (cachedEntry) {
444
+ const age = Date.now() - cachedEntry.timestamp;
445
+ const isStale = age > effectiveStaleTime;
446
+ setFiles(cachedEntry.files);
447
+ setHasNext(cachedEntry.paging.has_next_page);
448
+ if (isStale && autoLoad) {
449
+ fetchFiles(page, true);
450
+ }
451
+ } else if (autoLoad) {
452
+ fetchFiles(page, true);
362
453
  }
363
- }, [fetchFiles, page, autoLoad]);
454
+ }, [paramsKeyWithPage, autoLoad, getCache, effectiveStaleTime, fetchFiles, page]);
364
455
  useEffect(() => {
365
456
  const unsubscribe = on("file.uploaded", () => {
366
- fetchFiles(page);
457
+ fetchFiles(page, true);
367
458
  });
368
459
  return unsubscribe;
369
460
  }, [on, fetchFiles, page]);
@@ -376,7 +467,7 @@ function useFiles({
376
467
  setPage((p) => Math.max(1, p - 1));
377
468
  }, []);
378
469
  const refresh = useCallback3(async () => {
379
- await fetchFiles(page);
470
+ await fetchFiles(page, true);
380
471
  }, [fetchFiles, page]);
381
472
  return {
382
473
  files,
@@ -423,7 +514,7 @@ function FileList({
423
514
  }
424
515
 
425
516
  // src/components/FileImage.tsx
426
- import { useEffect as useEffect2, useState as useState3 } from "react";
517
+ import { useEffect as useEffect2, useState as useState4 } from "react";
427
518
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
428
519
  var imageCache = /* @__PURE__ */ new Map();
429
520
  function FileImage({
@@ -438,9 +529,9 @@ function FileImage({
438
529
  onLoad,
439
530
  onError
440
531
  }) {
441
- const [imageSrc, setImageSrc] = useState3(null);
442
- const [isLoading, setIsLoading] = useState3(true);
443
- const [error, setError] = useState3(null);
532
+ const [imageSrc, setImageSrc] = useState4(null);
533
+ const [isLoading, setIsLoading] = useState4(true);
534
+ const [error, setError] = useState4(null);
444
535
  useEffect2(() => {
445
536
  let isMounted = true;
446
537
  let objectUrl = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plugable-io/react",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "React components and hooks for Plugable File Management API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -43,7 +43,7 @@
43
43
  "react-dom": ">=16.8.0"
44
44
  },
45
45
  "dependencies": {
46
- "@plugable-io/js": "^0.0.8"
46
+ "@plugable-io/js": "^0.0.9"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^20.0.0",