@fluid-app/portal-sdk 0.1.638 → 0.1.640

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.
@@ -7,7 +7,7 @@ import "./MobileBottomSheet-nKP4TCOL.mjs";
7
7
  import "./static-dict-adapter-DUyazFbx.mjs";
8
8
  import "./SearchSort-CfBtIRVm.mjs";
9
9
  import "./use-share-link-mOe2vbOG.mjs";
10
- import { n as shareablesScreenPropertySchema, t as ShareablesScreen } from "./ShareablesScreen-Dqecn7OH.mjs";
10
+ import { n as shareablesScreenPropertySchema, t as ShareablesScreen } from "./ShareablesScreen-wKo6gZPp.mjs";
11
11
  import "./translation-api-context-factory-BTpIuUF0.mjs";
12
12
  import "./MobileActionSheet-cu_A10DU.mjs";
13
13
  import "./ResponsiveDialog-Cl9OVqlJ.mjs";
@@ -4350,6 +4350,30 @@ const PAGE_SIZE$6 = 20;
4350
4350
  * created was missing from the picker on reopen.
4351
4351
  */
4352
4352
  const PICKER_PLAYLISTS_KEY = ["add-to-playlist", "playlists"];
4353
+ /**
4354
+ * The member's own playlists, as the picker lists them. Declared once so a
4355
+ * host can run the same query ahead of the dialog (to learn whether playlists
4356
+ * are served at all) and leave its result in the cache for the dialog to use.
4357
+ */
4358
+ function playlistPickerQuery(api, search) {
4359
+ return {
4360
+ queryKey: [...PICKER_PLAYLISTS_KEY, search],
4361
+ queryFn: async ({ pageParam }) => {
4362
+ const response = await api.playlists.getPlaylists({
4363
+ "filter[title]": search || void 0,
4364
+ "filter[ownership]": "mine",
4365
+ "page[cursor]": pageParam,
4366
+ "page[limit]": PAGE_SIZE$6
4367
+ });
4368
+ return {
4369
+ playlists: response.playlists,
4370
+ nextCursor: response.meta.pagination.next_cursor
4371
+ };
4372
+ },
4373
+ getNextPageParam: (lastPage) => lastPage.nextCursor ?? void 0,
4374
+ initialPageParam: void 0
4375
+ };
4376
+ }
4353
4377
  function AddToPlaylistDialog({ open, onOpenChange, contentId, contentType, mode, resumePlaylistId = null, onPlaylistCreated, onAdded, restoreFocusTo }) {
4354
4378
  const api = useShareablesApi();
4355
4379
  const isMobile = useIsMobile$1();
@@ -4384,23 +4408,9 @@ function AddToPlaylistDialog({ open, onOpenChange, contentId, contentType, mode,
4384
4408
  return () => clearTimeout(timer);
4385
4409
  }, [search]);
4386
4410
  const { data, hasNextPage, isFetchingNextPage, fetchNextPage, error, isPending, isFetching, refetch } = useInfiniteQuery({
4387
- queryKey: [...PICKER_PLAYLISTS_KEY, debouncedSearch],
4411
+ ...playlistPickerQuery(api, debouncedSearch),
4388
4412
  enabled: open && effectiveMode === "pick",
4389
- placeholderData: keepPreviousData,
4390
- queryFn: async ({ pageParam }) => {
4391
- const response = await api.playlists.getPlaylists({
4392
- "filter[title]": debouncedSearch || void 0,
4393
- "filter[ownership]": "mine",
4394
- "page[cursor]": pageParam,
4395
- "page[limit]": PAGE_SIZE$6
4396
- });
4397
- return {
4398
- playlists: response.playlists,
4399
- nextCursor: response.meta.pagination.next_cursor
4400
- };
4401
- },
4402
- getNextPageParam: (lastPage) => lastPage.nextCursor ?? void 0,
4403
- initialPageParam: void 0
4413
+ placeholderData: keepPreviousData
4404
4414
  });
4405
4415
  const playlists = data?.pages.flatMap((page) => page.playlists) ?? [];
4406
4416
  const refusalAnnouncement = !lastRefusal ? "" : lastRefusal.title ? t("playlist_item_already_exists_named", { title: lastRefusal.title }) : t("playlist_item_already_exists");
@@ -9681,14 +9691,15 @@ const FilePicker = ({ config: configInput = {}, onFilesSelected, onClose, open,
9681
9691
  return result.success ? result.data : filePickerConfigSchema.parse({});
9682
9692
  }, [configInput]);
9683
9693
  const defaultTab = defaultMethod ?? "dam";
9684
- const [activeMethod, setActiveMethod] = useState(() => {
9694
+ const startMethod = (() => {
9685
9695
  if (config.allowedMethods && config.allowedMethods.length > 0) {
9686
9696
  const allowed = config.allowedMethods;
9687
9697
  if (allowed.includes(defaultTab)) return defaultTab;
9688
9698
  return allowed[0] ?? "dam";
9689
9699
  }
9690
9700
  return defaultTab;
9691
- });
9701
+ })();
9702
+ const [activeMethod, setActiveMethod] = useState(startMethod);
9692
9703
  useEffect(() => {
9693
9704
  if (config.allowedMethods && config.allowedMethods.length > 0 && !config.allowedMethods.includes(activeMethod)) setActiveMethod(config.allowedMethods[0] ?? "dam");
9694
9705
  }, [config.allowedMethods, activeMethod]);
@@ -9725,7 +9736,7 @@ const FilePicker = ({ config: configInput = {}, onFilesSelected, onClose, open,
9725
9736
  const [viewMode, setViewMode] = useState("grid");
9726
9737
  const [showNamesOnMedia, setShowNamesOnMedia] = useState(true);
9727
9738
  const resetPicker = useCallback(() => {
9728
- setActiveMethod(defaultTab);
9739
+ setActiveMethod(startMethod);
9729
9740
  setSelectedResults([]);
9730
9741
  setDamSelectionCount(0);
9731
9742
  setDamSelectedFiles([]);
@@ -9752,7 +9763,7 @@ const FilePicker = ({ config: configInput = {}, onFilesSelected, onClose, open,
9752
9763
  setSortOption(void 0);
9753
9764
  damLibraryRef.current?.clearSelection?.();
9754
9765
  setPreviewAsset(null);
9755
- }, [defaultTab]);
9766
+ }, [startMethod]);
9756
9767
  const handleMethodChange = useCallback((newMethod) => {
9757
9768
  if (!config.unifiedSelection) {
9758
9769
  setSelectedResults([]);
@@ -13910,6 +13921,7 @@ function PlaylistItemsSection({ playlistId }) {
13910
13921
  locale
13911
13922
  ]);
13912
13923
  const pickerAvailable = !!filePickerApi && !!filePickerContextValue;
13924
+ const taggedCanAdd = SECTIONS.tagged.allowedMethods.some((method) => method === "media" || pickerSources?.[method] != null);
13913
13925
  return /* @__PURE__ */ jsxs("div", {
13914
13926
  className: "space-y-8",
13915
13927
  children: [
@@ -13923,7 +13935,7 @@ function PlaylistItemsSection({ playlistId }) {
13923
13935
  isDeletePending: removeItemMutation.isPending,
13924
13936
  disableAdd: !pickerAvailable
13925
13937
  }),
13926
- /* @__PURE__ */ jsx(PlaylistItemsSubsection, {
13938
+ (!pickerAvailable || taggedCanAdd || taggedItems.length > 0) && /* @__PURE__ */ jsx(PlaylistItemsSubsection, {
13927
13939
  section: SECTIONS.tagged,
13928
13940
  items: taggedItems,
13929
13941
  onAdd: () => setOpenSection("tagged"),
@@ -13931,7 +13943,7 @@ function PlaylistItemsSection({ playlistId }) {
13931
13943
  onDeleteItem: handleDeleteItem,
13932
13944
  isPending: addItemMutation.isPending,
13933
13945
  isDeletePending: removeItemMutation.isPending,
13934
- disableAdd: !pickerAvailable
13946
+ disableAdd: !pickerAvailable || !taggedCanAdd
13935
13947
  }),
13936
13948
  pickerAvailable && /* @__PURE__ */ jsx(FilePickerApiProvider, {
13937
13949
  api: filePickerApi,
@@ -14805,19 +14817,30 @@ function getSourceProductIdForMediaDetail(currentRoute, parentSlug) {
14805
14817
  }
14806
14818
  //#endregion
14807
14819
  //#region src/screens/ShareablesScreen.tsx
14808
- function ShareablesScreen({ background, textColor, accentColor, padding, borderRadius, showProductSharablesCarousel = false, readOnly, canCuratePlaylists, ...divProps }) {
14820
+ /** Keeps only the sources a host allows; no allowlist keeps them all. */
14821
+ function pickSources(sources, keys) {
14822
+ if (!keys) return sources;
14823
+ const picked = {};
14824
+ for (const key of keys) {
14825
+ const source = sources[key];
14826
+ if (source) Object.assign(picked, { [key]: source });
14827
+ }
14828
+ return picked;
14829
+ }
14830
+ function ShareablesScreen({ background, textColor, accentColor, padding, borderRadius, showProductSharablesCarousel = false, readOnly, canCuratePlaylists, pickerSourceKeys, ...divProps }) {
14809
14831
  return /* @__PURE__ */ jsx("div", {
14810
14832
  ...divProps,
14811
14833
  className: `h-full ${divProps.className ?? ""}`,
14812
14834
  children: /* @__PURE__ */ jsx(ShareablesTranslationBridge, { children: /* @__PURE__ */ jsx(PortalContentApiProvider, { children: /* @__PURE__ */ jsx(ShareablesScreenContent, {
14813
14835
  showProductSharablesCarousel,
14814
14836
  readOnly,
14815
- canCuratePlaylists
14837
+ canCuratePlaylists,
14838
+ pickerSourceKeys
14816
14839
  }) }) })
14817
14840
  });
14818
14841
  }
14819
14842
  /** Inner component rendered inside providers so hooks can access context. */
14820
- function ShareablesScreenContent({ showProductSharablesCarousel, readOnly, canCuratePlaylists }) {
14843
+ function ShareablesScreenContent({ showProductSharablesCarousel, readOnly, canCuratePlaylists, pickerSourceKeys }) {
14821
14844
  const { t, locale } = useShareablesTranslation();
14822
14845
  const tRef = useRef(t);
14823
14846
  tRef.current = t;
@@ -14871,157 +14894,162 @@ function ShareablesScreenContent({ showProductSharablesCarousel, readOnly, canCu
14871
14894
  detailId,
14872
14895
  screen
14873
14896
  ]);
14874
- return /* @__PURE__ */ jsx(ShareablesCoreProvider, {
14875
- config: useMemo(() => ({
14876
- user: account ? { id: account.id } : null,
14877
- repContext: true
14878
- }), [account]),
14879
- children: /* @__PURE__ */ jsx(ShareablesUIProvider, {
14880
- config: useMemo(() => ({
14881
- user: account ? {
14882
- id: account.id,
14883
- company: { logo_url: store?.logo_url ?? null }
14884
- } : void 0,
14885
- affiliateId: null,
14886
- basePath: "",
14887
- navigate: (path) => {
14888
- const cleanPath = path.replace(/^\//, "");
14889
- navigate(cleanPath.startsWith("share/") ? cleanPath : `share/${cleanPath}`);
14890
- },
14891
- showToast: (opts) => {
14892
- fluidToast({
14893
- title: opts.title,
14894
- type: opts.type
14897
+ const coreConfig = useMemo(() => ({
14898
+ user: account ? { id: account.id } : null,
14899
+ repContext: true
14900
+ }), [account]);
14901
+ const uiConfig = useMemo(() => ({
14902
+ user: account ? {
14903
+ id: account.id,
14904
+ company: { logo_url: store?.logo_url ?? null }
14905
+ } : void 0,
14906
+ affiliateId: null,
14907
+ basePath: "",
14908
+ navigate: (path) => {
14909
+ const cleanPath = path.replace(/^\//, "");
14910
+ navigate(cleanPath.startsWith("share/") ? cleanPath : `share/${cleanPath}`);
14911
+ },
14912
+ showToast: (opts) => {
14913
+ fluidToast({
14914
+ title: opts.title,
14915
+ type: opts.type
14916
+ });
14917
+ if (opts.error) console.error("[Shareables]", opts.error);
14918
+ },
14919
+ filePickerApi,
14920
+ onToggleFavorite: async (params) => {
14921
+ const result = await toggleFavorite(client, params.favoriteableId, params.favoriteableType);
14922
+ queryClient.invalidateQueries({ queryKey: PORTAL_MYSITE_KEYS.favorites() });
14923
+ return result;
14924
+ },
14925
+ onMySiteShare: async (params) => {
14926
+ await toggleFavorite(client, params.relateable_id, params.relateable_type);
14927
+ queryClient.invalidateQueries({ queryKey: PORTAL_MYSITE_KEYS.favorites() });
14928
+ },
14929
+ onDeletePlaylist: async (playlistId) => {
14930
+ await shareablesCtx.playlistsAdapter.deletePlaylist(playlistId);
14931
+ },
14932
+ mediaProductsApi: isCustomer ? void 0 : {
14933
+ ...shareablesCtx.mediaProductsAdapter,
14934
+ listMediaProducts: (mediumId, params) => shareablesCtx.mediaProductsAdapter.listMediaProducts(mediumId, {
14935
+ ...params,
14936
+ language_iso: params?.language_iso ?? localeRef.current
14937
+ })
14938
+ },
14939
+ searchProducts: isCustomer ? void 0 : async (query, options) => {
14940
+ const limit = options?.limit ?? 25;
14941
+ const cursor = options?.cursor ?? void 0;
14942
+ const result = query.trim().length > 0 ? await portalProductsApi.searchProducts(query, {
14943
+ cursor,
14944
+ limit
14945
+ }) : await portalProductsApi.listProducts({
14946
+ cursor,
14947
+ limit
14948
+ });
14949
+ return {
14950
+ products: (result.products ?? []).map((p) => p.id != null ? {
14951
+ id: p.id,
14952
+ name: p.name ?? p.title ?? tRef.current("common_untitled"),
14953
+ image_url: p.image_url ?? p.images?.[0]?.url ?? null,
14954
+ price: p.price ?? null
14955
+ } : null).filter((p) => p !== null),
14956
+ nextCursor: result.meta?.pagination?.next_cursor ?? null
14957
+ };
14958
+ },
14959
+ readOnly: isCustomer,
14960
+ canCuratePlaylists: canCuratePlaylists ?? true,
14961
+ pickerSources: {
14962
+ pages: {
14963
+ cacheKey: `locale:${locale}`,
14964
+ list: async ({ search, cursor, limit }) => {
14965
+ const response = await shareablesCtx.pagesAdapter.listPages({
14966
+ cursor: cursor ?? void 0,
14967
+ limit,
14968
+ "filter[title]": search && search.trim().length > 0 ? search : void 0,
14969
+ language_iso: localeRef.current
14895
14970
  });
14896
- if (opts.error) console.error("[Shareables]", opts.error);
14897
- },
14898
- filePickerApi,
14899
- onToggleFavorite: async (params) => {
14900
- const result = await toggleFavorite(client, params.favoriteableId, params.favoriteableType);
14901
- queryClient.invalidateQueries({ queryKey: PORTAL_MYSITE_KEYS.favorites() });
14902
- return result;
14903
- },
14904
- onMySiteShare: async (params) => {
14905
- await toggleFavorite(client, params.relateable_id, params.relateable_type);
14906
- queryClient.invalidateQueries({ queryKey: PORTAL_MYSITE_KEYS.favorites() });
14907
- },
14908
- onDeletePlaylist: async (playlistId) => {
14909
- await shareablesCtx.playlistsAdapter.deletePlaylist(playlistId);
14910
- },
14911
- mediaProductsApi: isCustomer ? void 0 : {
14912
- ...shareablesCtx.mediaProductsAdapter,
14913
- listMediaProducts: (mediumId, params) => shareablesCtx.mediaProductsAdapter.listMediaProducts(mediumId, {
14914
- ...params,
14915
- language_iso: params?.language_iso ?? localeRef.current
14916
- })
14917
- },
14918
- searchProducts: isCustomer ? void 0 : async (query, options) => {
14919
- const limit = options?.limit ?? 25;
14920
- const cursor = options?.cursor ?? void 0;
14921
- const result = query.trim().length > 0 ? await portalProductsApi.searchProducts(query, {
14922
- cursor,
14971
+ return {
14972
+ items: response.pages.map((p) => ({
14973
+ id: p.id,
14974
+ title: p.title ?? tRef.current("pages_fallback_name", { id: String(p.id) }),
14975
+ image_url: p.image_url ?? null
14976
+ })),
14977
+ nextCursor: response.meta.pagination?.next_cursor ?? null
14978
+ };
14979
+ }
14980
+ },
14981
+ products: {
14982
+ cacheKey: `locale:${locale}`,
14983
+ list: async ({ search, cursor, limit }) => {
14984
+ const trimmedSearch = search?.trim() ?? "";
14985
+ const result = trimmedSearch.length > 0 ? await portalProductsApi.searchProducts(trimmedSearch, {
14986
+ cursor: cursor ?? void 0,
14923
14987
  limit
14924
14988
  }) : await portalProductsApi.listProducts({
14925
- cursor,
14989
+ cursor: cursor ?? void 0,
14926
14990
  limit
14927
14991
  });
14928
14992
  return {
14929
- products: (result.products ?? []).map((p) => p.id != null ? {
14993
+ items: (result.products ?? []).map((p) => p.id != null ? {
14930
14994
  id: p.id,
14931
- name: p.name ?? p.title ?? tRef.current("common_untitled"),
14932
- image_url: p.image_url ?? p.images?.[0]?.url ?? null,
14995
+ name: p.name ?? tRef.current("common_untitled"),
14996
+ image_url: p.images?.[0]?.url ?? null,
14933
14997
  price: p.price ?? null
14934
14998
  } : null).filter((p) => p !== null),
14935
14999
  nextCursor: result.meta?.pagination?.next_cursor ?? null
14936
15000
  };
14937
- },
14938
- readOnly: isCustomer,
14939
- canCuratePlaylists: canCuratePlaylists ?? true,
14940
- pickerSources: {
14941
- pages: {
14942
- cacheKey: `locale:${locale}`,
14943
- list: async ({ search, cursor, limit }) => {
14944
- const response = await shareablesCtx.pagesAdapter.listPages({
14945
- cursor: cursor ?? void 0,
14946
- limit,
14947
- "filter[title]": search && search.trim().length > 0 ? search : void 0,
14948
- language_iso: localeRef.current
14949
- });
14950
- return {
14951
- items: response.pages.map((p) => ({
14952
- id: p.id,
14953
- title: p.title ?? tRef.current("pages_fallback_name", { id: String(p.id) }),
14954
- image_url: p.image_url ?? null
14955
- })),
14956
- nextCursor: response.meta.pagination?.next_cursor ?? null
14957
- };
14958
- }
14959
- },
14960
- products: {
14961
- cacheKey: `locale:${locale}`,
14962
- list: async ({ search, cursor, limit }) => {
14963
- const trimmedSearch = search?.trim() ?? "";
14964
- const result = trimmedSearch.length > 0 ? await portalProductsApi.searchProducts(trimmedSearch, {
14965
- cursor: cursor ?? void 0,
14966
- limit
14967
- }) : await portalProductsApi.listProducts({
14968
- cursor: cursor ?? void 0,
14969
- limit
14970
- });
14971
- return {
14972
- items: (result.products ?? []).map((p) => p.id != null ? {
14973
- id: p.id,
14974
- name: p.name ?? tRef.current("common_untitled"),
14975
- image_url: p.images?.[0]?.url ?? null,
14976
- price: p.price ?? null
14977
- } : null).filter((p) => p !== null),
14978
- nextCursor: result.meta?.pagination?.next_cursor ?? null
14979
- };
14980
- }
14981
- },
14982
- enrollment_packs: {
14983
- cacheKey: `locale:${locale}`,
14984
- supportsSearch: false,
14985
- list: async ({ cursor, limit }) => {
14986
- const response = await shareablesCtx.enrollmentPacksAdapter.listEnrollmentPacks({
14987
- cursor: cursor ?? void 0,
14988
- limit
14989
- });
14990
- return {
14991
- items: response.enrollment_packs.map((pack) => ({
14992
- id: pack.id,
14993
- title: pack.title || tRef.current("enrollment_pack_fallback_name", { id: String(pack.id) }),
14994
- image_url: pack.images[0]?.url ?? null
14995
- })),
14996
- nextCursor: response.meta.pagination?.next_cursor ?? null
14997
- };
14998
- }
14999
- }
15000
- },
15001
- uploadThumbnail: isCustomer ? void 0 : async (blob, filename) => {
15002
- const url = (await filePickerApi.createDamAsset({
15003
- file: new File([blob], filename, { type: blob.type }),
15004
- name: filename
15005
- })).asset?.default_variant_url;
15006
- if (!url) throw new Error("Thumbnail upload succeeded but no URL returned");
15007
- return url;
15008
15001
  }
15009
- }), [
15010
- account,
15011
- store,
15012
- locale,
15013
- navigate,
15014
- filePickerApi,
15015
- isCustomer,
15016
- canCuratePlaylists,
15017
- shareablesCtx.playlistsAdapter,
15018
- shareablesCtx.pagesAdapter,
15019
- shareablesCtx.enrollmentPacksAdapter,
15020
- shareablesCtx.mediaProductsAdapter,
15021
- portalProductsApi,
15022
- client,
15023
- queryClient
15024
- ]),
15002
+ },
15003
+ enrollment_packs: {
15004
+ cacheKey: `locale:${locale}`,
15005
+ supportsSearch: false,
15006
+ list: async ({ cursor, limit }) => {
15007
+ const response = await shareablesCtx.enrollmentPacksAdapter.listEnrollmentPacks({
15008
+ cursor: cursor ?? void 0,
15009
+ limit
15010
+ });
15011
+ return {
15012
+ items: response.enrollment_packs.map((pack) => ({
15013
+ id: pack.id,
15014
+ title: pack.title || tRef.current("enrollment_pack_fallback_name", { id: String(pack.id) }),
15015
+ image_url: pack.images[0]?.url ?? null
15016
+ })),
15017
+ nextCursor: response.meta.pagination?.next_cursor ?? null
15018
+ };
15019
+ }
15020
+ }
15021
+ },
15022
+ uploadThumbnail: isCustomer ? void 0 : async (blob, filename) => {
15023
+ const url = (await filePickerApi.createDamAsset({
15024
+ file: new File([blob], filename, { type: blob.type }),
15025
+ name: filename
15026
+ })).asset?.default_variant_url;
15027
+ if (!url) throw new Error("Thumbnail upload succeeded but no URL returned");
15028
+ return url;
15029
+ }
15030
+ }), [
15031
+ account,
15032
+ store,
15033
+ locale,
15034
+ navigate,
15035
+ filePickerApi,
15036
+ isCustomer,
15037
+ canCuratePlaylists,
15038
+ shareablesCtx.playlistsAdapter,
15039
+ shareablesCtx.pagesAdapter,
15040
+ shareablesCtx.enrollmentPacksAdapter,
15041
+ shareablesCtx.mediaProductsAdapter,
15042
+ portalProductsApi,
15043
+ client,
15044
+ queryClient
15045
+ ]);
15046
+ return /* @__PURE__ */ jsx(ShareablesCoreProvider, {
15047
+ config: coreConfig,
15048
+ children: /* @__PURE__ */ jsx(ShareablesUIProvider, {
15049
+ config: useMemo(() => pickerSourceKeys ? {
15050
+ ...uiConfig,
15051
+ pickerSources: pickSources(uiConfig.pickerSources, pickerSourceKeys)
15052
+ } : uiConfig, [uiConfig, pickerSourceKeys]),
15025
15053
  children: /* @__PURE__ */ jsx(ShareablesApp, {
15026
15054
  screen,
15027
15055
  detailId,
@@ -15058,4 +15086,4 @@ const shareablesScreenPropertySchema = {
15058
15086
  //#endregion
15059
15087
  export { shareablesScreenPropertySchema as n, ShareablesScreen as t };
15060
15088
 
15061
- //# sourceMappingURL=ShareablesScreen-Dqecn7OH.mjs.map
15089
+ //# sourceMappingURL=ShareablesScreen-wKo6gZPp.mjs.map