@select-org/post-components 0.2.1 → 1.0.1

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 ADDED
@@ -0,0 +1,2932 @@
1
+ import {
2
+ SPOTIFY_HEIGHT,
3
+ TruncateText,
4
+ hasMeaningfulHtml,
5
+ postItemType,
6
+ resolvePostContent
7
+ } from "./chunk-JPYOLIZH.js";
8
+
9
+ // src/components/PostCard.tsx
10
+ import React11, { useMemo as useMemo7, useState as useState5 } from "react";
11
+
12
+ // src/utils/cn.ts
13
+ import { clsx } from "clsx";
14
+ import { twMerge } from "tailwind-merge";
15
+ function cn(...inputs) {
16
+ return twMerge(clsx(inputs));
17
+ }
18
+
19
+ // src/components/PostHeader.tsx
20
+ import {
21
+ Avatar,
22
+ AvatarImage,
23
+ AvatarFallback,
24
+ AvatarBadge
25
+ } from "@select-org/ui";
26
+
27
+ // src/components/AppIcon.tsx
28
+ import {
29
+ AlarmClock,
30
+ Check,
31
+ CheckCircle2,
32
+ CirclePlay,
33
+ ExternalLink,
34
+ LoaderCircle,
35
+ MessageCircleMore,
36
+ Smile,
37
+ ThumbsDown,
38
+ ThumbsUp,
39
+ Trash2,
40
+ TriangleAlert,
41
+ Video,
42
+ X
43
+ } from "lucide-react";
44
+ import { jsx } from "react/jsx-runtime";
45
+ var ICON_MAP = {
46
+ video: Video,
47
+ circlePlay: CirclePlay,
48
+ cancel: X,
49
+ externalLink: ExternalLink,
50
+ sendEmoji: Smile,
51
+ alarmClock: AlarmClock,
52
+ messageCircleMore: MessageCircleMore,
53
+ chatErase: Trash2,
54
+ checkCircle2: CheckCircle2,
55
+ thumbUp: ThumbsUp,
56
+ thumbDown: ThumbsDown,
57
+ check: Check,
58
+ loader: LoaderCircle,
59
+ triangleAlert: TriangleAlert
60
+ };
61
+ function AppIcon({ name, ...props }) {
62
+ const Icon = ICON_MAP[name];
63
+ if (!Icon) return null;
64
+ return /* @__PURE__ */ jsx(Icon, { ...props });
65
+ }
66
+
67
+ // src/components/PostHeader.tsx
68
+ 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
+ var PostHeader = ({
80
+ author,
81
+ authorHref,
82
+ group,
83
+ groupHref,
84
+ postedAt,
85
+ postType,
86
+ isFeatured = false,
87
+ isPostPreview = false,
88
+ isAdmin = false,
89
+ isOwner = false,
90
+ onErasePress,
91
+ onCancelPost,
92
+ renderActionsMenu,
93
+ linkComponent: LinkComponent,
94
+ imageComponent,
95
+ labels
96
+ }) => {
97
+ const cancelLabel = labels?.cancelPost ?? "Cancel";
98
+ const badgeVariant = isOwner ? "owner" : isAdmin ? "admin" : void 0;
99
+ const authorNameNode = isPostPreview ? /* @__PURE__ */ jsx2("span", { className: "flex text-base font-semibold leading-[100%] text-foreground", children: author.name }) : LinkComponent ? /* @__PURE__ */ jsx2(
100
+ LinkComponent,
101
+ {
102
+ href: authorHref,
103
+ className: "flex text-base font-semibold leading-[100%] text-foreground hover:opacity-70 bg-transparent border-none p-0 cursor-pointer no-underline",
104
+ children: author.name
105
+ }
106
+ ) : /* @__PURE__ */ jsx2(
107
+ "a",
108
+ {
109
+ href: authorHref,
110
+ onClick: (e) => e.stopPropagation(),
111
+ className: "flex text-base font-semibold leading-[100%] text-foreground hover:opacity-70 bg-transparent border-none p-0 cursor-pointer no-underline",
112
+ "aria-label": `View ${author.name}'s profile`,
113
+ children: author.name
114
+ }
115
+ );
116
+ const groupLinkNode = group && groupHref ? isPostPreview ? /* @__PURE__ */ jsxs("span", { className: "text-action-primary font-medium", children: [
117
+ "#",
118
+ group.name
119
+ ] }) : LinkComponent ? /* @__PURE__ */ jsxs(
120
+ LinkComponent,
121
+ {
122
+ href: groupHref,
123
+ className: "text-action-primary font-medium hover:underline cursor-pointer bg-transparent border-none p-0",
124
+ children: [
125
+ "#",
126
+ group.name
127
+ ]
128
+ }
129
+ ) : /* @__PURE__ */ jsxs(
130
+ "a",
131
+ {
132
+ href: groupHref,
133
+ onClick: (e) => e.stopPropagation(),
134
+ className: "text-action-primary font-medium hover:underline cursor-pointer bg-transparent border-none p-0",
135
+ title: group.name,
136
+ "aria-label": `View group ${group.name}`,
137
+ children: [
138
+ "#",
139
+ group.name
140
+ ]
141
+ }
142
+ ) : null;
143
+ return /* @__PURE__ */ jsxs("section", { className: "not-prose relative flex flex-col justify-start p-4 pb-3", children: [
144
+ /* @__PURE__ */ jsxs("section", { className: "flex items-center gap-3", children: [
145
+ /* @__PURE__ */ jsxs(
146
+ Avatar,
147
+ {
148
+ size: "md",
149
+ disabled: isPostPreview,
150
+ className: "shrink-0 ring-1 ring-border-subtle",
151
+ children: [
152
+ /* @__PURE__ */ jsx2(
153
+ AvatarImage,
154
+ {
155
+ src: author.avatar,
156
+ alt: `${author.name}'s profile picture`,
157
+ placeholder: author.avatarPlaceholder,
158
+ imageComponent
159
+ }
160
+ ),
161
+ /* @__PURE__ */ jsx2(AvatarFallback, { name: author.name }),
162
+ badgeVariant && /* @__PURE__ */ jsx2(AvatarBadge, { variant: badgeVariant })
163
+ ]
164
+ }
165
+ ),
166
+ /* @__PURE__ */ jsxs("section", { className: "flex flex-1 flex-col justify-center min-w-0", children: [
167
+ /* @__PURE__ */ jsx2("section", { className: "flex items-baseline justify-between gap-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [
168
+ /* @__PURE__ */ jsx2("div", { className: "font-semibold text-foreground truncate", children: authorNameNode }),
169
+ isFeatured && /* @__PURE__ */ jsx2(
170
+ "svg",
171
+ {
172
+ xmlns: "http://www.w3.org/2000/svg",
173
+ viewBox: "0 0 24 24",
174
+ width: 14,
175
+ height: 14,
176
+ className: "text-feedback-warning fill-feedback-warning shrink-0",
177
+ "aria-label": "Featured post",
178
+ 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
+ }
180
+ ),
181
+ postedAt && /* @__PURE__ */ jsxs(Fragment, { children: [
182
+ /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground font-bold shrink-0", children: "\xB7" }),
183
+ /* @__PURE__ */ jsx2(
184
+ "time",
185
+ {
186
+ title: postedAt,
187
+ className: "text-xs text-muted-foreground whitespace-nowrap shrink-0",
188
+ dateTime: postedAt,
189
+ children: formatRelativeTime(postedAt)
190
+ }
191
+ )
192
+ ] })
193
+ ] }) }),
194
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground truncate mt-0.5", children: [
195
+ /* @__PURE__ */ jsx2("span", { children: postType }),
196
+ groupLinkNode && /* @__PURE__ */ jsxs(Fragment, { children: [
197
+ /* @__PURE__ */ jsx2("span", { className: "mx-1" }),
198
+ groupLinkNode
199
+ ] })
200
+ ] })
201
+ ] })
202
+ ] }),
203
+ isPostPreview ? onCancelPost && /* @__PURE__ */ jsx2(
204
+ "button",
205
+ {
206
+ onClick: (e) => {
207
+ e.stopPropagation();
208
+ onCancelPost();
209
+ },
210
+ "aria-label": cancelLabel,
211
+ "data-testid": "cancel-pending-post",
212
+ className: "absolute right-4 top-4 leading-none rounded-full p-1.5 transition-colors hover:bg-feedback-danger/10 hover:text-feedback-danger",
213
+ children: /* @__PURE__ */ jsx2(AppIcon, { name: "cancel", width: 16, height: 16, strokeWidth: 1.75 })
214
+ }
215
+ ) : renderActionsMenu ? /* @__PURE__ */ jsx2("div", { className: "absolute right-2 top-2", children: renderActionsMenu() }) : onErasePress ? /* @__PURE__ */ jsx2(
216
+ "button",
217
+ {
218
+ onClick: (e) => {
219
+ e.stopPropagation();
220
+ onErasePress();
221
+ },
222
+ "aria-label": "Delete post",
223
+ className: "absolute right-4 top-4 leading-none rounded-full p-1.5 transition-colors hover:bg-feedback-danger/10 hover:text-feedback-danger",
224
+ children: /* @__PURE__ */ jsx2(AppIcon, { name: "chatErase", width: 16, height: 16, strokeWidth: 1.75 })
225
+ }
226
+ ) : null
227
+ ] });
228
+ };
229
+ var PostHeader_default = PostHeader;
230
+
231
+ // src/components/AutoLink.tsx
232
+ import { useCallback, useMemo } from "react";
233
+ import Autolinker from "autolinker";
234
+ import { truncateSmart } from "autolinker/dist/commonjs/truncate/truncate-smart";
235
+ import { truncateEnd } from "autolinker/dist/commonjs/truncate/truncate-end";
236
+ import { truncateMiddle } from "autolinker/dist/commonjs/truncate/truncate-middle";
237
+ import emojiRegex from "emoji-regex";
238
+ import { jsx as jsx3 } from "react/jsx-runtime";
239
+ var MENTION_TYPE = {
240
+ MENTION: "mention",
241
+ HASHTAG: "hashtag"
242
+ };
243
+ var EMOJI_REGEX = emojiRegex();
244
+ var getPureEmojiSize = (body) => {
245
+ let match;
246
+ let emojiCount = 0;
247
+ let emojiSize = 0;
248
+ while (match = EMOJI_REGEX.exec(body)) {
249
+ const emoji = match[0];
250
+ emojiSize += emoji.length;
251
+ emojiCount += 1;
252
+ }
253
+ if (emojiSize === body.length) {
254
+ return emojiCount;
255
+ }
256
+ return 0;
257
+ };
258
+ var checkMentionDataIndex = (mentionData = [], copyMentionData = [], body = "") => {
259
+ let mentionBody = body;
260
+ const newMentionData = [...mentionData, ...copyMentionData];
261
+ let lastMentionData = { index: 0, endIndex: 0 };
262
+ const formatNewMention = [];
263
+ for (let i = 0; i < newMentionData.length; i++) {
264
+ const mention = { ...newMentionData[i] };
265
+ if (mention.name && typeof mention.name === "string") {
266
+ mention.name = mention.name.trim();
267
+ } else {
268
+ continue;
269
+ }
270
+ if (mentionBody.indexOf(mention.name) === -1) {
271
+ continue;
272
+ }
273
+ const type = mention.type;
274
+ const name = (type === MENTION_TYPE.MENTION ? "@" : "#") + mention.name;
275
+ const index = mentionBody.indexOf(name) + lastMentionData.endIndex;
276
+ const endIndex = index + name.length;
277
+ mentionBody = body.substr(endIndex, body.length);
278
+ lastMentionData = { index, endIndex };
279
+ formatNewMention.push({
280
+ name,
281
+ type,
282
+ index,
283
+ endIndex,
284
+ data: mention
285
+ });
286
+ }
287
+ return formatNewMention;
288
+ };
289
+ var isLinkInternal = (url, supportDeepviewDomain) => {
290
+ if (!url) return false;
291
+ return supportDeepviewDomain.length > 0 && url.includes(supportDeepviewDomain);
292
+ };
293
+ var getUrl = (match, phone) => {
294
+ const type = match.getType();
295
+ switch (type) {
296
+ case "email": {
297
+ return [`mailto:${encodeURIComponent(match.getEmail())}`];
298
+ }
299
+ case "phone": {
300
+ const number = match.getNumber();
301
+ switch (phone) {
302
+ case "sms":
303
+ case "text":
304
+ return [`sms:${number}`];
305
+ default:
306
+ return [`tel:${number}`];
307
+ }
308
+ }
309
+ case "url": {
310
+ return [match.getAnchorHref()];
311
+ }
312
+ default: {
313
+ return [match.getMatchedText()];
314
+ }
315
+ }
316
+ };
317
+ var truncate = (text, { truncate: len = 32, truncateChars = "..", truncateLocation = "smart" } = {}) => {
318
+ let fn;
319
+ switch (truncateLocation) {
320
+ case "end":
321
+ fn = truncateEnd;
322
+ break;
323
+ case "middle":
324
+ fn = truncateMiddle;
325
+ break;
326
+ default:
327
+ fn = truncateSmart;
328
+ }
329
+ return fn(text, len, truncateChars);
330
+ };
331
+ var AutoLink = ({
332
+ text: textProp,
333
+ mentionData = [],
334
+ onNavigate,
335
+ supportDeepviewDomain = "",
336
+ phone = true,
337
+ email = true,
338
+ stripPrefix = false,
339
+ style = { fontSize: 12 },
340
+ mentionClassName: mentionClassNameProp,
341
+ className
342
+ }) => {
343
+ const mentionClassName = useMemo(
344
+ () => mentionClassNameProp || "font-bold text-action-primary",
345
+ [mentionClassNameProp]
346
+ );
347
+ const handleNavigate = useCallback(
348
+ (href, internal) => {
349
+ if (onNavigate) {
350
+ onNavigate(href, internal);
351
+ } else if (internal) {
352
+ window.location.href = href;
353
+ } else {
354
+ window.open(href, "_blank");
355
+ }
356
+ },
357
+ [onNavigate]
358
+ );
359
+ const renderDefaultLink = useCallback(
360
+ (text, match, index) => {
361
+ const [url] = getUrl(match, phone);
362
+ const internal = isLinkInternal(url, supportDeepviewDomain);
363
+ return /* @__PURE__ */ jsx3(
364
+ "a",
365
+ {
366
+ target: "_blank",
367
+ rel: `noreferrer noopener${!internal ? " nofollow" : ""}`,
368
+ href: url,
369
+ className: "items-center text-link",
370
+ onClick: (e) => {
371
+ e.stopPropagation();
372
+ handleNavigate(url, internal);
373
+ },
374
+ children: truncate(text, { truncate: 27, truncateLocation: "end" })
375
+ },
376
+ `${index}-autolink`
377
+ );
378
+ },
379
+ [phone, supportDeepviewDomain, handleNavigate]
380
+ );
381
+ const renderLink = useCallback(
382
+ (text, match, index) => {
383
+ const [url] = getUrl(match, phone);
384
+ if (supportDeepviewDomain && url.includes(supportDeepviewDomain)) {
385
+ const internal = true;
386
+ return /* @__PURE__ */ jsx3(
387
+ "a",
388
+ {
389
+ href: url,
390
+ className: "items-center text-link",
391
+ style: { alignSelf: "center" },
392
+ onClick: (e) => {
393
+ e.stopPropagation();
394
+ handleNavigate(url, internal);
395
+ },
396
+ children: text
397
+ },
398
+ `${index}-deeplink`
399
+ );
400
+ }
401
+ return renderDefaultLink(text, match, index);
402
+ },
403
+ [phone, supportDeepviewDomain, renderDefaultLink, handleNavigate]
404
+ );
405
+ const renderMentionData = useCallback(
406
+ (fmData, key) => {
407
+ const href = fmData.type === MENTION_TYPE.HASHTAG ? `/groups/${fmData.data.selectId}` : `/users/${fmData.data.selectId || fmData.data.id}`;
408
+ return /* @__PURE__ */ jsx3(
409
+ "a",
410
+ {
411
+ className: `context-menu-ios-disable cursor-pointer ${mentionClassName}`,
412
+ href,
413
+ onClick: (e) => {
414
+ e.stopPropagation();
415
+ e.nativeEvent.stopImmediatePropagation();
416
+ handleNavigate(href, true);
417
+ },
418
+ children: fmData.name
419
+ },
420
+ key + fmData.index
421
+ );
422
+ },
423
+ [mentionClassName, handleNavigate]
424
+ );
425
+ const renderTextNodes = useCallback(() => {
426
+ let text = textProp;
427
+ const uid = Math.floor(Math.random() * 1099511627776).toString(16);
428
+ const tokenRegexp = new RegExp(`(@__ELEMENT-${uid}-\\d+__@)`, "g");
429
+ const generateToken = /* @__PURE__ */ (() => {
430
+ let counter = 0;
431
+ return () => `@__ELEMENT-${uid}-${counter++}__@`;
432
+ })();
433
+ const matches = {};
434
+ try {
435
+ text = Autolinker.link(text || "", {
436
+ email,
437
+ phone,
438
+ urls: {
439
+ schemeMatches: true,
440
+ wwwMatches: true,
441
+ tldMatches: true
442
+ },
443
+ stripPrefix,
444
+ replaceFn: (match) => {
445
+ const token = generateToken();
446
+ matches[token] = match;
447
+ return token;
448
+ }
449
+ });
450
+ } catch {
451
+ return null;
452
+ }
453
+ const newNodes = [];
454
+ text.split(tokenRegexp).filter((part) => !!part).forEach((part, index) => {
455
+ const match = matches[part];
456
+ if (!match) {
457
+ if (!mentionData || mentionData.length === 0) {
458
+ newNodes.push(part);
459
+ } else {
460
+ const newMentionData = checkMentionDataIndex(mentionData, [], part);
461
+ if (!newMentionData || newMentionData.length === 0) {
462
+ newNodes.push(part);
463
+ return;
464
+ }
465
+ for (let j = 0; j < newMentionData.length; j++) {
466
+ const curMentionData = newMentionData[j];
467
+ if (j === 0) {
468
+ const word = part.substring(0, curMentionData.index);
469
+ newNodes.push(word);
470
+ }
471
+ newNodes.push(renderMentionData(curMentionData, part + index));
472
+ const nextMentionData = j < newMentionData.length ? newMentionData[j + 1] : null;
473
+ if (nextMentionData) {
474
+ const word = part.substring(curMentionData.endIndex, nextMentionData.index);
475
+ newNodes.push(word);
476
+ } else {
477
+ const word = part.substring(curMentionData.endIndex, part.length);
478
+ newNodes.push(word);
479
+ }
480
+ }
481
+ }
482
+ return;
483
+ }
484
+ switch (match.getType()) {
485
+ case "email":
486
+ case "phone":
487
+ case "url":
488
+ newNodes.push(renderLink(match.getAnchorText(), match, index));
489
+ break;
490
+ default:
491
+ newNodes.push(part);
492
+ break;
493
+ }
494
+ });
495
+ let textStyle = style;
496
+ const fontSize = style && style.fontSize ? style.fontSize : 12;
497
+ const pureEmojiCount = getPureEmojiSize(text.trim());
498
+ if (pureEmojiCount === 1) {
499
+ textStyle = { ...style, fontSize: fontSize * 2.5 };
500
+ } else if (pureEmojiCount === 2) {
501
+ textStyle = { ...style, fontSize: fontSize * 2 };
502
+ } else if (pureEmojiCount === 3) {
503
+ textStyle = { ...style, fontSize: fontSize * 1.5 };
504
+ }
505
+ return /* @__PURE__ */ jsx3("div", { style: textStyle, className, children: newNodes });
506
+ }, [
507
+ style,
508
+ email,
509
+ phone,
510
+ stripPrefix,
511
+ mentionData,
512
+ textProp,
513
+ className,
514
+ renderLink,
515
+ renderMentionData
516
+ ]);
517
+ return renderTextNodes();
518
+ };
519
+ AutoLink.truncate = truncate;
520
+ var AutoLink_default = AutoLink;
521
+
522
+ // src/components/PostBody.tsx
523
+ import DOMPurify2 from "dompurify";
524
+ import { useMemo as useMemo3 } from "react";
525
+
526
+ // src/components/PostEmail.tsx
527
+ import { useEffect, useMemo as useMemo2, useRef } from "react";
528
+ import DOMPurify from "dompurify";
529
+ import { jsx as jsx4 } from "react/jsx-runtime";
530
+ var EMAIL_PURIFY_CONFIG = {
531
+ ALLOWED_TAGS: [
532
+ // Structure
533
+ "html",
534
+ "head",
535
+ "body",
536
+ "style",
537
+ // Layout (email is table-based)
538
+ "table",
539
+ "thead",
540
+ "tbody",
541
+ "tr",
542
+ "th",
543
+ "td",
544
+ // Text
545
+ "p",
546
+ "span",
547
+ "div",
548
+ "h1",
549
+ "h2",
550
+ "h3",
551
+ "h4",
552
+ "h5",
553
+ "h6",
554
+ "b",
555
+ "strong",
556
+ "i",
557
+ "em",
558
+ "u",
559
+ "sub",
560
+ "sup",
561
+ "br",
562
+ "ul",
563
+ "ol",
564
+ "li",
565
+ // Media & links
566
+ "a",
567
+ "img"
568
+ ],
569
+ ALLOWED_ATTR: [
570
+ // Styling — critical for email
571
+ "style",
572
+ "class",
573
+ "bgcolor",
574
+ "color",
575
+ // Table layout attributes (email-specific)
576
+ "width",
577
+ "height",
578
+ "align",
579
+ "valign",
580
+ "cellpadding",
581
+ "cellspacing",
582
+ "border",
583
+ "colspan",
584
+ "rowspan",
585
+ // Images
586
+ "src",
587
+ "alt",
588
+ // Links
589
+ "href",
590
+ "target",
591
+ "rel",
592
+ // Accessibility
593
+ "role",
594
+ "aria-label",
595
+ "aria-roledescription",
596
+ // Email-specific
597
+ "data-open-tracking",
598
+ "data-read-online-tooltip"
599
+ ],
600
+ ALLOW_DATA_ATTR: false,
601
+ FORCE_BODY: false,
602
+ WHOLE_DOCUMENT: true
603
+ // preserves <head> with <style>
604
+ };
605
+ var sanitizeEmailHtml = (rawHtml) => {
606
+ const clean = DOMPurify.sanitize(rawHtml, EMAIL_PURIFY_CONFIG);
607
+ const doc = new DOMParser().parseFromString(clean, "text/html");
608
+ if (!doc.head) {
609
+ const head = doc.createElement("head");
610
+ doc.documentElement.insertBefore(head, doc.body);
611
+ }
612
+ const base = doc.createElement("base");
613
+ base.setAttribute("target", "_blank");
614
+ doc.head.appendChild(base);
615
+ const style = doc.createElement("style");
616
+ style.textContent = `
617
+ html, body {
618
+ margin: 0;
619
+ padding: 0;
620
+ }
621
+
622
+ @media only screen and (max-width: 640px) {
623
+ table[style*="min-width"] {
624
+ min-width: 0 !important;
625
+ width: 100% !important;
626
+ max-width: 100% !important;
627
+ }
628
+
629
+ td[style*="min-width"],
630
+ th[style*="min-width"],
631
+ div[style*="min-width"] {
632
+ min-width: 0 !important;
633
+ max-width: 100% !important;
634
+ box-sizing: border-box !important;
635
+ }
636
+
637
+ img {
638
+ max-width: 100% !important;
639
+ height: auto !important;
640
+ }
641
+ }`;
642
+ doc.head.appendChild(style);
643
+ doc.querySelectorAll("a[href]").forEach((a) => {
644
+ a.setAttribute("target", "_blank");
645
+ a.setAttribute("rel", "noopener noreferrer nofollow");
646
+ });
647
+ return "<!DOCTYPE html>\n" + doc.documentElement.outerHTML;
648
+ };
649
+ var PostEmail = ({ html }) => {
650
+ const ref = useRef(null);
651
+ const cleanHtml = useMemo2(() => sanitizeEmailHtml(html), [html]);
652
+ useEffect(() => {
653
+ const iframe = ref.current;
654
+ if (!iframe) return;
655
+ let resizeObserver = null;
656
+ let rafId = null;
657
+ let timeoutIds = [];
658
+ const resizeIframe = () => {
659
+ try {
660
+ const doc = iframe.contentDocument;
661
+ if (!doc) return;
662
+ const body = doc.body;
663
+ const height = Math.max(
664
+ Math.ceil(body.scrollHeight),
665
+ Math.ceil(body.offsetHeight),
666
+ Math.ceil(body.getBoundingClientRect().height)
667
+ );
668
+ iframe.style.height = `${height + 2}px`;
669
+ } catch {
670
+ }
671
+ };
672
+ const scheduleResize = () => {
673
+ if (rafId) cancelAnimationFrame(rafId);
674
+ rafId = requestAnimationFrame(resizeIframe);
675
+ };
676
+ const handleLoad = () => {
677
+ scheduleResize();
678
+ try {
679
+ const doc = iframe.contentDocument;
680
+ if (!doc) return;
681
+ Array.from(doc.images).forEach((img) => {
682
+ img.addEventListener("load", scheduleResize);
683
+ img.addEventListener("error", scheduleResize);
684
+ });
685
+ resizeObserver = new ResizeObserver(() => {
686
+ scheduleResize();
687
+ });
688
+ resizeObserver.observe(doc.body);
689
+ timeoutIds = [
690
+ window.setTimeout(scheduleResize, 50),
691
+ window.setTimeout(scheduleResize, 150),
692
+ window.setTimeout(scheduleResize, 300),
693
+ window.setTimeout(scheduleResize, 600)
694
+ ];
695
+ } catch {
696
+ }
697
+ };
698
+ const handleResize = () => {
699
+ scheduleResize();
700
+ };
701
+ iframe.addEventListener("load", handleLoad);
702
+ window.addEventListener("resize", handleResize);
703
+ return () => {
704
+ iframe.removeEventListener("load", handleLoad);
705
+ window.removeEventListener("resize", handleResize);
706
+ if (rafId) cancelAnimationFrame(rafId);
707
+ timeoutIds.forEach(clearTimeout);
708
+ resizeObserver?.disconnect();
709
+ try {
710
+ const doc = iframe.contentDocument;
711
+ if (!doc) return;
712
+ Array.from(doc.images).forEach((img) => {
713
+ img.removeEventListener("load", scheduleResize);
714
+ img.removeEventListener("error", scheduleResize);
715
+ });
716
+ } catch {
717
+ }
718
+ };
719
+ }, [cleanHtml]);
720
+ return /* @__PURE__ */ jsx4(
721
+ "iframe",
722
+ {
723
+ ref,
724
+ srcDoc: cleanHtml,
725
+ sandbox: "allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation",
726
+ className: "block w-full border-0"
727
+ }
728
+ );
729
+ };
730
+ var PostEmail_default = PostEmail;
731
+
732
+ // src/components/PostBody.tsx
733
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
734
+ var PURIFY_CONFIG = {
735
+ ALLOWED_TAGS: [
736
+ "p",
737
+ "br",
738
+ "b",
739
+ "i",
740
+ "em",
741
+ "strong",
742
+ "a",
743
+ "ul",
744
+ "ol",
745
+ "li",
746
+ "h1",
747
+ "h2",
748
+ "h3",
749
+ "h4",
750
+ "h5",
751
+ "h6",
752
+ "blockquote",
753
+ "code",
754
+ "pre",
755
+ "span",
756
+ "div",
757
+ "img",
758
+ "video",
759
+ "figure",
760
+ "figcaption",
761
+ "table",
762
+ "thead",
763
+ "tbody",
764
+ "tr",
765
+ "th",
766
+ "td",
767
+ "hr",
768
+ "sub",
769
+ "sup",
770
+ "s",
771
+ "u"
772
+ ],
773
+ ALLOWED_ATTR: [
774
+ "href",
775
+ "controls",
776
+ "target",
777
+ "rel",
778
+ "class",
779
+ "id",
780
+ "src",
781
+ "alt",
782
+ "title",
783
+ "width",
784
+ "height",
785
+ "data-*"
786
+ ],
787
+ ALLOW_DATA_ATTR: true,
788
+ ADD_ATTR: ["target"],
789
+ FORBID_TAGS: ["script", "style", "iframe", "object", "embed", "form"],
790
+ FORBID_ATTR: ["onerror", "onload", "onclick", "onmouseover"]
791
+ };
792
+ var VOID_TAGS = /* @__PURE__ */ new Set([
793
+ "BR",
794
+ "HR",
795
+ "IMG",
796
+ "VIDEO",
797
+ "INPUT",
798
+ "AREA",
799
+ "BASE",
800
+ "COL",
801
+ "LINK",
802
+ "META",
803
+ "PARAM",
804
+ "SOURCE",
805
+ "TRACK",
806
+ "WBR"
807
+ ]);
808
+ var removeEmpty = (el) => {
809
+ Array.from(el.children).forEach(removeEmpty);
810
+ if (VOID_TAGS.has(el.tagName)) return;
811
+ if (!el.textContent?.trim() && el.children.length === 0) el.remove();
812
+ };
813
+ var PostBody = ({
814
+ textContent,
815
+ showFull = true,
816
+ isPostDetail,
817
+ isRichContent = false,
818
+ hideEmailDescription = false,
819
+ isEmailPost = false,
820
+ onNavigate,
821
+ supportDeepviewDomain
822
+ }) => {
823
+ const { plain, html, truncatedPlain, mentionData } = textContent;
824
+ const { sanitizedHtml, feedSanitizedHtml } = useMemo3(() => {
825
+ if (!html) return { sanitizedHtml: null, feedSanitizedHtml: null };
826
+ const sanitized = DOMPurify2.sanitize(html, PURIFY_CONFIG);
827
+ if (typeof document === "undefined") {
828
+ return { sanitizedHtml: sanitized, feedSanitizedHtml: sanitized };
829
+ }
830
+ const feedContainer = document.createElement("div");
831
+ feedContainer.innerHTML = sanitized;
832
+ let keptOne = false;
833
+ feedContainer.querySelectorAll("img, video").forEach((el) => {
834
+ if (!keptOne) {
835
+ keptOne = true;
836
+ } else {
837
+ el.remove();
838
+ }
839
+ });
840
+ removeEmpty(feedContainer);
841
+ return {
842
+ sanitizedHtml: sanitized,
843
+ feedSanitizedHtml: feedContainer.innerHTML
844
+ };
845
+ }, [html]);
846
+ const { subject } = textContent;
847
+ const displayText = showFull ? plain : truncatedPlain;
848
+ const hasHtml = !!sanitizedHtml;
849
+ const hasMeaningfulHtmlText = hasMeaningfulHtml(sanitizedHtml);
850
+ const detailProseElement = useMemo3(
851
+ () => sanitizedHtml ? /* @__PURE__ */ jsx5(
852
+ "div",
853
+ {
854
+ className: "prose prose-p:my-2! prose-a:text-action-primary! prose-a:no-underline! prose-img:my-4! prose-video:my-4! px-5! pb-4! max-w-full leading-relaxed overflow-hidden",
855
+ dangerouslySetInnerHTML: { __html: sanitizedHtml }
856
+ }
857
+ ) : null,
858
+ [sanitizedHtml]
859
+ );
860
+ const feedProseElement = useMemo3(
861
+ () => feedSanitizedHtml ? /* @__PURE__ */ jsx5(
862
+ "div",
863
+ {
864
+ className: "prose *:px-4! *:data-[media='true']:px-0! *:data-link-preview:mx-4! [&>[data-media='true']>img]:rounded-none! [&>[data-media='true']>video]:rounded-none! [&>[data-media='true']>video]:size-full! prose-video:size-full! *:data-[media='true']:my-2 [&>div]:last-of-type:mb-0! prose-img:my-0! prose-img:w-full prose-video:my-0! prose-p:my-2! prose-a:text-action-primary! prose-a:no-underline! pb-4 max-w-full leading-relaxed line-clamp-4 overflow-hidden",
865
+ dangerouslySetInnerHTML: { __html: feedSanitizedHtml }
866
+ }
867
+ ) : null,
868
+ [feedSanitizedHtml]
869
+ );
870
+ if ((!displayText || typeof displayText !== "string") && !hasHtml) {
871
+ return null;
872
+ }
873
+ const renderAutoLink = () => {
874
+ if (!displayText || typeof displayText !== "string") return null;
875
+ return /* @__PURE__ */ jsx5(
876
+ AutoLink_default,
877
+ {
878
+ text: displayText,
879
+ mentionData,
880
+ onNavigate,
881
+ supportDeepviewDomain,
882
+ className: "whitespace-normal px-4 pb-4 leading-relaxed text-gray-800",
883
+ style: {
884
+ whiteSpace: "pre-wrap",
885
+ wordBreak: "break-word"
886
+ }
887
+ }
888
+ );
889
+ };
890
+ if (isPostDetail) {
891
+ if (isEmailPost && html) {
892
+ return /* @__PURE__ */ jsxs2("div", { className: "relative flex-1 max-h-screen w-full overflow-x-hidden overflow-y-auto xl:px-0 px-5", children: [
893
+ /* @__PURE__ */ jsx5("h1", { className: "text-2xl font-semibold text-gray-900 leading-snug mt-5 mb-3 px-5", children: subject }),
894
+ /* @__PURE__ */ jsx5(PostEmail_default, { html })
895
+ ] });
896
+ }
897
+ return /* @__PURE__ */ jsxs2("div", { className: "flex-1", children: [
898
+ !hasMeaningfulHtmlText && /* @__PURE__ */ jsx5("div", { children: renderAutoLink() }),
899
+ sanitizedHtml && hasMeaningfulHtmlText && /* @__PURE__ */ jsx5("div", { "data-theme": "select-post-builder", className: "w-full max-w-full", children: detailProseElement })
900
+ ] });
901
+ }
902
+ if (isRichContent && feedSanitizedHtml && hasMeaningfulHtmlText) {
903
+ return /* @__PURE__ */ jsx5("div", { className: cn("flex-1 pb-4"), "data-theme": "select-post-builder", children: feedProseElement });
904
+ }
905
+ if (subject) {
906
+ return /* @__PURE__ */ jsxs2("div", { className: "flex-1 px-4 pb-4", children: [
907
+ /* @__PURE__ */ jsx5("h1", { className: "text-2xl font-semibold text-gray-900 leading-snug mb-1", children: subject }),
908
+ !hideEmailDescription && displayText && typeof displayText === "string" && /* @__PURE__ */ jsx5(
909
+ AutoLink_default,
910
+ {
911
+ text: displayText,
912
+ mentionData,
913
+ onNavigate,
914
+ supportDeepviewDomain,
915
+ className: "whitespace-normal leading-relaxed text-gray-600 line-clamp-3",
916
+ style: { whiteSpace: "pre-wrap", wordBreak: "break-word" }
917
+ }
918
+ )
919
+ ] });
920
+ }
921
+ return /* @__PURE__ */ jsx5("div", { className: "flex-1", children: /* @__PURE__ */ jsx5("div", { children: renderAutoLink() }) });
922
+ };
923
+ var PostBody_default = PostBody;
924
+
925
+ // src/components/MediaView.tsx
926
+ import { useMemo as useMemo5 } from "react";
927
+
928
+ // src/components/AsyncImage.tsx
929
+ import { useCallback as useCallback2, useMemo as useMemo4 } from "react";
930
+ import { Gallery, Item } from "react-photoswipe-gallery";
931
+ import "photoswipe/dist/photoswipe.css";
932
+
933
+ // src/components/Loader.tsx
934
+ import { jsx as jsx6 } from "react/jsx-runtime";
935
+ var Loader = ({
936
+ type = "page_loader",
937
+ fullScreen = false
938
+ }) => {
939
+ const style = {
940
+ page_loader: "after:border-border after:border-t-action-primary",
941
+ upload_loader: "after:border-foreground after:border-t-background bg-foreground rounded-full"
942
+ };
943
+ const showLoader = () => {
944
+ return /* @__PURE__ */ jsx6("div", { className: "absolute left-1/2 top-1/2 z-20 -translate-x-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx6(
945
+ "div",
946
+ {
947
+ className: `after:contents-[''] flex h-full items-center justify-center after:h-10 after:w-10 after:animate-spin after:rounded-[50%] after:border-4 ${style[type]} `
948
+ }
949
+ ) });
950
+ };
951
+ if (fullScreen) {
952
+ return /* @__PURE__ */ jsx6("div", { className: "h-screen-safe-14 lg:h-screen-safe-16", children: showLoader() });
953
+ }
954
+ return showLoader();
955
+ };
956
+ var Loader_default = Loader;
957
+
958
+ // src/components/AsyncImage.tsx
959
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
960
+ var isVideo = (url) => {
961
+ return url?.toLowerCase().endsWith(".mp4") || url?.toLowerCase().endsWith(".webm") || url?.toLowerCase().endsWith(".ogg");
962
+ };
963
+ var isImage = (url) => {
964
+ return url?.toLowerCase().endsWith(".jpeg") || url?.toLowerCase().endsWith(".jpg") || url?.toLowerCase().endsWith(".png") || url?.toLowerCase().endsWith(".webp") || url?.toLowerCase().endsWith(".gif") || url?.toLowerCase().endsWith(".avif");
965
+ };
966
+ var calVideoRatio = (width, height) => {
967
+ let ratio = 1;
968
+ if (width > 0 && height > 0) {
969
+ ratio = width / height;
970
+ if (ratio < 0.7) {
971
+ ratio = 0.7;
972
+ }
973
+ }
974
+ return `${ratio}`;
975
+ };
976
+ var AsyncImage = ({
977
+ data,
978
+ ratio,
979
+ className,
980
+ isPostDetail,
981
+ item,
982
+ onCardPress,
983
+ src = "",
984
+ alt
985
+ }) => {
986
+ const {
987
+ placeholder = {
988
+ isBase64: false,
989
+ url: ""
990
+ }
991
+ } = item.sources || {};
992
+ const placeholderUrl = useMemo4(() => {
993
+ const url = placeholder.isBase64 ? `data:${item.imageType};base64,${placeholder.url}` : placeholder.url;
994
+ return url;
995
+ }, [placeholder.isBase64, placeholder.url, item.imageType]);
996
+ const handleClick = useCallback2(
997
+ (e) => {
998
+ e.stopPropagation();
999
+ if (onCardPress) {
1000
+ onCardPress();
1001
+ }
1002
+ },
1003
+ [onCardPress]
1004
+ );
1005
+ const showUploadImg = (item2) => {
1006
+ return item2.imageType === "image/heic" ? /* @__PURE__ */ jsx7(
1007
+ "img",
1008
+ {
1009
+ src,
1010
+ alt: alt ?? "",
1011
+ className,
1012
+ style: {
1013
+ aspectRatio: `${ratio}` || "auto"
1014
+ }
1015
+ }
1016
+ ) : /* @__PURE__ */ jsx7(
1017
+ "div",
1018
+ {
1019
+ className,
1020
+ style: {
1021
+ aspectRatio: `${ratio}` || "auto"
1022
+ }
1023
+ }
1024
+ );
1025
+ };
1026
+ const uploadVideoPost = item.type.includes("ToUpload");
1027
+ const showUploadingMedia = () => /* @__PURE__ */ jsxs3(
1028
+ "div",
1029
+ {
1030
+ className: "relative h-full w-full",
1031
+ style: {
1032
+ aspectRatio: `${ratio}` || "auto"
1033
+ },
1034
+ children: [
1035
+ uploadVideoPost && /* @__PURE__ */ jsx7(Loader_default, { type: "upload_loader" }),
1036
+ /* @__PURE__ */ jsx7(
1037
+ "video",
1038
+ {
1039
+ title: "Video Upload Preview",
1040
+ width: "100%",
1041
+ height: "100%",
1042
+ className,
1043
+ style: {
1044
+ aspectRatio: `${ratio}` || "auto"
1045
+ },
1046
+ children: item.file instanceof Blob && /* @__PURE__ */ jsx7("source", { src: URL.createObjectURL(item.file), type: item.file.type })
1047
+ }
1048
+ ),
1049
+ /* @__PURE__ */ jsx7(
1050
+ AppIcon,
1051
+ {
1052
+ name: "video",
1053
+ className: `absolute bottom-2 left-2 text-background`
1054
+ }
1055
+ )
1056
+ ]
1057
+ }
1058
+ );
1059
+ const renderMediaItem = useCallback2(
1060
+ (media) => {
1061
+ const originalInfo = media?.sources?.original || {};
1062
+ let thumbnailInfo = media?.sources?.thumbnail;
1063
+ if (originalInfo?.url?.endsWith("gif") || !isImage(media?.sources?.thumbnail?.url)) {
1064
+ thumbnailInfo = originalInfo;
1065
+ }
1066
+ const aspectRatio = `${ratio}` || calVideoRatio(originalInfo.width, originalInfo.height) || "auto";
1067
+ const hasStableKeys = Boolean(media.key && item.key);
1068
+ const isCurrentItem = hasStableKeys ? media.key === item.key : originalInfo.url === src;
1069
+ const display = isCurrentItem ? "block" : "none";
1070
+ const isBlobVideo = originalInfo?.url?.startsWith("blob:") && typeof media.imageType === "string" && media.imageType.startsWith("video/");
1071
+ if (isBlobVideo) {
1072
+ return /* @__PURE__ */ jsxs3(
1073
+ "div",
1074
+ {
1075
+ onClick: handleClick,
1076
+ className: className + " media-item",
1077
+ style: { aspectRatio, display },
1078
+ children: [
1079
+ /* @__PURE__ */ jsx7(
1080
+ "video",
1081
+ {
1082
+ src: originalInfo.url,
1083
+ muted: true,
1084
+ playsInline: true,
1085
+ preload: "metadata",
1086
+ className,
1087
+ style: {
1088
+ aspectRatio,
1089
+ display: "block",
1090
+ width: "100%",
1091
+ height: "100%"
1092
+ }
1093
+ }
1094
+ ),
1095
+ /* @__PURE__ */ jsx7(
1096
+ AppIcon,
1097
+ {
1098
+ name: "video",
1099
+ className: "absolute bottom-2 left-2 text-background"
1100
+ }
1101
+ ),
1102
+ /* @__PURE__ */ jsx7(
1103
+ AppIcon,
1104
+ {
1105
+ name: "circlePlay",
1106
+ className: "size-10 bg-foreground/20 hover:bg-foreground/40 transition-colors duration-300 rounded-full absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-3xl text-background"
1107
+ }
1108
+ )
1109
+ ]
1110
+ },
1111
+ media.key
1112
+ );
1113
+ }
1114
+ if (isVideo(originalInfo.url)) {
1115
+ return /* @__PURE__ */ jsx7(
1116
+ Item,
1117
+ {
1118
+ content: /* @__PURE__ */ jsx7(
1119
+ "div",
1120
+ {
1121
+ className: "flex h-full w-full items-center justify-center",
1122
+ style: {
1123
+ aspectRatio
1124
+ },
1125
+ children: /* @__PURE__ */ jsx7(
1126
+ "video",
1127
+ {
1128
+ height: "100%",
1129
+ width: "100%",
1130
+ className: "aspect-video",
1131
+ controls: true,
1132
+ autoPlay: true,
1133
+ children: /* @__PURE__ */ jsx7("source", { src: originalInfo.url, type: media.imageType })
1134
+ }
1135
+ )
1136
+ }
1137
+ ),
1138
+ children: ({ ref, open }) => /* @__PURE__ */ jsxs3("div", { onClick: isPostDetail ? open : handleClick, children: [
1139
+ /* @__PURE__ */ jsx7(
1140
+ "img",
1141
+ {
1142
+ src: thumbnailInfo.url,
1143
+ ref,
1144
+ alt: alt ?? "",
1145
+ className: className + " media-item ",
1146
+ style: {
1147
+ aspectRatio,
1148
+ display
1149
+ },
1150
+ onClick: isPostDetail ? open : handleClick
1151
+ }
1152
+ ),
1153
+ /* @__PURE__ */ jsx7(
1154
+ AppIcon,
1155
+ {
1156
+ name: "video",
1157
+ className: `absolute bottom-2 left-2 text-background`,
1158
+ onClick: isPostDetail ? open : handleClick,
1159
+ ref,
1160
+ style: {
1161
+ aspectRatio,
1162
+ display
1163
+ }
1164
+ }
1165
+ ),
1166
+ /* @__PURE__ */ jsx7(
1167
+ AppIcon,
1168
+ {
1169
+ name: "circlePlay",
1170
+ className: `size-10 bg-foreground/20 hover:bg-foreground/40 transition-colors duration-300 rounded-full absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-3xl text-background`,
1171
+ onClick: isPostDetail ? open : handleClick,
1172
+ ref,
1173
+ style: {
1174
+ aspectRatio,
1175
+ display
1176
+ }
1177
+ }
1178
+ )
1179
+ ] })
1180
+ },
1181
+ media.key
1182
+ );
1183
+ }
1184
+ return /* @__PURE__ */ jsx7(
1185
+ Item,
1186
+ {
1187
+ width: originalInfo.width,
1188
+ height: originalInfo.height,
1189
+ original: originalInfo.url,
1190
+ thumbnail: thumbnailInfo.url,
1191
+ children: ({ open, ref }) => /* @__PURE__ */ jsx7(
1192
+ "img",
1193
+ {
1194
+ onClick: isPostDetail ? open : handleClick,
1195
+ ref,
1196
+ src: originalInfo.url || thumbnailInfo.url,
1197
+ alt: alt ?? "",
1198
+ className,
1199
+ style: {
1200
+ aspectRatio,
1201
+ display
1202
+ }
1203
+ }
1204
+ )
1205
+ },
1206
+ media.key
1207
+ );
1208
+ },
1209
+ [className, handleClick, isPostDetail, item.key, ratio, src]
1210
+ );
1211
+ const handleMediaItems = () => {
1212
+ return /* @__PURE__ */ jsx7(Gallery, { options: { showHideAnimationType: "fade" }, children: data?.map((media) => renderMediaItem(media)) });
1213
+ };
1214
+ return /* @__PURE__ */ jsxs3(
1215
+ "div",
1216
+ {
1217
+ style: {
1218
+ backgroundImage: `url(${placeholderUrl})`
1219
+ },
1220
+ className: "h-full w-full bg-cover bg-center bg-no-repeat",
1221
+ children: [
1222
+ item.type === postItemType.MEDIA_ITEM_TO_UPLOAD && showUploadingMedia(),
1223
+ item.type === postItemType.IMAGE_ITEM_TO_UPLOAD && showUploadImg(item),
1224
+ item.type !== postItemType.IMAGE_ITEM_TO_UPLOAD && item.type !== postItemType.MEDIA_ITEM_TO_UPLOAD && handleMediaItems()
1225
+ ]
1226
+ }
1227
+ );
1228
+ };
1229
+ var AsyncImage_default = AsyncImage;
1230
+
1231
+ // src/components/MediaView.tsx
1232
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
1233
+ var MediaView = ({
1234
+ item,
1235
+ className,
1236
+ handleLightbox,
1237
+ additionalImagesCount,
1238
+ ratio,
1239
+ data,
1240
+ isPostDetail,
1241
+ onCardPress
1242
+ }) => {
1243
+ const mediaSrc = useMemo5(() => {
1244
+ if (item.type === postItemType.IMAGE_ITEM_TO_UPLOAD || item.type === postItemType.MEDIA_ITEM_TO_UPLOAD) {
1245
+ return URL.createObjectURL(item.file);
1246
+ }
1247
+ return item.sources?.original?.url ?? "";
1248
+ }, [item.file, item.sources?.original?.url, item.type]);
1249
+ if (item.type === postItemType.POLL_TEXT_ITEM) {
1250
+ const textViewClass = `${className} aspect-square`;
1251
+ return /* @__PURE__ */ jsx8(
1252
+ "div",
1253
+ {
1254
+ onClick: handleLightbox,
1255
+ className: textViewClass,
1256
+ style: {
1257
+ backgroundColor: item.option_color
1258
+ },
1259
+ children: /* @__PURE__ */ jsx8("div", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx8("p", { className: "text-center text-lg font-bold text-background", children: item.text }) })
1260
+ },
1261
+ item.key
1262
+ );
1263
+ }
1264
+ if (item.type === postItemType.IMAGE_ITEM || item.type === postItemType.POLL_IMAGE_ITEM || item.type === postItemType.GIF_ITEM || item.type === postItemType.IMAGE_ITEM_TO_UPLOAD || item.type === postItemType.MEDIA_ITEM || item.type === postItemType.MEDIA_ITEM_TO_UPLOAD || item.type === postItemType.POLL_MEDIA_ITEM) {
1265
+ return /* @__PURE__ */ jsxs4("div", { onClick: handleLightbox, className, children: [
1266
+ item.type.includes("ToUpload") && /* @__PURE__ */ jsx8(Loader_default, { type: "upload_loader" }),
1267
+ /* @__PURE__ */ jsx8(
1268
+ AsyncImage_default,
1269
+ {
1270
+ alt: mediaSrc,
1271
+ src: mediaSrc,
1272
+ className,
1273
+ ratio,
1274
+ item,
1275
+ data: data || [],
1276
+ isPostDetail,
1277
+ onCardPress
1278
+ }
1279
+ ),
1280
+ additionalImagesCount && /* @__PURE__ */ jsx8("div", { className: "absolute right-1 top-0", children: /* @__PURE__ */ jsx8("p", { className: "text-4xl font-bold text-muted-foreground", children: `+${additionalImagesCount}` }) })
1281
+ ] }, item.key);
1282
+ }
1283
+ return null;
1284
+ };
1285
+ var MediaView_default = MediaView;
1286
+
1287
+ // src/components/PostPollLayout/pollUtils.ts
1288
+ var getOptionRateV2 = (item, voteResult) => {
1289
+ if (!voteResult) return "0%";
1290
+ const voteItem = voteResult.options.find(
1291
+ (option) => option.option_id === item.option_id
1292
+ );
1293
+ const totalCount = voteResult.options.reduce(
1294
+ (total, option) => total + option.count,
1295
+ 0
1296
+ );
1297
+ if (voteItem && totalCount > 0) {
1298
+ return (voteItem.count / totalCount * 100).toFixed(0) + "%";
1299
+ }
1300
+ return "0%";
1301
+ };
1302
+ var getVoteInfo = (item, voteResult) => {
1303
+ const isAppOwnerVoted = !!voteResult?.user_vote_option_id;
1304
+ const isVoteItem = !!voteResult?.options.find(
1305
+ (option) => option.option_id === item.option_id && option.option_id === voteResult.user_vote_option_id
1306
+ );
1307
+ return {
1308
+ rate: getOptionRateV2(item, voteResult),
1309
+ isAppOwnerVoted,
1310
+ isVoteItem
1311
+ };
1312
+ };
1313
+
1314
+ // src/components/PostPollLayout/PostPollIResultView.tsx
1315
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1316
+ var PostPollIResultView = ({
1317
+ type,
1318
+ item,
1319
+ voteResult,
1320
+ isPostOwner
1321
+ }) => {
1322
+ const voteInfo = getVoteInfo(item, voteResult);
1323
+ const textCss = voteInfo.isVoteItem ? "#FFFFFF" : item.option_color;
1324
+ const backgroundCss = voteInfo.isVoteItem ? item.option_color : "#FFFFFF";
1325
+ const totalVotes = voteResult?.options.reduce((sum, o) => sum + o.count, 0) ?? 0;
1326
+ if (!voteInfo.isAppOwnerVoted && !isPostOwner || totalVotes === 0) {
1327
+ return null;
1328
+ }
1329
+ if (type === "SINGLE_LEFT") {
1330
+ return /* @__PURE__ */ jsxs5(
1331
+ "div",
1332
+ {
1333
+ className: "absolute top-0 m-6 flex flex-row items-center rounded-full px-4 py-2 shadow-sm backdrop-blur-md",
1334
+ style: { backgroundColor: backgroundCss },
1335
+ children: [
1336
+ /* @__PURE__ */ jsx9("p", { className: "text-sm font-bold", style: { color: textCss }, children: voteInfo.rate }),
1337
+ /* @__PURE__ */ jsx9(
1338
+ AppIcon,
1339
+ {
1340
+ name: "thumbUp",
1341
+ size: 16,
1342
+ className: "ml-2",
1343
+ style: { color: textCss }
1344
+ }
1345
+ )
1346
+ ]
1347
+ }
1348
+ );
1349
+ }
1350
+ if (type === "SINGLE_RIGHT") {
1351
+ return /* @__PURE__ */ jsxs5(
1352
+ "div",
1353
+ {
1354
+ className: "absolute right-0 top-0 m-6 flex flex-row items-center rounded-full px-4 py-2 shadow-sm backdrop-blur-md",
1355
+ style: { backgroundColor: backgroundCss },
1356
+ children: [
1357
+ /* @__PURE__ */ jsx9("p", { className: "text-sm font-bold", style: { color: textCss }, children: voteInfo.rate }),
1358
+ /* @__PURE__ */ jsx9(
1359
+ AppIcon,
1360
+ {
1361
+ name: "thumbDown",
1362
+ size: 16,
1363
+ className: "ml-2",
1364
+ style: { color: textCss }
1365
+ }
1366
+ )
1367
+ ]
1368
+ }
1369
+ );
1370
+ }
1371
+ return /* @__PURE__ */ jsx9(
1372
+ "div",
1373
+ {
1374
+ className: "absolute top-0 m-3 flex items-center justify-center rounded-full px-4 py-2 shadow-sm backdrop-blur-md",
1375
+ style: { backgroundColor: backgroundCss },
1376
+ children: /* @__PURE__ */ jsx9("p", { className: "text-sm font-bold", style: { color: textCss }, children: voteInfo.rate })
1377
+ }
1378
+ );
1379
+ };
1380
+ var PostPollIResultView_default = PostPollIResultView;
1381
+
1382
+ // src/components/PostPollLayout/PostPollIButtonView.tsx
1383
+ import { jsx as jsx10 } from "react/jsx-runtime";
1384
+ var PostPollIButtonView = ({
1385
+ type,
1386
+ item,
1387
+ voteResult,
1388
+ onVotePress
1389
+ }) => {
1390
+ const voteInfo = getVoteInfo(item, voteResult);
1391
+ const handleVoting = (e) => {
1392
+ e.stopPropagation();
1393
+ if (!voteInfo.isVoteItem) onVotePress(item);
1394
+ };
1395
+ const voteLabel = voteInfo.isVoteItem ? `Voted: ${item.text || "this option"}` : `Vote for ${item.text || "this option"}`;
1396
+ if (type === "SINGLE_LEFT") {
1397
+ const nonVoteClassName2 = "absolute bottom-0 left-0 m-6 flex size-11 items-center justify-center rounded-full bg-black/30 backdrop-blur-sm border border-background/20 cursor-pointer transition-all duration-200 hover:bg-black/50";
1398
+ const voteClassName2 = "absolute bottom-0 left-0 m-6 flex size-11 items-center justify-center rounded-full bg-background shadow-lg cursor-pointer transition-all duration-200";
1399
+ return /* @__PURE__ */ jsx10(
1400
+ "button",
1401
+ {
1402
+ className: voteInfo.isVoteItem ? voteClassName2 : nonVoteClassName2,
1403
+ onClick: handleVoting,
1404
+ "aria-label": voteLabel,
1405
+ "aria-pressed": voteInfo.isVoteItem,
1406
+ children: /* @__PURE__ */ jsx10(
1407
+ AppIcon,
1408
+ {
1409
+ name: "thumbUp",
1410
+ size: 20,
1411
+ "aria-hidden": "true",
1412
+ className: voteInfo.isVoteItem ? "" : "text-white",
1413
+ style: voteInfo.isVoteItem ? { color: item.option_color } : {}
1414
+ }
1415
+ )
1416
+ }
1417
+ );
1418
+ }
1419
+ if (type === "SINGLE_RIGHT") {
1420
+ const nonVoteClassName2 = "absolute bottom-0 right-0 m-6 flex size-11 items-center justify-center rounded-full bg-black/30 backdrop-blur-sm border border-background/20 cursor-pointer transition-all duration-200 hover:bg-black/50";
1421
+ const voteClassName2 = "absolute bottom-0 right-0 m-6 flex size-11 items-center justify-center rounded-full bg-background shadow-lg cursor-pointer transition-all duration-200";
1422
+ return /* @__PURE__ */ jsx10(
1423
+ "button",
1424
+ {
1425
+ className: voteInfo.isVoteItem ? voteClassName2 : nonVoteClassName2,
1426
+ onClick: handleVoting,
1427
+ "aria-label": voteLabel,
1428
+ "aria-pressed": voteInfo.isVoteItem,
1429
+ children: /* @__PURE__ */ jsx10(
1430
+ AppIcon,
1431
+ {
1432
+ name: "thumbDown",
1433
+ size: 20,
1434
+ "aria-hidden": "true",
1435
+ className: voteInfo.isVoteItem ? "" : "text-white",
1436
+ style: voteInfo.isVoteItem ? { color: item.option_color } : {}
1437
+ }
1438
+ )
1439
+ }
1440
+ );
1441
+ }
1442
+ const nonVoteClassName = "absolute bottom-3 right-3 rounded-full p-1 backdrop-blur-md border border-background/40 bg-background/20 hover:bg-background/30 scale-100 transition-all duration-300 z-20 cursor-pointer";
1443
+ const voteClassName = "absolute bottom-3 right-3 rounded-full p-1 backdrop-blur-md border border-action-primary bg-action-primary scale-110 transition-all duration-300 z-20 cursor-pointer";
1444
+ return /* @__PURE__ */ jsx10(
1445
+ "button",
1446
+ {
1447
+ className: voteInfo.isVoteItem ? voteClassName : nonVoteClassName,
1448
+ style: voteInfo.isVoteItem && item.option_color ? {
1449
+ backgroundColor: item.option_color,
1450
+ borderColor: item.option_color
1451
+ } : void 0,
1452
+ onClick: handleVoting,
1453
+ "aria-label": voteLabel,
1454
+ "aria-pressed": voteInfo.isVoteItem,
1455
+ children: /* @__PURE__ */ jsx10(
1456
+ AppIcon,
1457
+ {
1458
+ name: "checkCircle2",
1459
+ "aria-hidden": "true",
1460
+ className: `size-8 ${voteInfo.isVoteItem ? "text-white" : "text-white/90 drop-shadow-md"}`
1461
+ }
1462
+ )
1463
+ }
1464
+ );
1465
+ };
1466
+ var PostPollIButtonView_default = PostPollIButtonView;
1467
+
1468
+ // src/components/PostPollLayout/PostPollLayoutOne.tsx
1469
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
1470
+ var PostPollLayoutOne = ({
1471
+ items,
1472
+ voteResult,
1473
+ onVotePress,
1474
+ onCardPress,
1475
+ isPostDetail,
1476
+ isInboxCard,
1477
+ isPostOwner
1478
+ }) => {
1479
+ const { sources } = items[0];
1480
+ let ratio = 1;
1481
+ if (!isInboxCard && sources && sources.original) {
1482
+ const { width, height } = sources.original;
1483
+ if (width > 0 && height > 0) {
1484
+ ratio = width / height;
1485
+ if (ratio < 0.7) {
1486
+ ratio = 0.7;
1487
+ }
1488
+ }
1489
+ }
1490
+ const className = "object-cover w-full";
1491
+ return /* @__PURE__ */ jsx11("div", { className: "not-prose relative w-full box-border", children: /* @__PURE__ */ jsxs6("div", { className: "relative w-full bg-gray-100", children: [
1492
+ /* @__PURE__ */ jsx11(
1493
+ MediaView_default,
1494
+ {
1495
+ item: items[0],
1496
+ className,
1497
+ data: [items[0]],
1498
+ ratio,
1499
+ onCardPress,
1500
+ isPostDetail
1501
+ }
1502
+ ),
1503
+ /* @__PURE__ */ jsx11(
1504
+ PostPollIResultView_default,
1505
+ {
1506
+ type: "SINGLE_LEFT",
1507
+ item: items[0],
1508
+ index: 0,
1509
+ voteResult,
1510
+ isPostOwner
1511
+ }
1512
+ ),
1513
+ /* @__PURE__ */ jsx11(
1514
+ PostPollIButtonView_default,
1515
+ {
1516
+ type: "SINGLE_LEFT",
1517
+ item: items[0],
1518
+ voteResult,
1519
+ onVotePress
1520
+ }
1521
+ ),
1522
+ /* @__PURE__ */ jsx11(
1523
+ PostPollIResultView_default,
1524
+ {
1525
+ type: "SINGLE_RIGHT",
1526
+ item: items[1],
1527
+ index: 1,
1528
+ voteResult,
1529
+ isPostOwner
1530
+ }
1531
+ ),
1532
+ /* @__PURE__ */ jsx11(
1533
+ PostPollIButtonView_default,
1534
+ {
1535
+ type: "SINGLE_RIGHT",
1536
+ item: items[1],
1537
+ voteResult,
1538
+ onVotePress
1539
+ }
1540
+ )
1541
+ ] }) });
1542
+ };
1543
+ var PostPollLayoutOne_default = PostPollLayoutOne;
1544
+
1545
+ // src/components/PostPollLayout/PostPollLayoutGrid.tsx
1546
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1547
+ var GRID_CLASS = {
1548
+ 2: "grid grid-flow-col grid-cols-2 grid-rows-1 gap-0.5 bg-gray-100",
1549
+ 3: "grid grid-cols-2 grid-rows-1 gap-0.5 bg-gray-100",
1550
+ 4: "grid grid-flow-row grid-cols-2 grid-rows-2 gap-0.5 bg-gray-100"
1551
+ };
1552
+ var PostPollLayoutGrid = ({
1553
+ items,
1554
+ voteResult,
1555
+ onVotePress,
1556
+ onCardPress,
1557
+ isPostDetail,
1558
+ isPostOwner
1559
+ }) => {
1560
+ const gridClass = GRID_CLASS[items.length] ?? GRID_CLASS[4];
1561
+ return /* @__PURE__ */ jsx12("div", { className: "not-prose w-full box-border", children: /* @__PURE__ */ jsx12("div", { className: gridClass, children: items.map((item, index) => /* @__PURE__ */ jsxs7("div", { className: "relative group", children: [
1562
+ /* @__PURE__ */ jsx12(
1563
+ MediaView_default,
1564
+ {
1565
+ item,
1566
+ className: "aspect-[1] col-span-1 object-cover w-full",
1567
+ data: items,
1568
+ onCardPress,
1569
+ isPostDetail
1570
+ }
1571
+ ),
1572
+ /* @__PURE__ */ jsx12(
1573
+ PostPollIResultView_default,
1574
+ {
1575
+ type: "NORMAL",
1576
+ item,
1577
+ index,
1578
+ voteResult,
1579
+ isPostOwner
1580
+ }
1581
+ ),
1582
+ /* @__PURE__ */ jsx12(
1583
+ PostPollIButtonView_default,
1584
+ {
1585
+ type: "NORMAL",
1586
+ item,
1587
+ voteResult,
1588
+ onVotePress
1589
+ }
1590
+ )
1591
+ ] }, item.option_id || index)) }) });
1592
+ };
1593
+ var PostPollLayoutGrid_default = PostPollLayoutGrid;
1594
+
1595
+ // src/components/PostPollView.tsx
1596
+ import { jsx as jsx13 } from "react/jsx-runtime";
1597
+ var PostPollView = ({
1598
+ items,
1599
+ voteResult,
1600
+ onVotePress,
1601
+ onCardPress,
1602
+ isPostDetail,
1603
+ isInboxCard,
1604
+ isPostOwner
1605
+ }) => {
1606
+ const isSingleChoicePoll = items.length === 2 && items[0].key === items[1].key;
1607
+ if (isSingleChoicePoll) {
1608
+ return /* @__PURE__ */ jsx13(
1609
+ PostPollLayoutOne_default,
1610
+ {
1611
+ isInboxCard,
1612
+ onVotePress,
1613
+ items,
1614
+ voteResult,
1615
+ onCardPress,
1616
+ isPostDetail,
1617
+ isPostOwner
1618
+ }
1619
+ );
1620
+ }
1621
+ if (items.length >= 2 && items.length <= 4) {
1622
+ return /* @__PURE__ */ jsx13(
1623
+ PostPollLayoutGrid_default,
1624
+ {
1625
+ onVotePress,
1626
+ items,
1627
+ voteResult,
1628
+ onCardPress,
1629
+ isPostDetail,
1630
+ isPostOwner
1631
+ }
1632
+ );
1633
+ }
1634
+ return null;
1635
+ };
1636
+ var PostPollView_default = PostPollView;
1637
+
1638
+ // src/components/PostTextItem.tsx
1639
+ import { jsx as jsx14 } from "react/jsx-runtime";
1640
+ var PostTextItem = ({ textItems, showFullText }) => {
1641
+ return /* @__PURE__ */ jsx14("div", { className: "flex-1", children: /* @__PURE__ */ jsx14("div", { children: textItems.map((item, index) => {
1642
+ const text = "text" in item ? item.text ?? "" : "";
1643
+ const key = "key" in item ? item.key : index;
1644
+ const mentionData = "mentionData" in item ? item.mentionData : void 0;
1645
+ return /* @__PURE__ */ jsx14(
1646
+ AutoLink_default,
1647
+ {
1648
+ text: showFullText ? text : TruncateText(text, 300),
1649
+ mentionData,
1650
+ className: "whitespace-normal px-4 pb-4 leading-relaxed text-gray-800",
1651
+ style: {
1652
+ whiteSpace: "pre-wrap",
1653
+ wordBreak: "break-word"
1654
+ }
1655
+ },
1656
+ key
1657
+ );
1658
+ }) }) });
1659
+ };
1660
+ var PostTextItem_default = PostTextItem;
1661
+
1662
+ // src/components/PostLinkItem.tsx
1663
+ import { useState } from "react";
1664
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
1665
+ var getHostFromUrl = (url) => {
1666
+ if (!url) return "";
1667
+ try {
1668
+ return new URL(url).host;
1669
+ } catch {
1670
+ return url;
1671
+ }
1672
+ };
1673
+ var getLinkRel = (host, supportDeepviewDomain = "") => {
1674
+ const isInternal = supportDeepviewDomain.length > 0 && host.includes(supportDeepviewDomain);
1675
+ return isInternal ? "noreferrer" : "noreferrer nofollow";
1676
+ };
1677
+ var PostLinkItem = ({
1678
+ linkItems,
1679
+ imageComponent: ImageComponent,
1680
+ onCloseClick,
1681
+ type = "display",
1682
+ supportDeepviewDomain = ""
1683
+ }) => {
1684
+ const [imageError, setImageError] = useState(false);
1685
+ if (linkItems.length === 0) {
1686
+ return null;
1687
+ }
1688
+ const { linkUrl, linkTitle, linkDesc, linkImage } = linkItems[0] || {};
1689
+ const host = getHostFromUrl(linkUrl);
1690
+ const linkRel = getLinkRel(host, supportDeepviewDomain);
1691
+ const handleClick = (e) => {
1692
+ e.stopPropagation();
1693
+ e.nativeEvent.stopImmediatePropagation();
1694
+ if (linkUrl) window.open(linkUrl, "_blank", "noopener,noreferrer");
1695
+ };
1696
+ const renderImage = (src, alt, aspectClassName, containerClassName) => {
1697
+ if (imageError || !src) return null;
1698
+ return /* @__PURE__ */ jsx15("div", { className: containerClassName, children: /* @__PURE__ */ jsx15("div", { className: aspectClassName, children: ImageComponent ? /* @__PURE__ */ jsx15(
1699
+ ImageComponent,
1700
+ {
1701
+ src,
1702
+ alt,
1703
+ className: "w-full h-full object-cover rounded-t-md"
1704
+ }
1705
+ ) : /* @__PURE__ */ jsx15(
1706
+ "img",
1707
+ {
1708
+ src,
1709
+ alt,
1710
+ className: "w-full h-full object-cover rounded-t-md",
1711
+ onError: () => setImageError(true)
1712
+ }
1713
+ ) }) });
1714
+ };
1715
+ if (type === "preview") {
1716
+ const aspectRatio = linkImage && linkImage.height > linkImage.width && linkImage.height / linkImage.width > 0.7 ? "aspect-[0.7]" : "aspect-video";
1717
+ return /* @__PURE__ */ jsxs8(
1718
+ "section",
1719
+ {
1720
+ className: "not-prose min-w-96 relative cursor-pointer rounded-lg border border-border bg-background",
1721
+ onClick: handleClick,
1722
+ role: "link",
1723
+ tabIndex: 0,
1724
+ onKeyDown: (e) => {
1725
+ if (e.key === "Enter" || e.key === " ") {
1726
+ e.preventDefault();
1727
+ handleClick(e);
1728
+ }
1729
+ },
1730
+ children: [
1731
+ linkImage?.url && !imageError && /* @__PURE__ */ jsx15("div", { className: "relative w-full", children: ImageComponent ? /* @__PURE__ */ jsx15("div", { className: `relative w-full ${aspectRatio}`, children: /* @__PURE__ */ jsx15(
1732
+ ImageComponent,
1733
+ {
1734
+ src: linkImage.url,
1735
+ alt: linkTitle || "Link preview image",
1736
+ className: "w-full h-full object-cover rounded-t-md"
1737
+ }
1738
+ ) }) : /* @__PURE__ */ jsx15("div", { className: `relative w-full ${aspectRatio}`, children: /* @__PURE__ */ jsx15(
1739
+ "img",
1740
+ {
1741
+ src: linkImage.url,
1742
+ alt: linkTitle || "Link preview image",
1743
+ className: "w-full h-full object-cover rounded-t-md",
1744
+ onError: () => setImageError(true)
1745
+ }
1746
+ ) }) }),
1747
+ typeof onCloseClick === "function" && /* @__PURE__ */ jsx15(
1748
+ AppIcon,
1749
+ {
1750
+ name: "cancel",
1751
+ onClick: (e) => {
1752
+ e.stopPropagation();
1753
+ onCloseClick();
1754
+ },
1755
+ className: "absolute -right-2 -top-3 rounded-full bg-background size-4.5 hover:cursor-pointer hover:opacity-70"
1756
+ }
1757
+ ),
1758
+ /* @__PURE__ */ jsxs8("section", { className: "flex flex-col gap-1 p-3", children: [
1759
+ /* @__PURE__ */ jsx15("a", { href: linkUrl, className: "hidden", rel: linkRel, tabIndex: -1 }),
1760
+ /* @__PURE__ */ jsx15(
1761
+ "p",
1762
+ {
1763
+ className: "wrap-break-word text-xs font-normal leading-4 text-muted-foreground",
1764
+ "data-test-id": "post-link-host",
1765
+ children: host
1766
+ }
1767
+ ),
1768
+ /* @__PURE__ */ jsx15(
1769
+ "p",
1770
+ {
1771
+ className: "text-clip-1 text-ellipsis wrap-break-word text-base leading-6",
1772
+ "data-test-id": "post-link-title",
1773
+ children: linkTitle
1774
+ }
1775
+ ),
1776
+ /* @__PURE__ */ jsx15(
1777
+ "p",
1778
+ {
1779
+ className: "text-clip-2 text-ellipsis wrap-break-word text-xs font-normal leading-5 tracking-tight text-muted-foreground",
1780
+ "data-test-id": "post-link-desc",
1781
+ children: linkDesc
1782
+ }
1783
+ )
1784
+ ] })
1785
+ ]
1786
+ }
1787
+ );
1788
+ }
1789
+ return /* @__PURE__ */ jsx15(
1790
+ "section",
1791
+ {
1792
+ className: cn("not-prose relative cursor-pointer group overflow-hidden"),
1793
+ onClick: handleClick,
1794
+ role: "link",
1795
+ tabIndex: 0,
1796
+ onKeyDown: (e) => {
1797
+ if (e.key === "Enter" || e.key === " ") {
1798
+ e.preventDefault();
1799
+ handleClick(e);
1800
+ }
1801
+ },
1802
+ children: /* @__PURE__ */ jsxs8("div", { className: "aspect-video relative", children: [
1803
+ /* @__PURE__ */ jsx15("div", { className: "max-w-200 max-h-100 size-full relative", children: linkImage?.url && !imageError && /* @__PURE__ */ jsx15(Fragment2, { children: ImageComponent ? /* @__PURE__ */ jsx15(
1804
+ ImageComponent,
1805
+ {
1806
+ src: linkImage.url,
1807
+ alt: linkTitle || "Link preview image",
1808
+ className: "w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
1809
+ }
1810
+ ) : /* @__PURE__ */ jsx15(
1811
+ "img",
1812
+ {
1813
+ src: linkImage.url,
1814
+ alt: linkTitle || "Link preview image",
1815
+ className: "w-full h-full object-cover transition-transform duration-700 group-hover:scale-105",
1816
+ onError: () => setImageError(true)
1817
+ }
1818
+ ) }) }),
1819
+ /* @__PURE__ */ jsx15("div", { className: "absolute inset-0 bg-linear-to-t from-black/90 via-black/40 to-transparent opacity-90 group-hover:opacity-100 transition-opacity" }),
1820
+ typeof onCloseClick === "function" && /* @__PURE__ */ jsx15(
1821
+ "button",
1822
+ {
1823
+ onClick: (e) => {
1824
+ e.stopPropagation();
1825
+ onCloseClick();
1826
+ },
1827
+ className: "absolute top-2 right-2 bg-black/40 text-white rounded-full p-1 hover:bg-black/60 cursor-pointer border-none flex items-center justify-center transition-colors shadow-sm z-10",
1828
+ "aria-label": "Remove link preview",
1829
+ children: /* @__PURE__ */ jsx15(AppIcon, { name: "cancel", size: 16, strokeWidth: 2 })
1830
+ }
1831
+ ),
1832
+ /* @__PURE__ */ jsxs8("div", { className: "absolute bottom-0 left-0 right-0 p-5 transform transition-transform duration-300 z-10 pointer-events-none", children: [
1833
+ /* @__PURE__ */ jsx15(
1834
+ "a",
1835
+ {
1836
+ href: linkUrl,
1837
+ className: "hidden pointer-events-auto",
1838
+ rel: linkRel,
1839
+ tabIndex: -1
1840
+ }
1841
+ ),
1842
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 text-xs text-white/70 mb-2", children: [
1843
+ /* @__PURE__ */ jsx15(AppIcon, { name: "externalLink", className: "w-3.5 h-3.5" }),
1844
+ /* @__PURE__ */ jsx15("span", { className: "uppercase tracking-wider font-medium", children: host })
1845
+ ] }),
1846
+ /* @__PURE__ */ jsx15("h3", { className: "font-semibold text-white text-xl leading-tight mb-1.5 group-hover:text-action-primary transition-colors line-clamp-2", children: linkTitle }),
1847
+ linkDesc && /* @__PURE__ */ jsx15("p", { className: "text-white/70 text-sm line-clamp-2", children: linkDesc })
1848
+ ] })
1849
+ ] })
1850
+ }
1851
+ );
1852
+ };
1853
+ var PostLinkItem_default = PostLinkItem;
1854
+
1855
+ // src/components/PostEmbedView.tsx
1856
+ import { useMemo as useMemo6, useState as useState2 } from "react";
1857
+ import { jsx as jsx16 } from "react/jsx-runtime";
1858
+ var TRUSTED_HOSTS = {
1859
+ youtube: ["www.youtube.com", "youtube.com"],
1860
+ spotify: ["open.spotify.com"]
1861
+ };
1862
+ var isTrustedEmbedUrl = (provider, url) => {
1863
+ try {
1864
+ const u = new URL(url);
1865
+ if (u.protocol !== "https:") return false;
1866
+ const allowed = TRUSTED_HOSTS[provider] ?? [];
1867
+ return allowed.includes(u.hostname.toLowerCase());
1868
+ } catch {
1869
+ return false;
1870
+ }
1871
+ };
1872
+ var PostEmbedView = ({ embed }) => {
1873
+ const [hasError, setHasError] = useState2(false);
1874
+ const canRender = useMemo6(() => {
1875
+ return !hasError && !!embed.embedUrl && isTrustedEmbedUrl(embed.provider, embed.embedUrl);
1876
+ }, [hasError, embed.embedUrl, embed.provider]);
1877
+ const linkItem = {
1878
+ linkUrl: embed.originalUrl,
1879
+ linkTitle: embed.preview?.title || embed.originalUrl,
1880
+ linkDesc: embed.preview?.description || "",
1881
+ linkImage: embed.preview?.image ?? void 0,
1882
+ linkIcon: "",
1883
+ type: "linkItem"
1884
+ };
1885
+ if (!canRender) return /* @__PURE__ */ jsx16(PostLinkItem_default, { linkItems: [linkItem] });
1886
+ const isYouTube = embed.provider === "youtube";
1887
+ const isSpotify = embed.provider === "spotify";
1888
+ return /* @__PURE__ */ jsx16("div", { className: "w-full px-4 pb-4 not-first:pt-4", children: /* @__PURE__ */ jsx16(
1889
+ "div",
1890
+ {
1891
+ className: "relative w-full overflow-hidden rounded-xl",
1892
+ style: { height: embed.height || SPOTIFY_HEIGHT },
1893
+ children: /* @__PURE__ */ jsx16(
1894
+ "iframe",
1895
+ {
1896
+ src: embed.embedUrl,
1897
+ className: "absolute inset-0 h-full w-full",
1898
+ loading: "lazy",
1899
+ sandbox: "allow-scripts allow-same-origin allow-presentation",
1900
+ allow: isYouTube ? "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" : isSpotify ? "autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" : "",
1901
+ allowFullScreen: isYouTube,
1902
+ referrerPolicy: "no-referrer-when-downgrade",
1903
+ title: embed.preview?.title || `${embed.provider} embed`,
1904
+ style: { border: 0 },
1905
+ onError: () => setHasError(true)
1906
+ }
1907
+ )
1908
+ }
1909
+ ) });
1910
+ };
1911
+ var PostEmbedView_default = PostEmbedView;
1912
+
1913
+ // src/components/PostMediaLayout/MediaUploadStatusOverlay.tsx
1914
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
1915
+ var MediaUploadStatusOverlay = ({ status, tileWidth }) => {
1916
+ if (!status) return null;
1917
+ const showText = !tileWidth || tileWidth >= 100;
1918
+ if (status === "uploading" || status === "processing") {
1919
+ return /* @__PURE__ */ jsxs9("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2 bg-overlay-scrim", children: [
1920
+ /* @__PURE__ */ jsx17(AppIcon, { name: "loader", className: "size-6 animate-spin text-background" }),
1921
+ showText && /* @__PURE__ */ jsx17("span", { className: "text-xs font-medium text-background", children: status === "uploading" ? "Uploading" : "Processing" })
1922
+ ] });
1923
+ }
1924
+ if (status === "uploaded") {
1925
+ return /* @__PURE__ */ jsx17("div", { className: "absolute inset-0 flex items-center justify-center bg-feedback-success/15", children: /* @__PURE__ */ jsx17("div", { className: "flex size-8 items-center justify-center rounded-full bg-foreground/60", children: /* @__PURE__ */ jsx17(AppIcon, { name: "check", className: "size-5 text-background" }) }) });
1926
+ }
1927
+ return /* @__PURE__ */ jsx17("div", { className: "absolute inset-0 flex items-center justify-center bg-feedback-danger/20", children: /* @__PURE__ */ jsx17("div", { className: "flex size-8 items-center justify-center rounded-full bg-foreground/60", children: /* @__PURE__ */ jsx17("span", { className: "text-base font-bold text-background", children: "!" }) }) });
1928
+ };
1929
+ var MediaUploadStatusOverlay_default = MediaUploadStatusOverlay;
1930
+
1931
+ // src/components/PostMediaLayout/PostMediaLayoutCore.tsx
1932
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
1933
+ var gridLayoutData = {
1934
+ 1: {
1935
+ gridColsClass: "grid-cols-1",
1936
+ gridSpan: [1],
1937
+ colSpanClasses: ["col-span-1"],
1938
+ itemRatio: [1]
1939
+ },
1940
+ 2: {
1941
+ gridColsClass: "grid-cols-2",
1942
+ gridSpan: [1, 1],
1943
+ colSpanClasses: ["col-span-1", "col-span-1"],
1944
+ itemRatio: [1, 1]
1945
+ },
1946
+ 3: {
1947
+ gridColsClass: "grid-cols-2",
1948
+ gridSpan: [2, 1, 1],
1949
+ colSpanClasses: ["col-span-2", "col-span-1", "col-span-1"],
1950
+ itemRatio: [1.5, 1.5, 1.5]
1951
+ },
1952
+ 4: {
1953
+ gridColsClass: "grid-cols-2",
1954
+ gridSpan: [1, 1, 1, 1],
1955
+ colSpanClasses: ["col-span-1", "col-span-1", "col-span-1", "col-span-1"],
1956
+ itemRatio: [1, 1, 1, 1]
1957
+ },
1958
+ 5: {
1959
+ gridColsClass: "grid-cols-6",
1960
+ gridSpan: [3, 3, 2, 2, 2],
1961
+ colSpanClasses: [
1962
+ "col-span-3",
1963
+ "col-span-3",
1964
+ "col-span-2",
1965
+ "col-span-2",
1966
+ "col-span-2"
1967
+ ],
1968
+ itemRatio: [1.5, 1.5, 1, 1, 1]
1969
+ }
1970
+ };
1971
+ var PostMediaLayoutCore = ({
1972
+ items,
1973
+ parentId: _parentId,
1974
+ isPostDetail,
1975
+ onCardPress,
1976
+ mediaItemStatuses
1977
+ }) => {
1978
+ if (!items?.length) {
1979
+ return null;
1980
+ }
1981
+ const slicedData = items.slice(0, 5);
1982
+ const { gridColsClass, colSpanClasses, itemRatio } = gridLayoutData[slicedData.length];
1983
+ return /* @__PURE__ */ jsx18("div", { className: "not-prose w-full box-border", children: /* @__PURE__ */ jsx18(
1984
+ "div",
1985
+ {
1986
+ className: `grid ${gridColsClass} cursor-pointer grid-rows-1 gap-0.5 bg-muted`,
1987
+ children: slicedData.map((slicedItem, index) => {
1988
+ let ratio = 1;
1989
+ if (slicedData.length === 1) {
1990
+ const { width, height } = slicedItem.type.includes("ToUpload") ? slicedItem.file : slicedItem.sources?.original ?? {};
1991
+ const safeWidth = width ?? 1;
1992
+ const safeHeight = height ?? 1;
1993
+ ratio = safeWidth / safeHeight;
1994
+ if (ratio < 0.7) {
1995
+ ratio = 0.7;
1996
+ }
1997
+ } else {
1998
+ ratio = itemRatio[index];
1999
+ }
2000
+ const className = `w-full h-full relative ${colSpanClasses[index]} object-cover`;
2001
+ return /* @__PURE__ */ jsxs10("div", { className: `relative ${colSpanClasses[index]}`, children: [
2002
+ /* @__PURE__ */ jsx18(
2003
+ MediaView_default,
2004
+ {
2005
+ item: slicedItem,
2006
+ ratio,
2007
+ className,
2008
+ additionalImagesCount: index === slicedData.length - 1 && items.length > 5 ? items.length - slicedData.length : void 0,
2009
+ data: items,
2010
+ isPostDetail,
2011
+ onCardPress
2012
+ }
2013
+ ),
2014
+ /* @__PURE__ */ jsx18(
2015
+ MediaUploadStatusOverlay_default,
2016
+ {
2017
+ status: mediaItemStatuses?.[slicedItem.key]
2018
+ }
2019
+ )
2020
+ ] }, index);
2021
+ })
2022
+ }
2023
+ ) });
2024
+ };
2025
+ var PostMediaLayoutCore_default = PostMediaLayoutCore;
2026
+
2027
+ // src/components/PostItemsView.tsx
2028
+ import { jsx as jsx19 } from "react/jsx-runtime";
2029
+ var PostItemsView = ({
2030
+ primaryContent,
2031
+ isPostDetail,
2032
+ onCardPress,
2033
+ onVotePress,
2034
+ isPostOwner,
2035
+ parentId,
2036
+ isCommentScope,
2037
+ imageComponent,
2038
+ mediaItemStatuses
2039
+ }) => {
2040
+ if (!primaryContent) return null;
2041
+ switch (primaryContent.type) {
2042
+ case "embed":
2043
+ return /* @__PURE__ */ jsx19(PostEmbedView_default, { embed: primaryContent });
2044
+ case "poll":
2045
+ return /* @__PURE__ */ jsx19(
2046
+ PostPollView_default,
2047
+ {
2048
+ items: primaryContent.items,
2049
+ voteResult: primaryContent.pollResult,
2050
+ onVotePress,
2051
+ onCardPress,
2052
+ isPostDetail,
2053
+ isPostOwner
2054
+ }
2055
+ );
2056
+ case "link":
2057
+ return /* @__PURE__ */ jsx19(
2058
+ PostLinkItem_default,
2059
+ {
2060
+ linkItems: [
2061
+ {
2062
+ linkUrl: primaryContent.linkUrl,
2063
+ linkTitle: primaryContent.title,
2064
+ linkDesc: primaryContent.description,
2065
+ linkImage: primaryContent.image ?? void 0,
2066
+ linkIcon: "",
2067
+ type: "linkItem"
2068
+ }
2069
+ ],
2070
+ imageComponent
2071
+ }
2072
+ );
2073
+ case "media":
2074
+ return /* @__PURE__ */ jsx19(
2075
+ PostMediaLayoutCore_default,
2076
+ {
2077
+ items: primaryContent.items,
2078
+ parentId,
2079
+ isPostDetail,
2080
+ onCardPress,
2081
+ mediaItemStatuses
2082
+ }
2083
+ );
2084
+ case "textItem":
2085
+ return /* @__PURE__ */ jsx19(
2086
+ PostTextItem_default,
2087
+ {
2088
+ textItems: primaryContent.items,
2089
+ showFullText: isPostDetail,
2090
+ isPostDetail
2091
+ }
2092
+ );
2093
+ default:
2094
+ return null;
2095
+ }
2096
+ };
2097
+ var PostItemsView_default = PostItemsView;
2098
+
2099
+ // src/components/PostSectionsView.tsx
2100
+ import { useCallback as useCallback3 } from "react";
2101
+
2102
+ // src/utils/contentTypeConstants.ts
2103
+ var contentTypeRules = {
2104
+ NEW_MESSAGE: "new:message",
2105
+ NEW_POST: "new:post",
2106
+ NEW_COMMENT: "new:comment",
2107
+ EVENT: "event"
2108
+ };
2109
+ var contentTypeConstants = {
2110
+ TO_BE_DELETE: "tobedelete",
2111
+ NEW_GROUP: "new:group",
2112
+ LOAD_MORE_CONTENT: "loadMoreContent",
2113
+ NEW_MESSAGE: `${contentTypeRules.NEW_MESSAGE}:text`,
2114
+ NEW_MESSAGE_GUGGY: `${contentTypeRules.NEW_MESSAGE}:gif`,
2115
+ NEW_MESSAGE_VOICE: `${contentTypeRules.NEW_MESSAGE}:voice`,
2116
+ NEW_POST_IMAGE: `${contentTypeRules.NEW_POST}:image`,
2117
+ NEW_POST_MEDIA: `${contentTypeRules.NEW_POST}:media`,
2118
+ NEW_POST_POLL: `${contentTypeRules.NEW_POST}:poll`,
2119
+ NEW_POST_LINK: `${contentTypeRules.NEW_POST}:link`,
2120
+ NEW_POST_MESSAGE: `${contentTypeRules.NEW_POST}:text`,
2121
+ NEW_POST_THREAD: `${contentTypeRules.NEW_POST}:thread`,
2122
+ NEW_COMMENT: `${contentTypeRules.NEW_COMMENT}`,
2123
+ NEW_COMMENT_GUGGY: `${contentTypeRules.NEW_COMMENT}:gif`,
2124
+ NEW_COMMENT_VOICE: `${contentTypeRules.NEW_COMMENT}:voice`,
2125
+ NEW_COMMENT_IMAGE: `${contentTypeRules.NEW_COMMENT}:image`,
2126
+ NEW_COMMENT_MEDIA: `${contentTypeRules.NEW_COMMENT}:media`,
2127
+ ERASE_MESSAGE: "erase:message",
2128
+ EVENT_POLL_VOTE: "event:poll:vote",
2129
+ EVENT_NEW_GREETING_POST: "event:greeting:post",
2130
+ EVENT_UPDATE_GROUP: "event:update:group",
2131
+ EVENT_ERASE_GROUP_CONTENT: "event:erase:group:content",
2132
+ EVENT_ERASE_GROUP_COMMENT: "event:erase:group:comment",
2133
+ EVENT_NOT_A_CONTACT: "event:not:contact",
2134
+ EVENT_SCREENSHOT: "event:screenshot",
2135
+ EVENT_COMMENT_SCREENSHOT: "event:comment:screenshot",
2136
+ EVENT_TOAST_CONVERSATION: "event:toast:conversation",
2137
+ EVENT_TOAST_POST: "event:toast:post",
2138
+ UPDATE_GROUP_NAME: "group:update:name",
2139
+ UPDATE_GROUP_DESCRIPTION: "group:update:description",
2140
+ UPDATE_GROUP_BACKGROUND: "group:update:background",
2141
+ UPDATE_GROUP_DEFAULT_DEEPLINK: "group:update:default_deeplink",
2142
+ UPDATE_GROUP_AVATAR: "group:update:avatar",
2143
+ UPDATE_GROUP_NEW_USERS: "group:new:users",
2144
+ UPDATE_GROUP_REMOVE_USERS: "group:remove:users",
2145
+ UPDATE_GROUP_USER_LEAVE: "group:remove:users:leave",
2146
+ UPDATE_GROUP_NEW_OWNER: "group:new:owner",
2147
+ UPDATE_GROUP_SELECT_ID: "group:update:select_id",
2148
+ UPDATE_GROUP_SHOW_HISTORY: "group:update:show_history",
2149
+ UPDATE_GROUP_SHARE_TO_PUBLIC: "group:update:share_to_public",
2150
+ UPDATE_GROUP_PERMISSION: "group:update:permission",
2151
+ UPDATE_GROUP_NEW_FEATURED: "group:new:featured",
2152
+ UPDATE_GROUP_FEATURED_IN_GROUP: "group:featured:in:group",
2153
+ UPDATE_GROUP_FEATURED_BY_USER: "group:featured:by:user",
2154
+ UPDATE_RELATIONSHIP: "update:relationship",
2155
+ NEW_GROUP_REQUEST: "group:request:new",
2156
+ NEW_GROUP_REQUEST_ACCEPTED: "group:request:accepted",
2157
+ REMOVE_GROUP_ADMIN: "group:remove:admin",
2158
+ ADD_GROUP_ADMIN: "group:new:admin",
2159
+ UPDATE_CONTENT: "update:content",
2160
+ NEW_GROUP_REQUEST_REMOVE: "group:request:removed",
2161
+ AGGREGATED_MESSAGE_TIME: "aggregated:message:time",
2162
+ REQUEST_IMAGE_DOWNLOAD: "request:image:download"
2163
+ };
2164
+
2165
+ // src/components/PostSectionSourceInfoView.tsx
2166
+ import { jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
2167
+ var PostSectionSourceInfoView = ({ sourceInfo }) => {
2168
+ if (!sourceInfo) {
2169
+ return null;
2170
+ }
2171
+ return /* @__PURE__ */ jsx20("div", { className: "mx-4 mb-3 mt-3 border-b border-gray-100 pb-3", children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-row items-center", children: [
2172
+ /* @__PURE__ */ jsx20("div", { className: "size-7 shrink-0 rounded-full overflow-hidden border border-border-subtle bg-muted flex items-center justify-center", children: sourceInfo.avatar ? /* @__PURE__ */ jsx20(
2173
+ "img",
2174
+ {
2175
+ src: sourceInfo.avatar,
2176
+ alt: sourceInfo.username,
2177
+ className: "size-full object-cover"
2178
+ }
2179
+ ) : /* @__PURE__ */ jsx20("span", { className: "text-xs font-semibold text-muted-foreground", children: sourceInfo.username?.charAt(0)?.toUpperCase() ?? "?" }) }),
2180
+ /* @__PURE__ */ jsxs11("div", { className: "ml-2 flex-col justify-center flex", children: [
2181
+ /* @__PURE__ */ jsx20("div", { className: "text-sm font-semibold text-foreground leading-none", children: sourceInfo.username }),
2182
+ /* @__PURE__ */ jsx20("div", { className: "text-xs text-muted-foreground mt-1 leading-none", children: sourceInfo.url })
2183
+ ] })
2184
+ ] }) });
2185
+ };
2186
+ var PostSectionSourceInfoView_default = PostSectionSourceInfoView;
2187
+
2188
+ // src/components/PostSectionsView.tsx
2189
+ import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
2190
+ var PostSectionsView = ({
2191
+ sections,
2192
+ isPostDetail,
2193
+ onCardPress
2194
+ }) => {
2195
+ const renderSectionItem = useCallback3(
2196
+ (section, index) => {
2197
+ const { type, data } = section;
2198
+ if (type === contentTypeConstants.NEW_POST_MESSAGE) {
2199
+ return /* @__PURE__ */ jsx21(
2200
+ AutoLink_default,
2201
+ {
2202
+ text: data.body || "",
2203
+ className: "whitespace-normal px-4 pb-4 leading-relaxed text-gray-800",
2204
+ style: {
2205
+ whiteSpace: "pre-wrap",
2206
+ wordBreak: "break-word"
2207
+ },
2208
+ mentionData: data.mentionData
2209
+ },
2210
+ `${index} - ${data.body}`
2211
+ );
2212
+ }
2213
+ if (type === contentTypeConstants.NEW_POST_MEDIA) {
2214
+ return /* @__PURE__ */ jsxs12("div", { className: "my-3", children: [
2215
+ data.sourceInfo && /* @__PURE__ */ jsx21(PostSectionSourceInfoView_default, { sourceInfo: data.sourceInfo }),
2216
+ data.body && /* @__PURE__ */ jsx21(
2217
+ AutoLink_default,
2218
+ {
2219
+ text: data.body || "",
2220
+ className: "whitespace-normal px-4 pb-4 leading-relaxed text-gray-800",
2221
+ style: {
2222
+ whiteSpace: "pre-wrap",
2223
+ wordBreak: "break-word"
2224
+ },
2225
+ mentionData: data.mentionData
2226
+ },
2227
+ `${index} - ${data.body}`
2228
+ ),
2229
+ data.items && /* @__PURE__ */ jsx21(
2230
+ PostMediaLayoutCore_default,
2231
+ {
2232
+ items: data.items,
2233
+ isPostDetail,
2234
+ onCardPress
2235
+ }
2236
+ )
2237
+ ] }, `${index} - ${data.body}`);
2238
+ }
2239
+ return null;
2240
+ },
2241
+ [isPostDetail, onCardPress]
2242
+ );
2243
+ if (!sections || sections.length === 0) {
2244
+ return null;
2245
+ }
2246
+ return /* @__PURE__ */ jsx21("div", { className: "space-y-2", children: sections.map((section, index) => {
2247
+ return renderSectionItem(section, index);
2248
+ }) });
2249
+ };
2250
+ var PostSectionsView_default = PostSectionsView;
2251
+
2252
+ // src/components/PostFooter.tsx
2253
+ import { useState as useState3 } from "react";
2254
+ import { NotificationBadge } from "@select-org/ui";
2255
+ import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
2256
+ var PostFooter = ({
2257
+ postHref,
2258
+ commentCount = 0,
2259
+ unreadCommentCount = 0,
2260
+ showFooterLink = false,
2261
+ footerLinkLabel = "Open in app",
2262
+ footerLinkHref = "/",
2263
+ isSnoozed = false,
2264
+ onSnoozeToggle,
2265
+ renderReactionTrigger,
2266
+ renderReactionModal
2267
+ }) => {
2268
+ const [isReactionOpen, setIsReactionOpen] = useState3(false);
2269
+ const handleOpen = () => setIsReactionOpen(true);
2270
+ const handleClose = () => setIsReactionOpen(false);
2271
+ const commentButtonClass = "relative text-muted-foreground hover:text-foreground hover:bg-muted p-2 rounded-full transition-all flex items-center justify-center cursor-pointer bg-transparent border-none select-none";
2272
+ const commentAriaLabel = `View comments${commentCount > 0 ? `, ${commentCount} comment${commentCount !== 1 ? "s" : ""}` : ""}`;
2273
+ const commentBadge = commentCount > 0 ? /* @__PURE__ */ jsx22("span", { className: "absolute top-0 right-0 -mt-0.5 -mr-0.5", children: /* @__PURE__ */ jsx22(
2274
+ NotificationBadge,
2275
+ {
2276
+ count: unreadCommentCount > 0 ? unreadCommentCount : commentCount,
2277
+ variant: unreadCommentCount > 0 ? "solid" : "neutral",
2278
+ size: "sm",
2279
+ className: "ring-2 ring-background"
2280
+ }
2281
+ ) }) : null;
2282
+ return /* @__PURE__ */ jsxs13("section", { className: "flex items-center justify-between p-2 px-4 border-t border-border/50", children: [
2283
+ /* @__PURE__ */ jsxs13("div", { className: "relative flex items-center gap-1", children: [
2284
+ renderReactionTrigger ? renderReactionTrigger({ onOpen: handleOpen }) : /* @__PURE__ */ jsx22(
2285
+ "button",
2286
+ {
2287
+ onClick: (e) => {
2288
+ e.preventDefault();
2289
+ e.stopPropagation();
2290
+ handleOpen();
2291
+ },
2292
+ className: "text-muted-foreground hover:text-foreground hover:bg-muted p-2 rounded-full transition-all cursor-pointer bg-transparent border-none select-none",
2293
+ "aria-label": "Add reaction",
2294
+ "aria-expanded": isReactionOpen,
2295
+ "aria-haspopup": "true",
2296
+ children: /* @__PURE__ */ jsx22(AppIcon, { name: "sendEmoji", "aria-hidden": "true" })
2297
+ }
2298
+ ),
2299
+ onSnoozeToggle && /* @__PURE__ */ jsx22(
2300
+ "button",
2301
+ {
2302
+ onClick: (e) => {
2303
+ e.preventDefault();
2304
+ e.stopPropagation();
2305
+ onSnoozeToggle();
2306
+ },
2307
+ className: cn(
2308
+ "p-2 rounded-full hover:bg-muted transition-all cursor-pointer bg-transparent border-none select-none",
2309
+ isSnoozed ? "text-feedback-warning" : "text-muted-foreground hover:text-foreground"
2310
+ ),
2311
+ "aria-label": isSnoozed ? "Unsnooze post" : "Snooze post",
2312
+ title: isSnoozed ? "Unsnooze post" : "Snooze post (30 min)",
2313
+ children: /* @__PURE__ */ jsx22(AppIcon, { name: "alarmClock" })
2314
+ }
2315
+ ),
2316
+ renderReactionModal && /* @__PURE__ */ jsx22("div", { className: "z-50 absolute bottom-full left-0 mb-2", children: renderReactionModal({
2317
+ isOpen: isReactionOpen,
2318
+ onClose: handleClose,
2319
+ onSend: (emoji) => {
2320
+ handleClose();
2321
+ }
2322
+ }) })
2323
+ ] }),
2324
+ /* @__PURE__ */ jsx22("div", { className: "relative flex items-center ml-auto", children: postHref ? /* @__PURE__ */ jsxs13(
2325
+ "a",
2326
+ {
2327
+ href: postHref,
2328
+ className: cn(commentButtonClass, "no-underline"),
2329
+ onClick: (e) => {
2330
+ e.stopPropagation();
2331
+ },
2332
+ "aria-label": commentAriaLabel,
2333
+ children: [
2334
+ /* @__PURE__ */ jsx22(AppIcon, { name: "messageCircleMore", "aria-hidden": "true" }),
2335
+ commentBadge
2336
+ ]
2337
+ }
2338
+ ) : /* @__PURE__ */ jsxs13("button", { className: commentButtonClass, "aria-label": commentAriaLabel, children: [
2339
+ /* @__PURE__ */ jsx22(AppIcon, { name: "messageCircleMore", "aria-hidden": "true" }),
2340
+ commentBadge
2341
+ ] }) }),
2342
+ showFooterLink && /* @__PURE__ */ jsx22("section", { className: "flex justify-center -mx-5 -mb-2.5 mt-2.5", children: /* @__PURE__ */ jsx22(
2343
+ "a",
2344
+ {
2345
+ href: footerLinkHref,
2346
+ className: "bg-action-primary/5 hover:bg-action-primary/10 transition-colors w-full text-center py-3 text-sm font-medium text-action-primary no-underline rounded-b-xl border-t border-action-primary/10",
2347
+ children: footerLinkLabel
2348
+ }
2349
+ ) })
2350
+ ] });
2351
+ };
2352
+ var PostFooter_default = PostFooter;
2353
+
2354
+ // src/components/PostMediaLayout/uploadStats.ts
2355
+ function deriveUploadStats(statuses) {
2356
+ let inFlight = 0;
2357
+ let done = 0;
2358
+ let failed = 0;
2359
+ for (const s of Object.values(statuses ?? {})) {
2360
+ if (s === "uploading" || s === "processing") inFlight++;
2361
+ else if (s === "uploaded") done++;
2362
+ else if (s === "error") failed++;
2363
+ }
2364
+ return { inFlight, done, failed, total: inFlight + done + failed };
2365
+ }
2366
+
2367
+ // src/components/PostUploadStrip.tsx
2368
+ import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
2369
+ var PostUploadStrip = ({ statuses, labels }) => {
2370
+ const stats = deriveUploadStats(statuses);
2371
+ if (stats.total === 0) return null;
2372
+ if (stats.inFlight === 0 && stats.failed === 0) return null;
2373
+ const hasError = stats.failed > 0;
2374
+ const remaining = stats.inFlight;
2375
+ const remainingTemplate = labels?.uploadingRemaining ?? "Uploading {count} more";
2376
+ const doneLabel = labels?.uploadingDone ?? "All uploaded";
2377
+ const progressLabel = remaining > 0 ? remainingTemplate.replace("{count}", String(remaining)) : doneLabel;
2378
+ return /* @__PURE__ */ jsxs14(
2379
+ "div",
2380
+ {
2381
+ className: cn(
2382
+ "not-prose flex items-center gap-2.5 border-t border-border px-5 py-2.5",
2383
+ hasError ? "bg-feedback-danger/8" : "bg-feedback-info/8"
2384
+ ),
2385
+ children: [
2386
+ remaining > 0 ? /* @__PURE__ */ jsx23(
2387
+ AppIcon,
2388
+ {
2389
+ name: "loader",
2390
+ className: cn(
2391
+ "size-4 shrink-0 animate-spin",
2392
+ hasError ? "text-feedback-danger" : "text-feedback-info"
2393
+ )
2394
+ }
2395
+ ) : /* @__PURE__ */ jsx23(
2396
+ AppIcon,
2397
+ {
2398
+ name: "triangleAlert",
2399
+ className: "size-4 shrink-0 text-feedback-danger"
2400
+ }
2401
+ ),
2402
+ /* @__PURE__ */ jsx23(
2403
+ "span",
2404
+ {
2405
+ className: cn(
2406
+ "truncate text-xs font-medium",
2407
+ hasError ? "text-feedback-danger" : "text-feedback-info"
2408
+ ),
2409
+ children: progressLabel
2410
+ }
2411
+ ),
2412
+ /* @__PURE__ */ jsxs14(
2413
+ "span",
2414
+ {
2415
+ className: cn(
2416
+ "ml-auto shrink-0 text-[11px]",
2417
+ hasError ? "text-feedback-danger/80" : "text-feedback-info/80"
2418
+ ),
2419
+ children: [
2420
+ stats.done,
2421
+ " / ",
2422
+ stats.total,
2423
+ hasError && ` \xB7 ${stats.failed} failed`
2424
+ ]
2425
+ }
2426
+ )
2427
+ ]
2428
+ }
2429
+ );
2430
+ };
2431
+ var PostUploadStrip_default = PostUploadStrip;
2432
+
2433
+ // src/components/EraseConfirmModal.tsx
2434
+ import { useEffect as useEffect2, useState as useState4 } from "react";
2435
+ import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
2436
+ var EraseConfirmModal = ({
2437
+ onYesPress,
2438
+ onNoPress,
2439
+ itemType = "comment",
2440
+ isOpen,
2441
+ handleClose
2442
+ }) => {
2443
+ const [first, setFirst] = useState4(true);
2444
+ const [second, setSecond] = useState4(false);
2445
+ useEffect2(() => {
2446
+ if (!isOpen) return;
2447
+ const closeOnEscape = (e) => {
2448
+ if (e.key === "Escape") handleClose();
2449
+ };
2450
+ document.body.addEventListener("keydown", closeOnEscape);
2451
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
2452
+ document.body.style.overflow = "hidden";
2453
+ document.body.style.paddingRight = `${scrollbarWidth}px`;
2454
+ return () => {
2455
+ document.body.removeEventListener("keydown", closeOnEscape);
2456
+ document.body.style.overflow = "";
2457
+ document.body.style.paddingRight = "";
2458
+ };
2459
+ }, [isOpen, handleClose]);
2460
+ if (!isOpen) return null;
2461
+ const handleFirst = (e) => {
2462
+ e.stopPropagation();
2463
+ e.nativeEvent.stopImmediatePropagation();
2464
+ setFirst(false);
2465
+ setSecond(true);
2466
+ };
2467
+ const handleNoPress = (e) => {
2468
+ onNoPress(e);
2469
+ setFirst(true);
2470
+ setSecond(false);
2471
+ };
2472
+ const eraseLabel = itemType === "Post" ? "Erase post" : "Erase comment";
2473
+ const confirmTitle = itemType === "Post" ? "Are you sure you want to erase this post?" : "Are you sure you want to erase this comment?";
2474
+ const confirmBody = itemType === "Post" ? "This action cannot be undone." : "This action cannot be undone.";
2475
+ return /* @__PURE__ */ jsxs15("div", { children: [
2476
+ /* @__PURE__ */ jsx24("div", { className: "fixed left-0 top-0 z-50 h-screen w-full bg-black/50" }),
2477
+ /* @__PURE__ */ jsx24(
2478
+ "div",
2479
+ {
2480
+ className: "fixed left-0 top-0 z-50 flex h-screen w-full items-center justify-center p-4",
2481
+ onClick: (e) => {
2482
+ e.stopPropagation();
2483
+ e.nativeEvent.stopImmediatePropagation();
2484
+ },
2485
+ children: /* @__PURE__ */ jsxs15("div", { className: "rounded-3xl bg-background px-8 py-6 text-center shadow-3", children: [
2486
+ first && /* @__PURE__ */ jsxs15(
2487
+ "div",
2488
+ {
2489
+ className: "flex w-[256px] flex-col justify-around",
2490
+ onClick: (e) => e.stopPropagation(),
2491
+ children: [
2492
+ /* @__PURE__ */ jsxs15(
2493
+ "div",
2494
+ {
2495
+ className: "mb-4 flex flex-row items-center hover:cursor-pointer hover:opacity-50",
2496
+ "data-testid": "modal-erase-post",
2497
+ onClick: (e) => handleFirst(e),
2498
+ children: [
2499
+ /* @__PURE__ */ jsx24("div", { children: /* @__PURE__ */ jsx24(AppIcon, { name: "chatErase", className: "size-7.5 cursor-pointer" }) }),
2500
+ /* @__PURE__ */ jsx24("p", { className: "ml-3 text-lg", children: eraseLabel })
2501
+ ]
2502
+ }
2503
+ ),
2504
+ /* @__PURE__ */ jsxs15(
2505
+ "div",
2506
+ {
2507
+ className: "flex flex-row items-center hover:cursor-pointer hover:opacity-50",
2508
+ onClick: handleNoPress,
2509
+ children: [
2510
+ /* @__PURE__ */ jsx24("div", { children: /* @__PURE__ */ jsx24(AppIcon, { name: "cancel", className: "cursor-pointer size-6" }) }),
2511
+ /* @__PURE__ */ jsx24("p", { className: "ml-3 text-lg", children: "Cancel" })
2512
+ ]
2513
+ }
2514
+ )
2515
+ ]
2516
+ }
2517
+ ),
2518
+ second && /* @__PURE__ */ jsxs15(
2519
+ "div",
2520
+ {
2521
+ onClick: (e) => e.stopPropagation(),
2522
+ className: "flex w-[296px] flex-col items-center justify-around",
2523
+ children: [
2524
+ /* @__PURE__ */ jsxs15("div", { className: "mb-4", children: [
2525
+ /* @__PURE__ */ jsx24("h4", { className: "mb-2 text-lg font-bold", children: confirmTitle }),
2526
+ /* @__PURE__ */ jsx24("p", { className: "text-muted-foreground", children: confirmBody })
2527
+ ] }),
2528
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-around", children: [
2529
+ /* @__PURE__ */ jsx24(
2530
+ "button",
2531
+ {
2532
+ className: "mr-6 py-2 text-muted-foreground",
2533
+ onClick: handleNoPress,
2534
+ children: "Cancel"
2535
+ }
2536
+ ),
2537
+ /* @__PURE__ */ jsx24(
2538
+ "button",
2539
+ {
2540
+ className: "rounded-3xl bg-action-primary px-6 py-2 font-semibold text-white",
2541
+ onClick: onYesPress,
2542
+ "data-testid": "confirm-modal-erase",
2543
+ children: "Erase"
2544
+ }
2545
+ )
2546
+ ] })
2547
+ ]
2548
+ }
2549
+ )
2550
+ ] })
2551
+ }
2552
+ )
2553
+ ] });
2554
+ };
2555
+ var EraseConfirmModal_default = EraseConfirmModal;
2556
+
2557
+ // src/components/PostCard.tsx
2558
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
2559
+ var PostCard = ({
2560
+ post,
2561
+ author,
2562
+ authorHref,
2563
+ group,
2564
+ groupHref,
2565
+ postHref,
2566
+ linkComponent,
2567
+ imageComponent,
2568
+ isAdmin,
2569
+ isOwner,
2570
+ isPostPreview,
2571
+ onPostPress,
2572
+ onErasePress,
2573
+ onCancelPost,
2574
+ onVotePress,
2575
+ onCardPress,
2576
+ renderActionsMenu,
2577
+ renderReactionTrigger,
2578
+ renderReactionModal,
2579
+ onNavigate,
2580
+ supportDeepviewDomain,
2581
+ labels,
2582
+ mode = "feed",
2583
+ commentCount = 0,
2584
+ unreadCommentCount = 0,
2585
+ isSnoozed = false,
2586
+ onSnoozeToggle,
2587
+ mediaItemStatuses,
2588
+ showFooterLink = false,
2589
+ footerLinkLabel,
2590
+ footerLinkHref,
2591
+ className
2592
+ }) => {
2593
+ const [eraseModalOpen, setEraseModalOpen] = useState5(false);
2594
+ const isPending = !!post.is_pending;
2595
+ const isPostDetail = mode === "detail";
2596
+ const sendingLabel = labels?.sending ?? "Sending...";
2597
+ const contextData = useMemo7(
2598
+ () => ({
2599
+ body: post.body ?? "",
2600
+ htmlBody: post.htmlBody,
2601
+ jsonBody: post.jsonBody,
2602
+ textBody: post.textBody,
2603
+ items: post.items,
2604
+ mentionData: post.mentionData,
2605
+ sections: post.sections
2606
+ }),
2607
+ [post.body, post.htmlBody, post.jsonBody, post.textBody, post.items, post.mentionData, post.sections]
2608
+ );
2609
+ const isEmailPost = Boolean(post.htmlBody && !post.jsonBody);
2610
+ const resolvedContent = useMemo7(
2611
+ () => resolvePostContent(contextData, {
2612
+ postType: post.postType,
2613
+ pollResult: post.poll_result ?? null,
2614
+ postSpecialType: post.isFeatured ? 1 : 0,
2615
+ mode: isPostDetail ? "detail" : mode,
2616
+ isEmailPost
2617
+ }),
2618
+ [contextData, post.postType, post.poll_result, post.isFeatured, isPostDetail, mode, isEmailPost]
2619
+ );
2620
+ const handleErasePress = () => {
2621
+ setEraseModalOpen(true);
2622
+ };
2623
+ const handleEraseConfirm = () => {
2624
+ setEraseModalOpen(false);
2625
+ onErasePress?.();
2626
+ };
2627
+ const handleEraseCancel = () => {
2628
+ setEraseModalOpen(false);
2629
+ };
2630
+ const handleCardClick = (e) => {
2631
+ e.stopPropagation();
2632
+ e.nativeEvent.stopImmediatePropagation();
2633
+ if (isPending) return;
2634
+ onPostPress?.();
2635
+ };
2636
+ const handleCardKeyDown = (e) => {
2637
+ if (e.key === "Enter" || e.key === " ") {
2638
+ e.preventDefault();
2639
+ e.stopPropagation();
2640
+ if (isPending) return;
2641
+ onPostPress?.();
2642
+ }
2643
+ };
2644
+ return /* @__PURE__ */ jsxs16(
2645
+ "article",
2646
+ {
2647
+ className: cn(
2648
+ "flex flex-col box-border bg-background",
2649
+ !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
+ isPostDetail && "cursor-auto rounded-t-2xl w-full",
2651
+ post.isFeatured && !isPostDetail && "bg-surface-raised border-border-subtle sm:border-t-4",
2652
+ isPending && "cursor-default",
2653
+ className
2654
+ ),
2655
+ onClick: !isPostDetail && onPostPress ? handleCardClick : void 0,
2656
+ onKeyDown: !isPostDetail && !isPending && onPostPress ? handleCardKeyDown : void 0,
2657
+ tabIndex: !isPostDetail && !isPending && onPostPress ? 0 : void 0,
2658
+ role: !isPostDetail && !isPending && onPostPress ? "button" : void 0,
2659
+ "aria-label": !isPostDetail && !isPending && onPostPress ? "View post details" : void 0,
2660
+ children: [
2661
+ !isPostDetail && !isPending && postHref && linkComponent && React11.createElement(linkComponent, {
2662
+ href: postHref,
2663
+ className: "sr-only",
2664
+ children: "View post details"
2665
+ }),
2666
+ !isPostDetail && !isPending && postHref && !linkComponent && /* @__PURE__ */ jsx25("a", { href: postHref, className: "sr-only", onClick: (e) => e.stopPropagation(), children: "View post details" }),
2667
+ isPending && /* @__PURE__ */ jsx25("div", { className: "absolute top-0 left-0 w-full z-10", children: /* @__PURE__ */ jsx25(
2668
+ "div",
2669
+ {
2670
+ className: "h-1 w-full overflow-hidden rounded bg-muted",
2671
+ "aria-label": sendingLabel,
2672
+ "data-testid": "post-loader",
2673
+ children: /* @__PURE__ */ jsx25("div", { className: "h-full animate-pulse bg-action-primary" })
2674
+ }
2675
+ ) }),
2676
+ !isPending && eraseModalOpen && /* @__PURE__ */ jsx25(
2677
+ EraseConfirmModal_default,
2678
+ {
2679
+ isOpen: eraseModalOpen,
2680
+ itemType: "Post",
2681
+ onYesPress: handleEraseConfirm,
2682
+ onNoPress: handleEraseCancel,
2683
+ handleClose: handleEraseCancel
2684
+ }
2685
+ ),
2686
+ /* @__PURE__ */ jsxs16("div", { className: cn(isPending && "opacity-50"), children: [
2687
+ /* @__PURE__ */ jsx25(
2688
+ PostHeader_default,
2689
+ {
2690
+ author,
2691
+ authorHref,
2692
+ group,
2693
+ groupHref,
2694
+ postedAt: post.postedAt,
2695
+ postType: post.postType,
2696
+ isFeatured: post.isFeatured,
2697
+ isPostPreview,
2698
+ isAdmin,
2699
+ isOwner,
2700
+ onErasePress: onErasePress ? handleErasePress : void 0,
2701
+ onCancelPost,
2702
+ renderActionsMenu,
2703
+ linkComponent,
2704
+ imageComponent,
2705
+ labels
2706
+ }
2707
+ ),
2708
+ /* @__PURE__ */ jsx25(
2709
+ PostBody_default,
2710
+ {
2711
+ textContent: resolvedContent.textContent,
2712
+ showFull: isPostDetail,
2713
+ isPostDetail,
2714
+ isRichContent: resolvedContent.isRichContent,
2715
+ isEmailPost,
2716
+ hideEmailDescription: isEmailPost && !isPostDetail && resolvedContent.primaryContent?.type === "media",
2717
+ onNavigate,
2718
+ supportDeepviewDomain
2719
+ }
2720
+ ),
2721
+ resolvedContent.sections && /* @__PURE__ */ jsx25(
2722
+ PostSectionsView_default,
2723
+ {
2724
+ sections: resolvedContent.sections.sections,
2725
+ isPostDetail,
2726
+ onCardPress: onCardPress ?? (() => {
2727
+ })
2728
+ }
2729
+ ),
2730
+ /* @__PURE__ */ jsx25(
2731
+ PostItemsView_default,
2732
+ {
2733
+ primaryContent: resolvedContent.primaryContent,
2734
+ isPostDetail,
2735
+ onCardPress: onCardPress ?? (() => {
2736
+ }),
2737
+ onVotePress: onVotePress ?? (() => {
2738
+ }),
2739
+ isPostOwner: isOwner,
2740
+ parentId: post.context_id,
2741
+ isCommentScope: false,
2742
+ imageComponent,
2743
+ mediaItemStatuses
2744
+ }
2745
+ ),
2746
+ /* @__PURE__ */ jsx25(
2747
+ PostUploadStrip_default,
2748
+ {
2749
+ statuses: mediaItemStatuses,
2750
+ labels: {
2751
+ uploadingRemaining: labels?.uploadingRemaining,
2752
+ uploadingDone: labels?.uploadingDone
2753
+ }
2754
+ }
2755
+ )
2756
+ ] }),
2757
+ /* @__PURE__ */ jsx25("div", { className: cn(isPending && "opacity-50 pointer-events-none"), children: /* @__PURE__ */ jsx25(
2758
+ PostFooter_default,
2759
+ {
2760
+ postHref,
2761
+ commentCount,
2762
+ unreadCommentCount,
2763
+ isSnoozed,
2764
+ onSnoozeToggle,
2765
+ showFooterLink,
2766
+ footerLinkLabel,
2767
+ footerLinkHref,
2768
+ renderReactionTrigger,
2769
+ renderReactionModal
2770
+ }
2771
+ ) })
2772
+ ]
2773
+ }
2774
+ );
2775
+ };
2776
+ var PostCard_default = PostCard;
2777
+
2778
+ // src/components/PostSectionsSummaryView.tsx
2779
+ import { useCallback as useCallback4, useMemo as useMemo8 } from "react";
2780
+ import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
2781
+ var BODY_SIZE = 80;
2782
+ var PostSectionsSummaryView = ({
2783
+ sections,
2784
+ fontColor,
2785
+ onCardPress
2786
+ }) => {
2787
+ const imageItems = useMemo8(() => {
2788
+ if (!sections || sections.length === 0) return [];
2789
+ return sections.reduce((items, section) => {
2790
+ if (section.type === contentTypeConstants.NEW_POST_MEDIA) {
2791
+ return items.concat(section.data.items || []);
2792
+ }
2793
+ return items;
2794
+ }, []);
2795
+ }, [sections]);
2796
+ const textBody = useMemo8(() => {
2797
+ if (!sections || sections.length === 0) return "";
2798
+ const allBody = sections.reduce((body, section) => {
2799
+ if (section.type === contentTypeConstants.NEW_POST_MEDIA && section.data.body) {
2800
+ return body + (body ? "\n" : "") + section.data.body;
2801
+ }
2802
+ return body;
2803
+ }, "");
2804
+ return allBody ? `${allBody.slice(0, BODY_SIZE)}...` : "";
2805
+ }, [sections]);
2806
+ const renderSectionItem = useCallback4(
2807
+ (section, index) => {
2808
+ const { type, data } = section;
2809
+ if (type === contentTypeConstants.NEW_POST_MESSAGE) {
2810
+ return /* @__PURE__ */ jsx26(
2811
+ AutoLink_default,
2812
+ {
2813
+ text: data.body || "",
2814
+ className: "whitespace-normal px-4 pb-4 leading-relaxed text-gray-800",
2815
+ style: {
2816
+ color: fontColor || "var(--color-foreground)",
2817
+ whiteSpace: "pre-wrap",
2818
+ wordBreak: "break-word"
2819
+ },
2820
+ mentionData: data.mentionData
2821
+ },
2822
+ `${index} - ${data.body}`
2823
+ );
2824
+ }
2825
+ return null;
2826
+ },
2827
+ [fontColor]
2828
+ );
2829
+ if (!sections || sections.length === 0) {
2830
+ return null;
2831
+ }
2832
+ return /* @__PURE__ */ jsxs17("div", { className: "flex-col", children: [
2833
+ sections.map((section, index) => {
2834
+ return renderSectionItem(section, index);
2835
+ }),
2836
+ textBody && /* @__PURE__ */ jsx26(
2837
+ AutoLink_default,
2838
+ {
2839
+ text: textBody,
2840
+ className: "whitespace-normal px-4 pb-4 leading-relaxed text-gray-800",
2841
+ style: {
2842
+ color: fontColor || "var(--color-foreground)",
2843
+ whiteSpace: "pre-wrap",
2844
+ wordBreak: "break-word"
2845
+ },
2846
+ mentionData: []
2847
+ },
2848
+ "body"
2849
+ ),
2850
+ /* @__PURE__ */ jsx26(
2851
+ PostMediaLayoutCore_default,
2852
+ {
2853
+ items: imageItems
2854
+ }
2855
+ )
2856
+ ] });
2857
+ };
2858
+ var PostSectionsSummaryView_default = PostSectionsSummaryView;
2859
+
2860
+ // src/modals/ReactionModal.tsx
2861
+ import { useRef as useRef2, useEffect as useEffect3 } from "react";
2862
+ import { jsx as jsx27 } from "react/jsx-runtime";
2863
+ var reactionList = ["\u{1F44D}", "\u{1F60D}", "\u{1F602}", "\u{1F631}", "\u{1F62D}", "\u{1F60E}"];
2864
+ var ReactionModal = ({ isOpen, close, sendReaction, onAnalyticsEvent }) => {
2865
+ const ref = useRef2(null);
2866
+ useEffect3(() => {
2867
+ if (!isOpen) return;
2868
+ onAnalyticsEvent?.("reaction");
2869
+ const handleOutsideClick = (e) => {
2870
+ if (ref.current && !ref.current.contains(e.target)) {
2871
+ close();
2872
+ }
2873
+ };
2874
+ document.addEventListener("click", handleOutsideClick);
2875
+ return () => {
2876
+ document.removeEventListener("click", handleOutsideClick);
2877
+ };
2878
+ }, [isOpen, close, onAnalyticsEvent]);
2879
+ if (!isOpen) return null;
2880
+ return /* @__PURE__ */ jsx27("div", { className: "z-500 flex items-center justify-center", role: "dialog", "aria-modal": "true", "aria-label": "Reaction picker", children: /* @__PURE__ */ jsx27("div", { ref, className: "rounded-3xl bg-muted p-2", children: /* @__PURE__ */ jsx27("div", { className: "flex justify-around", children: reactionList.map((reaction, index) => {
2881
+ return /* @__PURE__ */ jsx27(
2882
+ "button",
2883
+ {
2884
+ type: "button",
2885
+ onClick: (e) => {
2886
+ e.preventDefault();
2887
+ e.stopPropagation();
2888
+ onAnalyticsEvent?.("reaction_send");
2889
+ sendReaction(reaction);
2890
+ close();
2891
+ },
2892
+ className: "cursor-pointer px-2 text-3xl hover:scale-125 transition-transform",
2893
+ children: reaction
2894
+ },
2895
+ reaction + index
2896
+ );
2897
+ }) }) }) });
2898
+ };
2899
+ export {
2900
+ AutoLink_default as AutoLink,
2901
+ EraseConfirmModal_default as EraseConfirmModal,
2902
+ MediaUploadStatusOverlay_default as MediaUploadStatusOverlay,
2903
+ PostBody_default as PostBody,
2904
+ PostCard_default as PostCard,
2905
+ PostEmail_default as PostEmail,
2906
+ PostEmbedView_default as PostEmbedView,
2907
+ PostFooter_default as PostFooter,
2908
+ PostHeader_default as PostHeader,
2909
+ PostItemsView_default as PostItemsView,
2910
+ PostLinkItem_default as PostLinkItem,
2911
+ PostMediaLayoutCore_default as PostMediaLayoutCore,
2912
+ PostPollView_default as PostPollView,
2913
+ PostSectionSourceInfoView_default as PostSectionSourceInfoView,
2914
+ PostSectionsSummaryView_default as PostSectionsSummaryView,
2915
+ PostSectionsView_default as PostSectionsView,
2916
+ PostTextItem_default as PostTextItem,
2917
+ PostUploadStrip_default as PostUploadStrip,
2918
+ ReactionModal,
2919
+ hasMeaningfulHtml,
2920
+ resolvePostContent
2921
+ };
2922
+ /*!
2923
+ * React Native Autolink
2924
+ *
2925
+ * Copyright 2016-2018 Josh Swan
2926
+ * Released under the MIT license
2927
+ * https://github.com/joshswan/react-native-autolink/blob/master/LICENSE
2928
+ *
2929
+ * Simplified and improved by Dean, to support mention list.
2930
+ * Ported to design-system post-components: routing and config dependencies removed.
2931
+ */
2932
+ //# sourceMappingURL=index.js.map