@postrun/react 0.1.0 → 1.0.0

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.js CHANGED
@@ -1,9 +1,11 @@
1
1
  "use client";
2
2
  import { createContext, memo, useMemo, useState, useEffect, Fragment as Fragment$1, useRef, createElement, useContext, useCallback } from 'react';
3
3
  import { useInfiniteQuery, useQuery, useMutation, QueryClient } from '@tanstack/react-query';
4
- import { createPostrunClient, profilesList, profilesGet, profilesCreate, profilesUpdate, profilesDelete, connectionsConnect, connectionsListByProfile, connectionsGet, connectionsListAccounts, connectionsSelect, connectionsDelete, mediaCreate, mediaGet, mediaUpdate, mediaDelete, postsList, postsGet, postsCreate, buildCreatePost, isPostPlatform, postsUpdate, postsDelete } from '@postrun/js';
4
+ import { createPostrunClient, profilesList, profilesGet, profilesCreate, profilesUpdate, profilesDelete, connectionsConnect, connectionsListByProfile, connectionsGet, connectionsListAccounts, connectionsSelect, connectionsDelete, mediaGet, mediaList, mediaUpdate, mediaDelete, postsList, postsGet, postsCreate, buildCreatePost, isPostPlatform, postsUpdate, postsDelete, mediaCreate, PostrunError } from '@postrun/js';
5
+ import Nango, { AuthError } from '@nangohq/frontend';
6
+ import pWaitFor, { TimeoutError } from 'p-wait-for';
7
+ import pLimit from 'p-limit';
5
8
  import pRetry, { AbortError } from 'p-retry';
6
- import pWaitFor from 'p-wait-for';
7
9
  import axios, { isAxiosError } from 'axios';
8
10
  import { enrichTweet, TweetContainer, TweetHeader, TweetInReplyTo, TweetBody, TweetMedia, QuotedTweet } from 'react-tweet';
9
11
  import { FiMessageCircle, FiRepeat, FiHeart, FiBarChart2, FiShare, FiGlobe, FiUsers, FiThumbsUp, FiMessageSquare, FiSend } from 'react-icons/fi';
