@select-org/post-components 1.0.1 → 2.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.d.ts CHANGED
@@ -50,6 +50,16 @@ interface PostCardProps {
50
50
  isAdmin?: boolean;
51
51
  isOwner?: boolean;
52
52
  isPostPreview?: boolean;
53
+ isPrivate?: boolean;
54
+ membersCount?: number;
55
+ crossPostGroup?: {
56
+ name: string;
57
+ href?: string;
58
+ };
59
+ locale?: string;
60
+ translate?: (key: string, params?: {
61
+ count?: number;
62
+ }) => string;
53
63
  onPostPress?: () => void;
54
64
  onErasePress?: () => void;
55
65
  onCancelPost?: () => void;
@@ -100,6 +110,16 @@ interface PostHeaderProps {
100
110
  groupHref?: string;
101
111
  postedAt: string;
102
112
  postType: string;
113
+ isPrivate?: boolean;
114
+ membersCount?: number;
115
+ crossPostGroup?: {
116
+ name: string;
117
+ href?: string;
118
+ };
119
+ locale?: string;
120
+ translate?: (key: string, params?: {
121
+ count?: number;
122
+ }) => string;
103
123
  isFeatured?: boolean;
104
124
  isPostPreview?: boolean;
105
125
  isAdmin?: boolean;
@@ -299,6 +319,25 @@ declare const AutoLink: {
299
319
  truncate: (text: string, { truncate: len, truncateChars, truncateLocation }?: TruncateOptions) => string;
300
320
  };
301
321
 
322
+ interface PostTypeHintContext {
323
+ isInGroup?: boolean;
324
+ isOwner?: boolean;
325
+ isPrivate?: boolean;
326
+ membersCount?: number;
327
+ /** Mirrors the web client's `isMetaDesc` branch (SEO/meta-description copy). */
328
+ isMetaDesc?: boolean;
329
+ }
330
+ interface PostTypeHint {
331
+ key: string;
332
+ params?: {
333
+ count?: number;
334
+ };
335
+ }
336
+ declare function getPostTypeHint(postType: string, ctx?: PostTypeHintContext): PostTypeHint;
337
+ declare const POST_TYPE_HINT_DEFAULTS: Record<string, (p?: {
338
+ count?: number;
339
+ }) => string>;
340
+
302
341
  interface ReactionModalProps {
303
342
  isOpen: boolean;
304
343
  close: () => void;
@@ -318,4 +357,4 @@ interface Props {
318
357
  }
319
358
  declare const EraseConfirmModal: React.FC<Props>;
320
359
 
321
- export { AutoLink, EmbedContent, EraseConfirmModal, ItemData, LinkItemMetaData, MediaUploadStatus, MediaUploadStatusOverlay, MentionData, PollItemData, PollResult, PostBody, PostCard, type PostCardProps, PostEmail, PostEmbedView, PostFooter, PostHeader, type PostHeaderProps, PostItemsView, PostLinkItem, PostMediaLayoutCore, PostPollView, PostSectionSourceInfoView, PostSectionsSummaryView, PostSectionsView, PostTextItem, PostUploadStrip, PrimaryContent, ReactionModal, ResolvedTextContent, SectionItem, TextItemContent, TipTapDocument };
360
+ export { AutoLink, EmbedContent, EraseConfirmModal, ItemData, LinkItemMetaData, MediaUploadStatus, MediaUploadStatusOverlay, MentionData, POST_TYPE_HINT_DEFAULTS, PollItemData, PollResult, PostBody, PostCard, type PostCardProps, PostEmail, PostEmbedView, PostFooter, PostHeader, type PostHeaderProps, PostItemsView, PostLinkItem, PostMediaLayoutCore, PostPollView, PostSectionSourceInfoView, PostSectionsSummaryView, PostSectionsView, PostTextItem, type PostTypeHint, type PostTypeHintContext, PostUploadStrip, PrimaryContent, ReactionModal, ResolvedTextContent, SectionItem, TextItemContent, TipTapDocument, getPostTypeHint };
package/dist/index.js CHANGED
@@ -64,18 +64,169 @@ function AppIcon({ name, ...props }) {
64
64
  return /* @__PURE__ */ jsx(Icon, { ...props });
65
65
  }
66
66
 
67
+ // src/utils/postTypeHint.ts
68
+ var MEDIA_TYPES = /* @__PURE__ */ new Set([
69
+ "new:post:media",
70
+ "new:post:image",
71
+ "mediaItemToUpload"
72
+ ]);
73
+ function getPostTypeHint(postType, ctx = {}) {
74
+ const {
75
+ isInGroup = false,
76
+ isOwner = false,
77
+ isPrivate = false,
78
+ membersCount = 0,
79
+ isMetaDesc = false
80
+ } = ctx;
81
+ const isMedia = MEDIA_TYPES.has(postType);
82
+ if (isInGroup) {
83
+ if (isMetaDesc) {
84
+ if (isMedia)
85
+ return { key: "post_detail_meta_description_type_image_post" };
86
+ switch (postType) {
87
+ case "new:post:link":
88
+ return { key: "post_detail_meta_description_type_link_post" };
89
+ case "new:post:poll":
90
+ return { key: "post_detail_meta_description_type_poll_post" };
91
+ case "new:post:thread":
92
+ return {
93
+ key: "post_detail_one_liner_receive_and_send_group_thread_post"
94
+ };
95
+ case "new:post:text":
96
+ default:
97
+ return { key: "post_detail_meta_description_type_message_post" };
98
+ }
99
+ }
100
+ if (isMedia)
101
+ return { key: "post_detail_one_liner_receive_and_send_group_image_post" };
102
+ switch (postType) {
103
+ case "new:post:link":
104
+ return {
105
+ key: "post_detail_one_liner_receive_and_send_group_link_post"
106
+ };
107
+ case "new:post:poll":
108
+ return {
109
+ key: "post_detail_one_liner_receive_and_send_group_poll_post"
110
+ };
111
+ case "new:post:thread":
112
+ return {
113
+ key: "post_detail_one_liner_receive_and_send_group_thread_post"
114
+ };
115
+ case "new:post:text":
116
+ default:
117
+ return {
118
+ key: "post_detail_one_liner_receive_and_send_group_message_post"
119
+ };
120
+ }
121
+ }
122
+ if (isOwner) {
123
+ return membersCount > 1 ? sentPlural(postType, membersCount) : sentSingular(postType);
124
+ }
125
+ if (isPrivate) {
126
+ if (isMedia)
127
+ return { key: "post_detail_one_liner_receive_private_image_post" };
128
+ switch (postType) {
129
+ case "new:post:link":
130
+ return { key: "post_detail_one_liner_receive_private_link_post" };
131
+ case "new:post:poll":
132
+ return { key: "post_detail_one_liner_receive_private_poll_post" };
133
+ case "new:post:thread":
134
+ case "new:post:text":
135
+ default:
136
+ return { key: "post_detail_one_liner_receive_message_post" };
137
+ }
138
+ }
139
+ return membersCount > 1 ? sentPlural(postType, membersCount) : sentSingular(postType);
140
+ }
141
+ function sentPlural(postType, count) {
142
+ const params = { count };
143
+ if (MEDIA_TYPES.has(postType))
144
+ return { key: "post_detail_one_liner_send_private_image_post_plural", params };
145
+ switch (postType) {
146
+ case "new:post:link":
147
+ return {
148
+ key: "post_detail_one_liner_send_private_link_post_plural",
149
+ params
150
+ };
151
+ case "new:post:poll":
152
+ return {
153
+ key: "post_detail_one_liner_send_private_link_poll_plural",
154
+ params
155
+ };
156
+ case "new:post:thread":
157
+ case "new:post:text":
158
+ default:
159
+ return { key: "post_detail_one_liner_send_message_post_plural", params };
160
+ }
161
+ }
162
+ function sentSingular(postType) {
163
+ if (MEDIA_TYPES.has(postType))
164
+ return { key: "post_detail_one_liner_send_private_image_post_singular" };
165
+ switch (postType) {
166
+ case "new:post:link":
167
+ return { key: "post_detail_one_liner_send_private_link_post_singular" };
168
+ case "new:post:poll":
169
+ return { key: "post_detail_one_liner_send_private_link_poll_singular" };
170
+ case "new:post:thread":
171
+ case "new:post:text":
172
+ default:
173
+ return { key: "post_detail_one_liner_send_message_post_singular" };
174
+ }
175
+ }
176
+ var POST_TYPE_HINT_DEFAULTS = {
177
+ // in-group (one-liner)
178
+ post_detail_one_liner_receive_and_send_group_image_post: () => "shared a media post in group",
179
+ post_detail_one_liner_receive_and_send_group_link_post: () => "shared a link post in group",
180
+ post_detail_one_liner_receive_and_send_group_poll_post: () => "shared a poll in group",
181
+ post_detail_one_liner_receive_and_send_group_thread_post: () => "shared a thread in group",
182
+ post_detail_one_liner_receive_and_send_group_message_post: () => "shared a post in group",
183
+ // in-group (meta description)
184
+ post_detail_meta_description_type_image_post: () => "A media post",
185
+ post_detail_meta_description_type_link_post: () => "A link post",
186
+ post_detail_meta_description_type_poll_post: () => "A poll",
187
+ post_detail_meta_description_type_message_post: () => "A post",
188
+ // owner / 1:M sent — plural
189
+ post_detail_one_liner_send_private_image_post_plural: (p) => `sent a media post to ${p?.count ?? 0} people`,
190
+ post_detail_one_liner_send_private_link_post_plural: (p) => `sent a link post to ${p?.count ?? 0} people`,
191
+ post_detail_one_liner_send_private_link_poll_plural: (p) => `sent a poll to ${p?.count ?? 0} people`,
192
+ post_detail_one_liner_send_message_post_plural: (p) => `sent a post to ${p?.count ?? 0} people`,
193
+ // owner / 1:M sent — singular
194
+ post_detail_one_liner_send_private_image_post_singular: () => "sent a media post",
195
+ post_detail_one_liner_send_private_link_post_singular: () => "sent a link post",
196
+ post_detail_one_liner_send_private_link_poll_singular: () => "sent a poll",
197
+ post_detail_one_liner_send_message_post_singular: () => "sent a post",
198
+ // private 1:1 received
199
+ post_detail_one_liner_receive_private_image_post: () => "sent you a media post",
200
+ post_detail_one_liner_receive_private_link_post: () => "sent you a link post",
201
+ post_detail_one_liner_receive_private_poll_post: () => "sent you a poll",
202
+ post_detail_one_liner_receive_message_post: () => "sent you a post"
203
+ };
204
+
205
+ // src/utils/relativeTime.ts
206
+ var UNITS = [
207
+ ["year", 31536e3],
208
+ ["month", 2592e3],
209
+ ["week", 604800],
210
+ ["day", 86400],
211
+ ["hour", 3600],
212
+ ["minute", 60]
213
+ ];
214
+ function formatRelativeTime(iso, locale = "en") {
215
+ const diffMs = Date.now() - new Date(iso).getTime();
216
+ const seconds = Math.round(diffMs / 1e3);
217
+ if (Math.abs(seconds) < 60) return "just now";
218
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
219
+ for (const [unit, secs] of UNITS) {
220
+ if (Math.abs(seconds) >= secs) {
221
+ const value = Math.round(seconds / secs);
222
+ return rtf.format(-value, unit);
223
+ }
224
+ }
225
+ return "just now";
226
+ }
227
+
67
228
  // src/components/PostHeader.tsx
68
229
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
69
- function formatRelativeTime(iso) {
70
- const diff = Date.now() - new Date(iso).getTime();
71
- const minutes = Math.floor(diff / 6e4);
72
- if (minutes < 1) return "just now";
73
- if (minutes < 60) return `${minutes}m`;
74
- const hours = Math.floor(minutes / 60);
75
- if (hours < 24) return `${hours}h`;
76
- const days = Math.floor(hours / 24);
77
- return `${days}d`;
78
- }
79
230
  var PostHeader = ({
80
231
  author,
81
232
  authorHref,
@@ -83,6 +234,11 @@ var PostHeader = ({
83
234
  groupHref,
84
235
  postedAt,
85
236
  postType,
237
+ isPrivate = false,
238
+ membersCount,
239
+ crossPostGroup,
240
+ locale,
241
+ translate,
86
242
  isFeatured = false,
87
243
  isPostPreview = false,
88
244
  isAdmin = false,
@@ -95,6 +251,13 @@ var PostHeader = ({
95
251
  labels
96
252
  }) => {
97
253
  const cancelLabel = labels?.cancelPost ?? "Cancel";
254
+ const { key: hintKey, params: hintParams } = getPostTypeHint(postType, {
255
+ isInGroup: !!group,
256
+ isOwner,
257
+ isPrivate,
258
+ membersCount
259
+ });
260
+ const hintText = translate?.(hintKey, hintParams) ?? POST_TYPE_HINT_DEFAULTS[hintKey]?.(hintParams) ?? hintKey;
98
261
  const badgeVariant = isOwner ? "owner" : isAdmin ? "admin" : void 0;
99
262
  const authorNameNode = isPostPreview ? /* @__PURE__ */ jsx2("span", { className: "flex text-base font-semibold leading-[100%] text-foreground", children: author.name }) : LinkComponent ? /* @__PURE__ */ jsx2(
100
263
  LinkComponent,
@@ -173,7 +336,7 @@ var PostHeader = ({
173
336
  viewBox: "0 0 24 24",
174
337
  width: 14,
175
338
  height: 14,
176
- className: "text-feedback-warning fill-feedback-warning shrink-0",
339
+ className: "text-highlight fill-highlight shrink-0",
177
340
  "aria-label": "Featured post",
178
341
  children: /* @__PURE__ */ jsx2("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" })
179
342
  }
@@ -186,16 +349,44 @@ var PostHeader = ({
186
349
  title: postedAt,
187
350
  className: "text-xs text-muted-foreground whitespace-nowrap shrink-0",
188
351
  dateTime: postedAt,
189
- children: formatRelativeTime(postedAt)
352
+ children: formatRelativeTime(postedAt, locale ?? "en")
190
353
  }
191
354
  )
192
355
  ] })
193
356
  ] }) }),
194
357
  /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground truncate mt-0.5", children: [
195
- /* @__PURE__ */ jsx2("span", { children: postType }),
358
+ /* @__PURE__ */ jsx2("span", { children: hintText }),
196
359
  groupLinkNode && /* @__PURE__ */ jsxs(Fragment, { children: [
197
360
  /* @__PURE__ */ jsx2("span", { className: "mx-1" }),
198
361
  groupLinkNode
362
+ ] }),
363
+ crossPostGroup && /* @__PURE__ */ jsxs(Fragment, { children: [
364
+ /* @__PURE__ */ jsx2("span", { className: "mx-1", children: "via" }),
365
+ LinkComponent && crossPostGroup.href ? /* @__PURE__ */ jsxs(
366
+ LinkComponent,
367
+ {
368
+ href: crossPostGroup.href,
369
+ className: "text-action-primary font-medium hover:underline no-underline",
370
+ children: [
371
+ "#",
372
+ crossPostGroup.name
373
+ ]
374
+ }
375
+ ) : crossPostGroup.href ? /* @__PURE__ */ jsxs(
376
+ "a",
377
+ {
378
+ href: crossPostGroup.href,
379
+ onClick: (e) => e.stopPropagation(),
380
+ className: "text-action-primary font-medium hover:underline no-underline",
381
+ children: [
382
+ "#",
383
+ crossPostGroup.name
384
+ ]
385
+ }
386
+ ) : /* @__PURE__ */ jsxs("span", { className: "text-action-primary font-medium", children: [
387
+ "#",
388
+ crossPostGroup.name
389
+ ] })
199
390
  ] })
200
391
  ] })
