@fluid-app/portal-sdk 0.1.222 → 0.1.224

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.
@@ -9,7 +9,7 @@ require("./es-nxOxb57F.cjs");
9
9
  require("./SearchSort-Hwga1dIi.cjs");
10
10
  require("./dist-5XPflEEG.cjs");
11
11
  require("./dist-FHf4OHgt.cjs");
12
- const require_ShareablesScreen = require("./ShareablesScreen-DjA2eYY7.cjs");
12
+ const require_ShareablesScreen = require("./ShareablesScreen-DtR0PfSD.cjs");
13
13
  require("./PortalProductsApiProvider-DzjO6yix.cjs");
14
14
  exports.ShareablesScreen = require_ShareablesScreen.ShareablesScreen;
15
15
  exports.shareablesScreenPropertySchema = require_ShareablesScreen.shareablesScreenPropertySchema;
@@ -1667,15 +1667,7 @@ function MediaRowActionsMenu({ onDelete }) {
1667
1667
  })
1668
1668
  })] });
1669
1669
  }
1670
- /** Normalize a raw `kind` string into one of our filter buckets. Anything
1671
- * that isn't explicitly video/pdf is treated as image (matches the label
1672
- * fallback in getMediaKindLabel). */
1673
- function kindBucket(kind) {
1674
- if (kind === "video") return "video";
1675
- if (kind === "pdf") return "pdf";
1676
- return "image";
1677
- }
1678
- function MediaListingScreen({ onNavigate }) {
1670
+ function MediaListingScreen(_props) {
1679
1671
  const api = useShareablesApi();
1680
1672
  const repContext = useRepContext();
1681
1673
  const queryClient = (0, _tanstack_react_query.useQueryClient)();
@@ -1695,9 +1687,8 @@ function MediaListingScreen({ onNavigate }) {
1695
1687
  }) }), []));
1696
1688
  const [searchTerm, setSearchTerm] = (0, react.useState)("");
1697
1689
  const [debouncedSearch, setDebouncedSearch] = (0, react.useState)("");
1698
- const [sortValue, setSortValue] = (0, react.useState)("title_asc");
1690
+ const [sortValue, setSortValue] = (0, react.useState)("newest");
1699
1691
  const [viewMode, setViewMode] = (0, react.useState)("grid");
1700
- const [page, setPage] = (0, react.useState)(1);
1701
1692
  const [ownerFilter, setOwnerFilter] = (0, react.useState)("all");
1702
1693
  const [kindFilter, setKindFilter] = (0, react.useState)("all");
1703
1694
  (0, react.useEffect)(() => {
@@ -1706,14 +1697,7 @@ function MediaListingScreen({ onNavigate }) {
1706
1697
  }, 300);
1707
1698
  return () => clearTimeout(timer);
1708
1699
  }, [searchTerm]);