@@ -89,7 +91,8 @@ var profileKeys = {
89
91
  list: (query) => [...profileKeys.lists(), query ?? {}],
90
92
  // Nested under lists() so a create/update/delete invalidating lists() also
91
93
  // refreshes the infinite cache; distinct tail so the two cache shapes (a
92
- // single Page vs accumulated pages) never collide on one key.
94
+ // single Page vs accumulated pages) never collide on one key. The filter omits
95
+ // limit/offset — the infinite hook owns pagination, so they never key the cache.
93
96
  infinite: (query) => [...profileKeys.lists(), "infinite", query ?? {}],
94
97
  details: () => [...profileKeys.all, "detail"],
95
98
  detail: (id) => [...profileKeys.details(), id]
@@ -100,20 +103,28 @@ var postKeys = {
100
103
  list: (query) => [...postKeys.lists(), query ?? {}],
101
104
  // Nested under lists() so a create/update/delete invalidating lists() also
102
105
  // refreshes the infinite cache; distinct tail so the two cache shapes (a
103
- // single Page vs accumulated pages) never collide on one key.
106
+ // single Page vs accumulated pages) never collide on one key. The filter omits
107
+ // limit/offset — the infinite hook owns pagination, so they never key the cache.
104
108
  infinite: (query) => [...postKeys.lists(), "infinite", query ?? {}],
105
109
  details: () => [...postKeys.all, "detail"],
106
110
  detail: (id) => [...postKeys.details(), id]
107
111
  };
108
112
  var mediaKeys = {
109
113
  all: [ROOT, "media"],
114
+ lists: () => [...mediaKeys.all, "list"],
115
+ list: (query) => [...mediaKeys.lists(), query ?? {}],
116
+ // Nested under lists() so an upload/update/delete invalidating lists() also
117
+ // refreshes the infinite cache; distinct tail so the two cache shapes (a
118
+ // single Page vs accumulated pages) never collide on one key. The filter omits
119
+ // limit/offset — the infinite hook owns pagination, so they never key the cache.
120
+ infinite: (query) => [...mediaKeys.lists(), "infinite", query ?? {}],
110
121
  details: () => [...mediaKeys.all, "detail"],
111
122
  detail: (id) => [...mediaKeys.details(), id]
112
123
  };
113
124
  var connectionKeys = {
114
125
  all: [ROOT, "connections"],
115
126
  lists: () => [...connectionKeys.all, "list"],
116
- list: (profileId) => [...connectionKeys.lists(), profileId],
127
+ list: (profileId, filter) => [...connectionKeys.lists(), profileId, filter ?? {}],
117
128
  details: () => [...connectionKeys.all, "detail"],
118
129
  detail: (id) => [...connectionKeys.details(), id],
119
130
  accounts: (id) => [...connectionKeys.all, "accounts", id]
@@ -185,36 +196,256 @@ function useDeleteProfile() {
185
196
  queryClient
186
197
  );
187
198
  }
188
-
189
- // src/navigate.ts
190
- function navigate(url) {
191
- window.location.assign(url);
199
+ var PENDING = { status: "connected_pending" };
200
+ var CANCELLED = { status: "cancelled" };
201
+ var active = (connection) => ({
202
+ status: "active",
203
+ connection
204
+ });
205
+ var failed = (reason) => ({
206
+ status: "error",
207
+ reason
208
+ });
209
+ function outcomeForAuthError(error) {
210
+ switch (error.type) {
211
+ case "window_closed":
212
+ return CANCELLED;
213
+ case "blocked_by_browser":
214
+ return failed("popup_blocked");
215
+ case "missing_auth_token":
216
+ case "invalid_host_url":
217
+ case "missing_credentials":
218
+ case "connection_test_failed":
219
+ case "missing_connect_session_token":
220
+ case "connection_validation_failed":
221
+ case "resource_capped":
222
+ case "unknown_error":
223
+ return failed("auth_failed");
224
+ }
225
+ }
226
+ async function grant(authorize) {
227
+ try {
228
+ return { ok: true, connectionId: await authorize() };
229
+ } catch (error) {
230
+ if (error instanceof AuthError) {
231
+ return { ok: false, outcome: outcomeForAuthError(error) };
232
+ }
233
+ return { ok: false, outcome: failed("auth_failed") };
234
+ }
235
+ }
236
+ async function awaitGrantedConnection(deps, nangoConnectionId) {
237
+ try {
238
+ return await pWaitFor(
239
+ async () => {
240
+ const rows = await deps.listByNangoConnectionId(nangoConnectionId);
241
+ const match = rows[0];
242
+ return match ? pWaitFor.resolveWith(match) : false;
243
+ },
244
+ { interval: deps.pollIntervalMs, timeout: deps.pollTimeoutMs }
245
+ );
246
+ } catch (error) {
247
+ if (error instanceof TimeoutError) {
248
+ return null;
249
+ }
250
+ throw error;
251
+ }
252
+ }
253
+ async function bindPendingConnection(deps, connection) {
254
+ let accounts;
255
+ try {
256
+ accounts = await deps.discoverAccounts(connection.id);
257
+ } catch (error) {
258
+ if (error instanceof PostrunError && error.code === "not_implemented") {
259
+ return PENDING;
260
+ }
261
+ throw error;
262
+ }
263
+ if (accounts.length === 0) {
264
+ return PENDING;
265
+ }
266
+ const chosen = await deps.chooseAccount(accounts);
267
+ try {
268
+ return active(await deps.selectAccount(connection.id, chosen));
269
+ } catch (error) {
270
+ if (error instanceof PostrunError) {
271
+ return error.code === "connection_reauth_required" ? failed("reauth_required") : failed("select_failed");
272
+ }
273
+ throw error;
274
+ }
275
+ }
276
+ async function runEmbeddedConnect(deps) {
277
+ const granted = await grant(deps.authorize);
278
+ if (!granted.ok) {
279
+ return granted.outcome;
280
+ }
281
+ let connection;
282
+ try {
283
+ connection = await awaitGrantedConnection(deps, granted.connectionId);
284
+ } catch {
285
+ return failed("connection_not_found");
286
+ }
287
+ if (connection === null) {
288
+ return PENDING;
289
+ }
290
+ if (connection.external_account_id !== null) {
291
+ return active(connection);
292
+ }
293
+ try {
294
+ return await bindPendingConnection(deps, connection);
295
+ } catch {
296
+ return failed("select_failed");
297
+ }
192
298
  }
193
299
 
194
300
  // src/connections.ts
195
- function useConnect() {
196
- const { client, queryClient } = usePostrun();
197
- return useMutation(
198
- {
199
- mutationFn: async ({ profileId, platform }) => {
200
- const session = (await connectionsConnect({
301
+ var POLL_INTERVAL_MS = 1500;
302
+ var POLL_TIMEOUT_MS = 15e3;
303
+ function useConnect({
304
+ profileId,
305
+ platform,
306
+ onConnected
307
+ }) {
308
+ const { client } = usePostrun();
309
+ const [state, setState] = useState({ phase: "preparing" });
310
+ const [remintNonce, setRemintNonce] = useState(0);
311
+ const sessionRef = useRef(null);
312
+ const pickRef = useRef(null);
313
+ const inFlightRef = useRef(false);
314
+ const flowGenRef = useRef(0);
315
+ const onConnectedRef = useRef(onConnected);
316
+ useEffect(() => {
317
+ onConnectedRef.current = onConnected;
318
+ }, [onConnected]);
319
+ const abandonFlow = useCallback(() => {
320
+ flowGenRef.current += 1;
321
+ inFlightRef.current = false;
322
+ const pick = pickRef.current;
323
+ pickRef.current = null;
324
+ pick?.reject(new Error("connect flow abandoned"));
325
+ }, []);
326
+ useEffect(() => {
327
+ let abandoned = false;
328
+ setState({ phase: "preparing" });
329
+ sessionRef.current = null;
330
+ connectionsConnect({ client, path: { id: profileId }, body: { platform } }).then(({ data }) => {
331
+ if (abandoned || !data) return;
332
+ sessionRef.current = {
333
+ token: data.connect_session_token,
334
+ providerConfigKey: data.provider_config_key,
335
+ host: data.nango_host
336
+ };
337
+ setState({ phase: "idle" });
338
+ }).catch(() => {
339
+ if (!abandoned) setState({ phase: "error", reason: "auth_failed" });
340
+ });
341
+ return () => {
342
+ abandoned = true;
343
+ abandonFlow();
344
+ };
345
+ }, [client, profileId, platform, remintNonce, abandonFlow]);
346
+ const start = useCallback(() => {
347
+ const session = sessionRef.current;
348
+ if (!session || inFlightRef.current) return;
349
+ inFlightRef.current = true;
350
+ const gen = flowGenRef.current;
351
+ const isCurrent = () => flowGenRef.current === gen;
352
+ setState({ phase: "connecting" });
353
+ void runEmbeddedConnect({
354
+ // Nango lives INSIDE `authorize` so a SYNCHRONOUS throw (invalid host /
355
+ // missing token — the Nango SDK throws `AuthError` synchronously) becomes a
356
+ // promise rejection that `grant()` maps to `auth_failed`, never an uncaught
357
+ // throw escaping the click and wedging `inFlightRef`. Gesture timing still
358
+ // holds: `authorize()` is invoked SYNCHRONOUSLY down the
359
+ // start → runEmbeddedConnect → grant chain (each `await`'s operand is
360
+ // evaluated before it suspends), so `nango.auth()`'s `window.open` fires
361
+ // inside the user gesture, with no `await` before it.
362
+ authorize: async () => {
363
+ const nango = new Nango({
364
+ host: session.host,
365
+ connectSessionToken: session.token
366
+ });
367
+ const result = await nango.auth(session.providerConfigKey, {
368
+ detectClosedAuthWindow: true
369
+ });
370
+ return result.connectionId;
371
+ },
372
+ chooseAccount: (accounts) => new Promise((resolve, reject) => {
373
+ if (!isCurrent()) {
374
+ reject(new Error("connect flow abandoned"));
375
+ return;
376
+ }
377
+ pickRef.current = { resolve, reject };
378
+ setState({ phase: "picking", accounts });
379
+ }),
380
+ listByNangoConnectionId: async (nangoConnectionId) => {
381
+ const { data } = await connectionsListByProfile({
201
382
  client,
202
383
  path: { id: profileId },
203
- body: { platform }
204
- })).data;
205
- navigate(session.connect_url);
206
- return session;
384
+ query: { nango_connection_id: nangoConnectionId }
385
+ });
386
+ return data?.data ?? [];
387
+ },
388
+ discoverAccounts: async (connectionId) => {
389
+ const { data } = await connectionsListAccounts({
390
+ client,
391
+ path: { id: connectionId }
392
+ });
393
+ return data?.data ?? [];
394
+ },
395
+ selectAccount: async (connectionId, externalAccountId) => {
396
+ const { data } = await connectionsSelect({
397
+ client,
398
+ path: { id: connectionId },
399
+ body: { external_account_id: externalAccountId }
400
+ });
401
+ if (!data) throw new Error("select returned no connection");
402
+ return data;
403
+ },
404
+ pollIntervalMs: POLL_INTERVAL_MS,
405
+ pollTimeoutMs: POLL_TIMEOUT_MS
406
+ }).then((outcome) => {
407
+ if (!isCurrent()) return;
408
+ inFlightRef.current = false;
409
+ pickRef.current = null;
410
+ switch (outcome.status) {
411
+ case "active":
412
+ setState({ phase: "active", connection: outcome.connection });
413
+ onConnectedRef.current?.(outcome.connection);
414
+ return;
415
+ case "connected_pending":
416
+ setState({ phase: "connected_pending" });
417
+ return;
418
+ case "cancelled":
419
+ setState({ phase: "cancelled" });
420
+ return;
421
+ case "error":
422
+ setState({ phase: "error", reason: outcome.reason });
423
+ return;
207
424
  }
208
- },
209
- queryClient
210
- );
425
+ });
426
+ }, [client, profileId]);
427
+ const select = useCallback((externalAccountId) => {
428
+ const pick = pickRef.current;
429
+ if (!pick) return;
430
+ pickRef.current = null;
431
+ setState({ phase: "connecting" });
432
+ pick.resolve(externalAccountId);
433
+ }, []);
434
+ const reset = useCallback(() => {
435
+ setRemintNonce((n) => n + 1);
436
+ }, []);
437
+ return { state, start, select, reset };
211
438
  }
212
- function useConnections(profileId) {
439
+ function useConnections(profileId, filter) {
213
440
  const { client, queryClient } = usePostrun();
214
441
  return useQuery(
215
442
  {
216
- queryKey: connectionKeys.list(profileId),
217
- queryFn: async () => (await connectionsListByProfile({ client, path: { id: profileId } })).data,
443
+ queryKey: connectionKeys.list(profileId, filter),
444
+ queryFn: async () => (await connectionsListByProfile({
445
+ client,
446
+ path: { id: profileId },
447
+ query: filter
448
+ })).data,
218
449
  enabled: Boolean(profileId)
219
450
  },
220
451
  queryClient
@@ -269,6 +500,17 @@ function useDisconnect() {
269
500
  queryClient
270
501
  );
271
502
  }
503
+
504
+ // src/Connect.tsx
505
+ function Connect({
506
+ profileId,
507
+ platform,
508
+ onConnected,
509
+ children
510
+ }) {
511
+ const api = useConnect({ profileId, platform, onConnected });
512
+ return children(api);
513
+ }
272
514
  var UploadError = class extends Error {
273
515
  status;
274
516
  constructor(status, message) {
@@ -309,17 +551,7 @@ async function uploadBytes(target, file, options = {}) {
309
551
  }
310
552
 
311
553
  // src/media.ts
312
- var DOCUMENT_MIME = /^application\/(pdf|msword|vnd\.(openxmlformats-officedocument\.(wordprocessingml\.document|presentationml\.presentation)|ms-powerpoint))$/;
313
- function inferKind(contentType) {
314
- if (contentType === "image/gif") return "gif";
315
- if (contentType.startsWith("image/")) return "image";
316
- if (contentType.startsWith("video/")) return "video";
317
- if (DOCUMENT_MIME.test(contentType)) return "document";
318
- throw new Error(
319
- `Could not infer media kind from "${contentType}". Pass { kind } explicitly.`
320
- );
321
- }
322
- async function pollUntilSettled(client, id, signal) {
554
+ async function pollUntilSettled(client, id, signal, onTick) {
323
555
  let latest;
324
556
  await pWaitFor(
325
557
  async () => {
@@ -327,6 +559,7 @@ async function pollUntilSettled(client, id, signal) {
327
559
  throw new DOMException("Upload aborted", "AbortError");
328
560
  }
329
561
  latest = (await mediaGet({ client, path: { id } })).data;
562
+ onTick?.(latest);
330
563
  return latest.status === "ready" || latest.status === "failed";
331
564
  },
332
565
  { interval: 1500, timeout: 3e5 }
@@ -336,92 +569,134 @@ async function pollUntilSettled(client, id, signal) {
336
569
  }
337
570
  return latest;
338
571
  }
339
- function useMediaUpload() {
340
- const { client, queryClient } = usePostrun();
341
- const [status, setStatus] = useState("idle");
342
- const [progress, setProgress] = useState(0);
343
- const [media, setMedia] = useState(null);
344
- const [error, setError] = useState(null);
345
- const abortRef = useRef(null);
346
- const upload = useCallback(
347
- async (file, options) => {
348
- const contentType = options.contentType || file.type;
349
- if (!contentType) {
350
- throw new Error(
351
- "Could not determine the file's content type. Pass { contentType } explicitly."
352
- );
353
- }
354
- const kind = options.kind ?? inferKind(contentType);
355
- abortRef.current?.abort();
356
- const controller = new AbortController();
357
- abortRef.current = controller;
358
- setStatus("uploading");
359
- setProgress(0);
360
- setMedia(null);
361
- setError(null);
362
- try {
363
- const created = (await mediaCreate({
364
- client,
365
- body: {
366
- profile_id: options.profileId,
367
- kind,
368
- content_type: contentType,
369
- targets: options.targets,
370
- raw: options.raw,
371
- alt_text: options.altText,
372
- external_id: options.externalId,
373
- metadata: options.metadata
572
+ async function runUpload(client, file, options, signal, callbacks) {
573
+ const created = (await mediaCreate({
574
+ client,
575
+ body: {
576
+ profile_id: options.profileId,
577
+ kind: options.kind,
578
+ content_type: options.contentType,
579
+ targets: options.targets,
580
+ raw: options.raw,
581
+ alt_text: options.altText,
582
+ external_id: options.externalId,
583
+ metadata: options.metadata
584
+ }
585
+ })).data;
586
+ if (created.upload) {
587
+ const target = created.upload;
588
+ await pRetry(
589
+ async () => {
590
+ try {
591
+ await uploadBytes(target, file, {
592
+ onProgress: callbacks.onProgress,
593
+ signal
594
+ });
595
+ } catch (uploadError) {
596
+ if (uploadError instanceof UploadError && uploadError.status >= 400 && uploadError.status < 500) {
597
+ throw new AbortError(uploadError);
374
598
  }
375
- })).data;
376
- if (created.upload) {
377
- const target = created.upload;
378
- await pRetry(
379
- async () => {
380
- try {
381
- await uploadBytes(target, file, {
382
- onProgress: setProgress,
383
- signal: controller.signal
384
- });
385
- } catch (uploadError) {
386
- if (uploadError instanceof UploadError && uploadError.status >= 400 && uploadError.status < 500) {
387
- throw new AbortError(uploadError);
388
- }
389
- throw uploadError;
390
- }
391
- },
392
- { retries: 3, signal: controller.signal }
393
- );
394
- }
395
- setStatus("processing");
396
- const settled = await pollUntilSettled(
397
- client,
398
- created.id,
399
- controller.signal
400
- );
401
- queryClient.setQueryData(mediaKeys.detail(created.id), settled);
402
- setMedia(settled);
403
- setStatus(settled.status === "failed" ? "failed" : "ready");
404
- return settled;
405
- } catch (caught) {
406
- setError(caught);
407
- setStatus("failed");
408
- throw caught;
409
- } finally {
410
- if (abortRef.current === controller) {
411
- abortRef.current = null;
599
+ throw uploadError;
412
600
  }
601
+ },
602
+ { retries: 3, signal }
603
+ );
604
+ }
605
+ callbacks.onProcessing();
606
+ return pollUntilSettled(client, created.id, signal, callbacks.onPoll);
607
+ }
608
+ function toFileArray(files) {
609
+ if (files instanceof File) return [files];
610
+ return Array.from(files);
611
+ }
612
+ function useMediaUpload(options) {
613
+ const { client, queryClient } = usePostrun();
614
+ const [items, setItems] = useState([]);
615
+ const controllers = useRef(/* @__PURE__ */ new Map());
616
+ const limitRef = useRef(null);
617
+ if (!limitRef.current) {
618
+ limitRef.current = pLimit(options?.concurrency ?? 3);
619
+ }
620
+ const patch = useCallback(
621
+ (id, changes) => {
622
+ setItems(
623
+ (current) => current.map(
624
+ (item) => item.id === id ? { ...item, ...changes } : item
625
+ )
626
+ );
627
+ },
628
+ []
629
+ );
630
+ const add = useCallback(
631
+ (files, uploadOptions) => {
632
+ const queued = toFileArray(files).map((file) => ({
633
+ id: crypto.randomUUID(),
634
+ file,
635
+ status: "uploading",
636
+ progress: 0,
637
+ media: null,
638
+ error: null
639
+ }));
640
+ setItems((current) => [...current, ...queued]);
641
+ const limit = limitRef.current;
642
+ if (!limit) {
643
+ return Promise.resolve([]);
413
644
  }
645
+ const settlements = queued.map((item) => {
646
+ const controller = new AbortController();
647
+ controllers.current.set(item.id, controller);
648
+ return limit(() => {
649
+ if (controller.signal.aborted) {
650
+ throw new DOMException("Upload aborted", "AbortError");
651
+ }
652
+ return runUpload(client, item.file, uploadOptions, controller.signal, {
653
+ onProgress: (progress) => patch(item.id, { progress }),
654
+ onProcessing: () => patch(item.id, { status: "processing" }),
655
+ // Live server progress (stage + percent) each poll tick.
656
+ onPoll: (media) => patch(item.id, { media })
657
+ });
658
+ }).then((settled) => {
659
+ patch(item.id, {
660
+ status: settled.status === "failed" ? "failed" : "ready",
661
+ media: settled,
662
+ progress: 1
663
+ });
664
+ queryClient.setQueryData(mediaKeys.detail(settled.id), settled);
665
+ void queryClient.invalidateQueries({ queryKey: mediaKeys.lists() });
666
+ return settled;
667
+ }).catch((error) => {
668
+ if (controller.signal.aborted) {
669
+ return null;
670
+ }
671
+ patch(item.id, { status: "failed", error });
672
+ return null;
673
+ }).finally(() => {
674
+ controllers.current.delete(item.id);
675
+ });
676
+ });
677
+ return Promise.all(settlements).then(
678
+ (results) => results.filter((result) => result !== null)
679
+ );
414
680
  },
415
- [client, queryClient]
681
+ [client, queryClient, patch]
416
682
  );
417
- const cancel = useCallback(() => abortRef.current?.abort(), []);
683
+ const remove = useCallback((id) => {
684
+ controllers.current.get(id)?.abort();
685
+ controllers.current.delete(id);
686
+ setItems((current) => current.filter((item) => item.id !== id));
687
+ }, []);
418
688
  const reset = useCallback(() => {
419
- setStatus("idle");
420
- setProgress(0);
421
- setMedia(null);
422
- setError(null);
689
+ controllers.current.forEach((controller) => controller.abort());
690
+ controllers.current.clear();
691
+ setItems([]);
423
692
  }, []);
424
- return { upload, cancel, reset, status, progress, media, error };
693
+ const ready = items.flatMap(
694
+ (item) => item.status === "ready" && item.media ? [item.media] : []
695
+ );
696
+ const isUploading = items.some(
697
+ (item) => item.status === "uploading" || item.status === "processing"
698
+ );
699
+ return { items, ready, isUploading, add, remove, reset };
425
700
  }
426
701
  function useMedia(id) {
427
702
  const { client, queryClient } = usePostrun();
@@ -438,12 +713,33 @@ function useMedia(id) {
438
713
  queryClient
439
714
  );
440
715
  }
716
+ function useMediaList(query) {
717
+ const { client, queryClient } = usePostrun();
718
+ return useQuery(
719
+ {
720
+ queryKey: mediaKeys.list(query),
721
+ queryFn: async () => (await mediaList({ client, query })).data
722
+ },
723
+ queryClient
724
+ );
725
+ }
726
+ function useMediaInfinite(filters, options) {
727
+ const { client } = usePostrun();
728
+ return useInfiniteList({
729
+ queryKey: mediaKeys.infinite(filters),
730
+ limit: options?.pageSize,
731
+ fetchPage: async ({ limit, offset }) => (await mediaList({ client, query: { ...filters, limit, offset } })).data
732
+ });
733
+ }
441
734
  function useUpdateMedia() {
442
735
  const { client, queryClient } = usePostrun();
443
736
  return useMutation(
444
737
  {
445
738
  mutationFn: async ({ id, ...body }) => (await mediaUpdate({ client, path: { id }, body })).data,
446
- onSuccess: (result, { id }) => queryClient.setQueryData(mediaKeys.detail(id), result)
739
+ onSuccess: (result, { id }) => {
740
+ queryClient.setQueryData(mediaKeys.detail(id), result);
741
+ void queryClient.invalidateQueries({ queryKey: mediaKeys.lists() });
742
+ }
447
743
  },
448
744
  queryClient
449
745
  );
@@ -453,7 +749,10 @@ function useDeleteMedia() {
453
749
  return useMutation(
454
750
  {
455
751
  mutationFn: async (id) => (await mediaDelete({ client, path: { id } })).data,
456
- onSuccess: (_result, id) => queryClient.removeQueries({ queryKey: mediaKeys.detail(id) })
752
+ onSuccess: (_result, id) => {
753
+ queryClient.removeQueries({ queryKey: mediaKeys.detail(id) });
754
+ void queryClient.invalidateQueries({ queryKey: mediaKeys.lists() });
755
+ }
457
756
  },
458
757
  queryClient
459
758
  );
@@ -1279,6 +1578,6 @@ function LinkedInPostPreviewImpl({
1279
1578
  }
1280
1579
  var LinkedInPostPreview = memo(LinkedInPostPreviewImpl);
1281
1580
 
1282
- export { LinkedInPostPreview, PostrunProvider, UploadError, XPostPreview, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile };
1581
+ export { Connect, LinkedInPostPreview, PostrunProvider, UploadError, XPostPreview, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile };
1283
1582
  //# sourceMappingURL=index.js.map
1284
1583
  //# sourceMappingURL=index.js.map