201
392
  ] })
@@ -2568,6 +2759,11 @@ var PostCard = ({
2568
2759
  isAdmin,
2569
2760
  isOwner,
2570
2761
  isPostPreview,
2762
+ isPrivate,
2763
+ membersCount,
2764
+ crossPostGroup,
2765
+ locale,
2766
+ translate,
2571
2767
  onPostPress,
2572
2768
  onErasePress,
2573
2769
  onCancelPost,
@@ -2645,10 +2841,10 @@ var PostCard = ({
2645
2841
  "article",
2646
2842
  {
2647
2843
  className: cn(
2648
- "flex flex-col box-border bg-background",
2844
+ "w-full flex flex-col box-border bg-background",
2649
2845
  !isPostDetail && "cursor-pointer sm:border border-border rounded-xl sm:rounded-2xl overflow-hidden shadow-sm hover:shadow-md transition-shadow duration-300 relative mt-6",
2650
2846
  isPostDetail && "cursor-auto rounded-t-2xl w-full",
2651
- post.isFeatured && !isPostDetail && "bg-surface-raised border-border-subtle sm:border-t-4",
2847
+ post.isFeatured && !isPostDetail && "bg-highlight-surface border-highlight-border sm:border-t-4",
2652
2848
  isPending && "cursor-default",
2653
2849
  className
2654
2850
  ),
@@ -2697,6 +2893,11 @@ var PostCard = ({
2697
2893
  isPostPreview,
2698
2894
  isAdmin,
2699
2895
  isOwner,
2896
+ isPrivate,
2897
+ membersCount,
2898
+ crossPostGroup,
2899
+ locale,
2900
+ translate,
2700
2901
  onErasePress: onErasePress ? handleErasePress : void 0,
2701
2902
  onCancelPost,
2702
2903
  renderActionsMenu,
@@ -2900,6 +3101,7 @@ export {
2900
3101
  AutoLink_default as AutoLink,
2901
3102
  EraseConfirmModal_default as EraseConfirmModal,
2902
3103
  MediaUploadStatusOverlay_default as MediaUploadStatusOverlay,
3104
+ POST_TYPE_HINT_DEFAULTS,
2903
3105
  PostBody_default as PostBody,
2904
3106
  PostCard_default as PostCard,
2905
3107
  PostEmail_default as PostEmail,
@@ -2916,6 +3118,7 @@ export {
2916
3118
  PostTextItem_default as PostTextItem,
2917
3119
  PostUploadStrip_default as PostUploadStrip,
2918
3120
  ReactionModal,
3121
+ getPostTypeHint,
2919
3122
  hasMeaningfulHtml,
2920
3123
  resolvePostContent
2921
3124
  };