1709
- (0, react.useEffect)(() => {
1710
- setPage(1);
1711
- }, [
1712
- debouncedSearch,
1713
- sortValue,
1714
- ownerFilter,
1715
- kindFilter
1716
- ]);
1700
+ const observerTarget = (0, react.useRef)(null);
1717
1701
  const { mutate: deleteMedia, isPending: isDeleting } = (0, _tanstack_react_query.useMutation)({
1718
1702
  mutationFn: (id) => api.media.deleteMedia(id),
1719
1703
  onSuccess: () => {
@@ -1736,23 +1720,45 @@ function MediaListingScreen({ onNavigate }) {
1736
1720
  const confirmDelete = (0, react.useCallback)(() => {
1737
1721
  if (pendingDeleteId != null) deleteMedia(pendingDeleteId);
1738
1722
  }, [pendingDeleteId, deleteMedia]);
1739
- const { data, isLoading, isFetching, error } = (0, _tanstack_react_query.useQuery)({
1740
- queryKey: [...shareablesKeys.media.list(debouncedSearch, sortValue === "title_desc", repContext), page],
1741
- queryFn: async () => {
1742
- return await api.media.getMedia({
1743
- page,
1744
- per_page: PAGE_SIZE$3,
1745
- search_query: debouncedSearch || void 0,
1746
- sorted_by: sortValue
1723
+ const bffSort = sortValue === "title_asc" ? "title_asc" : sortValue === "title_desc" ? "title_desc" : void 0;
1724
+ const bffKind = kindFilter === "all" ? void 0 : kindFilter;
1725
+ const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage, error } = (0, _tanstack_react_query.useInfiniteQuery)({
1726
+ queryKey: [
1727
+ ...shareablesKeys.media.list(debouncedSearch, sortValue === "title_desc", repContext),
1728
+ sortValue,
1729
+ kindFilter
1730
+ ],
1731
+ queryFn: async ({ pageParam }) => {
1732
+ return await api.media.list({
1733
+ "filter[title]": debouncedSearch || void 0,
1734
+ "filter[content_format]": bffKind,
1735
+ "page[cursor]": pageParam,
1736
+ "page[limit]": PAGE_SIZE$3,
1737
+ sort: bffSort
1747
1738
  });
1748
1739
  },
1740
+ getNextPageParam: (lastPage) => lastPage.nextCursor ?? void 0,
1741
+ initialPageParam: void 0,
1749
1742
  placeholderData: _tanstack_react_query.keepPreviousData
1750
1743
  });
1751
- const totalCount = data?.meta?.total_count ?? 0;
1752
- const hasNextPage = page * PAGE_SIZE$3 < totalCount;
1753
- const allItems = data?.media ?? [];
1754
- const ownerFiltered = ownerFilter === "all" ? allItems : ownerFilter === "my" ? allItems.filter((item) => item.owner_type === "user") : allItems.filter((item) => item.owner_type === "company");
1755
- const filteredItems = kindFilter === "all" ? ownerFiltered : ownerFiltered.filter((item) => kindBucket(item.kind) === kindFilter);
1744
+ const allItems = (0, react.useMemo)(() => data?.pages.flatMap((p) => p.media) ?? [], [data?.pages]);
1745
+ const filteredItems = ownerFilter === "all" ? allItems : ownerFilter === "my" ? allItems.filter((item) => item.owner_type === "user") : allItems.filter((item) => item.owner_type === "company");
1746
+ (0, react.useEffect)(() => {
1747
+ const target = observerTarget.current;
1748
+ if (!target) return;
1749
+ const observer = new IntersectionObserver((entries) => {
1750
+ if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) fetchNextPage();
1751
+ }, {
1752
+ threshold: 0,
1753
+ rootMargin: "200px"
1754
+ });
1755
+ observer.observe(target);
1756
+ return () => observer.disconnect();
1757
+ }, [
1758
+ fetchNextPage,
1759
+ hasNextPage,
1760
+ isFetchingNextPage
1761
+ ]);
1756
1762
  const filterBar = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1757
1763
  className: "flex items-center gap-3",
1758
1764
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OwnerFilterTabs, {
@@ -1768,13 +1774,20 @@ function MediaListingScreen({ onNavigate }) {
1768
1774
  searchValue: searchTerm,
1769
1775
  onSearchChange: setSearchTerm,
1770
1776
  placeholder: "Search media...",
1771
- sortOptions: [{
1772
- label: "Name (A-Z)",
1773
- value: "title_asc"
1774
- }, {
1775
- label: "Name (Z-A)",
1776
- value: "title_desc"
1777
- }],
1777
+ sortOptions: [
1778
+ {
1779
+ label: "Newest",
1780
+ value: "newest"
1781
+ },
1782
+ {
1783
+ label: "Name (A-Z)",
1784
+ value: "title_asc"
1785
+ },
1786
+ {
1787
+ label: "Name (Z-A)",
1788
+ value: "title_desc"
1789
+ }
1790
+ ],
1778
1791
  sortValue,
1779
1792
  onSortChange: setSortValue
1780
1793
  })
@@ -1823,34 +1836,16 @@ function MediaListingScreen({ onNavigate }) {
1823
1836
  ]
1824
1837
  })]
1825
1838
  });
1826
- const paginationFooter = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1827
- className: "flex items-center justify-center gap-4 pt-4",
1828
- children: [
1829
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Button, {
1830
- variant: "outline",
1831
- size: "sm",
1832
- onClick: () => setPage((p) => p - 1),
1833
- disabled: page === 1 || isFetching,
1834
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronLeft, { className: "h-4 w-4" }), "Previous"]
1835
- }),
1836
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1837
- className: "text-muted-foreground text-sm",
1838
- children: ["Page ", page]
1839
- }),
1840
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Button, {
1841
- variant: "outline",
1842
- size: "sm",
1843
- onClick: () => setPage((p) => p + 1),
1844
- disabled: !hasNextPage || isFetching,
1845
- children: ["Next", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { className: "h-4 w-4" })]
1846
- })
1847
- ]
1848
- });
1839
+ const footer = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [isFetchingNextPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1840
+ className: "flex justify-center py-4",
1841
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" })
1842
+ }), error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1843
+ className: "bg-destructive/10 text-destructive rounded-lg px-3 py-2",
1844
+ children: ["Error: ", error.message]
1845
+ })] });
1849
1846
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShareableListLayout, {
1850
1847
  isLoading,
1851
- error,
1852
- errorMessage: "Failed to load media. Please try again.",
1853
- isEmpty: filteredItems.length === 0,
1848
+ isEmpty: !error && filteredItems.length === 0 && !isFetchingNextPage && !hasNextPage,
1854
1849
  emptyMessage: getFilteredEmptyMessage({
1855
1850
  searchTerm: debouncedSearch,
1856
1851
  ownerFilter,
@@ -1859,7 +1854,8 @@ function MediaListingScreen({ onNavigate }) {
1859
1854
  defaultMessage: "No media available."
1860
1855
  }),
1861
1856
  filters: filterBar,
1862
- footer: paginationFooter,
1857
+ footer,
1858
+ sentinelRef: observerTarget,
1863
1859
  loadingFilterShape: "search-view",
1864
1860
  children: viewMode === "grid" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1865
1861
  className: SHAREABLE_GRID_CLASS,
@@ -11153,6 +11149,7 @@ function createRawMediaAdapter(client) {
11153
11149
  "page[cursor]": params?.cursor,
11154
11150
  "page[limit]": params?.limit,
11155
11151
  "filter[title]": params?.["filter[title]"],
11152
+ "filter[content_format]": params?.["filter[content_format]"],
11156
11153
  sort: params?.sort
11157
11154
  });
11158
11155
  return {
@@ -11319,6 +11316,19 @@ function createMediaAdapter(client) {
11319
11316
  const cursorByPage = /* @__PURE__ */ new Map();
11320
11317
  let lastFilterKey = "";
11321
11318
  return {
11319
+ list: async (options) => {
11320
+ const response = await portAdapter.listMedia({
11321
+ cursor: options?.["page[cursor]"],
11322
+ limit: options?.["page[limit]"],
11323
+ "filter[title]": options?.["filter[title]"],
11324
+ "filter[content_format]": options?.["filter[content_format]"],
11325
+ sort: options?.sort
11326
+ });
11327
+ return {
11328
+ media: response.media.map(toBffMediumResponse),
11329
+ nextCursor: response.meta.pagination?.next_cursor ?? null
11330
+ };
11331
+ },
11322
11332
  getMedia: async (options) => {
11323
11333
  const pageNumber = options?.page ?? 1;
11324
11334
  const filterKey = `${options?.search_query ?? ""}|${options?.sorted_by ?? ""}`;
@@ -12209,4 +12219,4 @@ Object.defineProperty(exports, "shareablesScreenPropertySchema", {
12209
12219
  }
12210
12220
  });
12211
12221
 
12212
- //# sourceMappingURL=ShareablesScreen-DjA2eYY7.cjs.map
12222
+ //# sourceMappingURL=ShareablesScreen-DtR0PfSD.cjs.map