@postrun/react 3.3.0 → 4.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,13 +1,12 @@
1
1
  "use client";
2
2
  import { createContext, memo, useMemo, useState, Fragment as Fragment$1, useRef, useLayoutEffect, useCallback, useEffect, createElement, useContext, useId } from 'react';
3
- import { useInfiniteQuery, useQuery, useMutation, useQueries, QueryClient } from '@tanstack/react-query';
4
- import { xPollDurationLabel, linkedinPollDurationLabel, captionMaxFor, UploadError, TIKTOK_CAPTION_MAX, createPostrunClient, profilesList, profilesGet, profilesCreate, profilesUpdate, profilesDelete, connectionsConnect, connectionsListByProfile, connectionsGet, connectionsListAccounts, connectionsSelect, connectionsDelete, mediaGet, mediaList, mediaUpdate, mediaDelete, postsList, postsGet, postsCreate, buildCreatePost, isPostPlatform, failedVariants, isPublished, postsValidate, postsUpdate, postsDelete, tiktokCreatorInfo, groupAdTree, audiencePrivacyOptions, audienceUnselected, tiktokPrivacyLabel, parsePrivacyLevel, TIKTOK_AUDIENCE_REQUIRED_HINT, commercialLabelNotice, commercialDisclosureIncomplete, setCommercialDisclosure, setBrandKind, TIKTOK_DISCLOSURE_REQUIRED_HINT, TIKTOK_BRANDED_CONTENT_POLICY_URL, TIKTOK_MUSIC_CONFIRMATION_URL, interactionRows, interactionValueKey, toggleInteraction, tiktokOptionsReady, TIKTOK_PROCESSING_NOTICE, brandedContentDeclared, mediaCreate, uploadFile, waitForMedia, PostrunError } from '@postrun/js';
3
+ import { useInfiniteQuery, useQuery, useMutation, QueryClient } from '@tanstack/react-query';
4
+ import { xPollDurationLabel, linkedinPollDurationLabel, captionMaxFor, UploadError, TIKTOK_CAPTION_MAX, createPostrunClient, profilesList, profilesGet, profilesCreate, profilesUpdate, profilesDelete, connectionsConnect, connectionsListByProfile, connectionsGet, connectionsListAccounts, connectionsSelect, connectionsDelete, mediaGet, mediaList, mediaUpdate, mediaDelete, postsList, postsGet, postsCreate, buildCreatePost, isPostPlatform, failedVariants, isPublished, postsValidate, postsUpdate, postsDelete, tiktokCreatorInfo, audiencePrivacyOptions, audienceUnselected, parsePrivacyLevel, TIKTOK_AUDIENCE_REQUIRED_HINT, commercialLabelNotice, commercialDisclosureIncomplete, brandedContentDisabledReason, setCommercialDisclosure, setBrandKind, TIKTOK_DISCLOSURE_REQUIRED_HINT, TIKTOK_BRANDED_CONTENT_POLICY_URL, TIKTOK_MUSIC_CONFIRMATION_URL, interactionRows, interactionValueKey, toggleInteraction, tiktokOptionsReady, TIKTOK_PROCESSING_NOTICE, brandedContentDeclared, mediaCreate, uploadFile, waitForMedia, PostrunError } from '@postrun/js';
5
5
  export { TIKTOK_CAPTION_MAX, UploadError, captionMaxFor } from '@postrun/js';
6
6
  import Nango, { AuthError } from '@nangohq/frontend';
7
7
  import pWaitFor, { TimeoutError } from 'p-wait-for';
8
8
  import pLimit from 'p-limit';
9
9
  import * as query_star from '@postrun/js/query';
10
- import { googleGetAdTreeOptions } from '@postrun/js/query';
11
10
  import { enrichTweet, TweetContainer, TweetHeader, TweetInReplyTo, TweetBody, TweetMedia, QuotedTweet } from 'react-tweet';
12
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
12
  import { FiMessageCircle, FiRepeat, FiHeart, FiBarChart2, FiShare, FiGlobe, FiUsers, FiThumbsUp, FiMessageSquare, FiSend, FiBookmark, FiMoreHorizontal, FiFileText } from 'react-icons/fi';
@@ -65,7 +64,6 @@ __export(index_exports, {
65
64
  profileKeys: () => profileKeys,
66
65
  resolveVariantMedia: () => resolveVariantMedia,
67
66
  tiktokKeys: () => tiktokKeys,
68
- useAdTree: () => useAdTree,
69
67
  useCalendar: () => useCalendar,
70
68
  useConnect: () => useConnect,
71
69
  useConnection: () => useConnection,
@@ -984,76 +982,6 @@ function useTikTokCreatorInfo(connectionId) {
984
982
  queryClient
985
983
  );
986
984
  }
987
- function useAdTree(params) {
988
- const { connectionId, since, until, metrics, expanded = [] } = params;
989
- const { client, queryClient } = usePostrun();
990
- const enabled = Boolean(connectionId && since && until);
991
- const path = { connection_id: connectionId };
992
- const baseQuery = { since, until, metrics };
993
- const campaign = useQuery(
994
- {
995
- ...googleGetAdTreeOptions({ client, path, query: baseQuery }),
996
- enabled
997
- },
998
- queryClient
999
- );
1000
- const combine = useCallback(
1001
- (results) => {
1002
- const nodes = results.flatMap((q) => q.data?.data ?? []);
1003
- const loadingById2 = new Map(
1004
- expanded.map((id, i) => [id, results[i]?.isLoading ?? false])
1005
- );
1006
- return { nodes, loadingById: loadingById2 };
1007
- },
1008
- // eslint-disable-next-line react-hooks/exhaustive-deps -- expanded is an array; join gives a stable string dep
1009
- // biome-ignore lint/correctness/useExhaustiveDependencies: intentional string-join dep for array stability
1010
- [expanded.join(",")]
1011
- );
1012
- const { nodes: subtreeNodes, loadingById } = useQueries(
1013
- {
1014
- queries: expanded.map((campaignId) => ({
1015
- ...googleGetAdTreeOptions({
1016
- client,
1017
- path,
1018
- query: { ...baseQuery, campaign_id: campaignId }
1019
- }),
1020
- enabled
1021
- })),
1022
- combine
1023
- },
1024
- queryClient
1025
- );
1026
- const tree = useMemo(
1027
- () => groupAdTree([...campaign.data?.data ?? [], ...subtreeNodes]),
1028
- [campaign.data, subtreeNodes]
1029
- );
1030
- const isExpanding = useCallback(
1031
- (campaignId) => loadingById.get(campaignId) ?? false,
1032
- [loadingById]
1033
- );
1034
- const refetch = useCallback(
1035
- (...args) => campaign.refetch(...args),
1036
- [campaign.refetch]
1037
- );
1038
- return useMemo(
1039
- () => ({
1040
- tree,
1041
- isLoading: campaign.isLoading,
1042
- isError: campaign.isError,
1043
- error: campaign.error,
1044
- isExpanding,
1045
- refetch
1046
- }),
1047
- [
1048
- tree,
1049
- campaign.isLoading,
1050
- campaign.isError,
1051
- campaign.error,
1052
- isExpanding,
1053
- refetch
1054
- ]
1055
- );
1056
- }
1057
985
 
1058
986
  // src/query.ts
1059
987
  var query_exports = {};
@@ -2788,10 +2716,10 @@ var ShareIcon = (p) => /* @__PURE__ */ jsx(Icon, { viewBox: "0 0 20 20", ...p, c
2788
2716
  var PlusIcon = (p) => /* @__PURE__ */ jsx(Icon, { viewBox: "0 0 48 48", ...p, children: /* @__PURE__ */ jsx("path", { d: "M26 7a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v15H7a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h15v15a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V26h15a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1H26V7Z" }) });
2789
2717
  var DASH = "\u2011\u2011\u2011";
2790
2718
  function commercialLabel(settings) {
2791
- if (settings?.brand_content_toggle) {
2719
+ if (settings?.is_branded_content) {
2792
2720
  return "Paid partnership";
2793
2721
  }
2794
- if (settings?.brand_organic_toggle) {
2722
+ if (settings?.is_brand_organic) {
2795
2723
  return "Promotional content";
2796
2724
  }
2797
2725
  return null;
@@ -2811,7 +2739,7 @@ function TikTokPostPreviewImpl({
2811
2739
  const { creator } = creatorInfo;
2812
2740
  const handle = creator.username || creator.nickname;
2813
2741
  const label = commercialLabel(variant.settings);
2814
- const isAigc = Boolean(variant.settings?.is_aigc);
2742
+ const isAigc = Boolean(variant.settings?.is_ai_generated);
2815
2743
  const pending = resolved.length > 0 && readyMedia.length === 0;
2816
2744
  return /* @__PURE__ */ jsxs("div", { className, style: { ...cardStyle4, ...style }, children: [
2817
2745
  /* @__PURE__ */ jsx("style", { children: KEYFRAMES }),
@@ -3089,7 +3017,8 @@ function Notice({ children }) {
3089
3017
  function CheckRow({
3090
3018
  label,
3091
3019
  checked,
3092
- onChange
3020
+ onChange,
3021
+ disabledReason
3093
3022
  }) {
3094
3023
  const id = useId();
3095
3024
  return /* @__PURE__ */ jsxs("label", { htmlFor: id, style: checkRowStyle, children: [
@@ -3099,11 +3028,13 @@ function CheckRow({
3099
3028
  id,
3100
3029
  type: "checkbox",
3101
3030
  checked,
3031
+ disabled: disabledReason !== void 0,
3102
3032
  onChange: (e) => onChange(e.target.checked),
3103
3033
  style: { accentColor: ACCENT, width: 16, height: 16 }
3104
3034
  }
3105
3035
  ),
3106
- /* @__PURE__ */ jsx("span", { style: { fontSize: 14 }, children: label })
3036
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 14 }, children: label }),
3037
+ disabledReason ? /* @__PURE__ */ jsx(Hint, { children: disabledReason }) : null
3107
3038
  ] });
3108
3039
  }
3109
3040
  var rowStyle3 = {
@@ -3160,8 +3091,10 @@ function AudienceSelect({
3160
3091
  value,
3161
3092
  onChange
3162
3093
  }) {
3163
- const options = audiencePrivacyOptions(creatorInfo.privacy_options, value);
3164
- const unselected = audienceUnselected(value);
3094
+ const choices = audiencePrivacyOptions(creatorInfo.privacy_options, value);
3095
+ const enabled = choices.flatMap((c) => c.disabled ? [] : [c.level]);
3096
+ const reason = choices.find((c) => c.disabled)?.disabledReason;
3097
+ const unselected = audienceUnselected(value, false);
3165
3098
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
3166
3099
  /* @__PURE__ */ jsx(SectionLabel, { children: "Who can view this post" }),
3167
3100
  /* @__PURE__ */ jsxs(
@@ -3171,17 +3104,27 @@ function AudienceSelect({
3171
3104
  value: value.privacy_level ?? "",
3172
3105
  onChange: (e) => onChange({
3173
3106
  ...value,
3174
- // Validate against the OFFERED (filtered) options, not the full set —
3175
- // so a manipulated value (e.g. SELF_ONLY while branded) can't slip in.
3176
- privacy_level: parsePrivacyLevel(e.target.value, options)
3107
+ // Validate against the ENABLED options so a manipulated value
3108
+ // (e.g. SELF_ONLY while branded) can't slip in.
3109
+ privacy_level: parsePrivacyLevel(e.target.value, enabled)
3177
3110
  }),
3178
3111
  style: selectStyle,
3179
3112
  children: [
3180
3113
  /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select who can view this post" }),
3181
- options.map((level) => /* @__PURE__ */ jsx("option", { value: level, children: tiktokPrivacyLabel(level) }, level))
3114
+ choices.map((choice) => /* @__PURE__ */ jsx(
3115
+ "option",
3116
+ {
3117
+ value: choice.level,
3118
+ disabled: choice.disabled,
3119
+ title: choice.disabledReason,
3120
+ children: choice.label
3121
+ },
3122
+ choice.level
3123
+ ))
3182
3124
  ]
3183
3125
  }
3184
3126
  ),
3127
+ reason ? /* @__PURE__ */ jsx(Hint, { children: reason }) : null,
3185
3128
  unselected ? /* @__PURE__ */ jsx(Hint, { children: TIKTOK_AUDIENCE_REQUIRED_HINT }) : null
3186
3129
  ] });
3187
3130
  }
@@ -3280,10 +3223,12 @@ var textareaStyle = {
3280
3223
  };
3281
3224
  function CommercialDisclosure({
3282
3225
  value,
3283
- onChange
3226
+ onChange,
3227
+ isVideo
3284
3228
  }) {
3285
3229
  const labelNotice = commercialLabelNotice(value);
3286
3230
  const incomplete = commercialDisclosureIncomplete(value);
3231
+ const brandedLocked = isVideo ? void 0 : brandedContentDisabledReason(value);
3287
3232
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
3288
3233
  /* @__PURE__ */ jsx(SectionLabel, { children: "Disclose post content" }),
3289
3234
  /* @__PURE__ */ jsx(
@@ -3315,6 +3260,7 @@ function CommercialDisclosure({
3315
3260
  {
3316
3261
  label: "Branded content",
3317
3262
  checked: value.branded_content,
3263
+ disabledReason: brandedLocked,
3318
3264
  onChange: (next) => onChange(setBrandKind(value, "branded_content", next))
3319
3265
  }
3320
3266
  ),
@@ -3404,7 +3350,7 @@ function TikTokPublishPanel({
3404
3350
  const isVideo = postType === "video";
3405
3351
  const captionOver = caption.length > captionMaxFor(postType);
3406
3352
  const mediaOk = mediaCountValid(postType, variant.media?.length ?? 0);
3407
- const ready = !captionOver && mediaOk && tiktokOptionsReady(options);
3353
+ const ready = !captionOver && mediaOk && tiktokOptionsReady(options, isVideo);
3408
3354
  return /* @__PURE__ */ jsxs(
3409
3355
  "div",
3410
3356
  {
@@ -3425,8 +3371,17 @@ function TikTokPublishPanel({
3425
3371
  postType
3426
3372
  }
3427
3373
  ),
3428
- /* @__PURE__ */ jsx(Divider, {}),
3429
- /* @__PURE__ */ jsx(AudienceSelect, { creatorInfo, value: options, onChange: onOptionsChange }),
3374
+ isVideo ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
3375
+ /* @__PURE__ */ jsx(Divider, {}),
3376
+ /* @__PURE__ */ jsx(
3377
+ AudienceSelect,
3378
+ {
3379
+ creatorInfo,
3380
+ value: options,
3381
+ onChange: onOptionsChange
3382
+ }
3383
+ )
3384
+ ] }),
3430
3385
  /* @__PURE__ */ jsx(Divider, {}),
3431
3386
  /* @__PURE__ */ jsx(
3432
3387
  InteractionToggles,
@@ -3438,7 +3393,14 @@ function TikTokPublishPanel({
3438
3393
  }
3439
3394
  ),
3440
3395
  /* @__PURE__ */ jsx(Divider, {}),
3441
- /* @__PURE__ */ jsx(CommercialDisclosure, { value: options, onChange: onOptionsChange }),
3396
+ /* @__PURE__ */ jsx(
3397
+ CommercialDisclosure,
3398
+ {
3399
+ value: options,
3400
+ onChange: onOptionsChange,
3401
+ isVideo
3402
+ }
3403
+ ),
3442
3404
  isVideo ? /* @__PURE__ */ jsxs(Fragment, { children: [
3443
3405
  /* @__PURE__ */ jsx(Divider, {}),
3444
3406
  /* @__PURE__ */ jsx(
@@ -3449,9 +3411,9 @@ function TikTokPublishPanel({
3449
3411
  control: /* @__PURE__ */ jsx(
3450
3412
  Switch,
3451
3413
  {
3452
- checked: options.aigc,
3414
+ checked: options.ai_generated,
3453
3415
  ariaLabel: "AI-generated content",
3454
- onChange: (next) => onOptionsChange({ ...options, aigc: next })
3416
+ onChange: (next) => onOptionsChange({ ...options, ai_generated: next })
3455
3417
  }
3456
3418
  )
3457
3419
  }
@@ -3504,6 +3466,6 @@ var postButtonStyle = {
3504
3466
  transition: "opacity 160ms ease"
3505
3467
  };
3506
3468
 
3507
- export { ArticleCard, AudienceSelect, CommercialDisclosure, Connect, Declaration, DocumentCard, InstagramPostPreview, InteractionToggles, LinkedInPostPreview, Poll, PostrunProvider, ReelPreview, TikTokCaptionField, TikTokPostPreview, TikTokPublishPanel, XPoll, XPostPreview, connectionKeys, isReadyMedia, mediaKeys, postKeys, profileKeys, resolveVariantMedia, tiktokKeys, useAdTree, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePostrunMutation, usePostrunQuery, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useTikTokCreatorInfo, useUpdateMedia, useUpdatePost, useUpdateProfile, useValidatePost };
3469
+ export { ArticleCard, AudienceSelect, CommercialDisclosure, Connect, Declaration, DocumentCard, InstagramPostPreview, InteractionToggles, LinkedInPostPreview, Poll, PostrunProvider, ReelPreview, TikTokCaptionField, TikTokPostPreview, TikTokPublishPanel, XPoll, XPostPreview, connectionKeys, isReadyMedia, mediaKeys, postKeys, profileKeys, resolveVariantMedia, tiktokKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePostrunMutation, usePostrunQuery, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useTikTokCreatorInfo, useUpdateMedia, useUpdatePost, useUpdateProfile, useValidatePost };
3508
3470
  //# sourceMappingURL=index.js.map
3509
3471
  //# sourceMappingURL=index.js.map