Files
discord-decomp/modules/icymi/native/ICYMIShared.tsx
T
2026-11-01 12:12:55 +02:00

540 lines
21 KiB
TypeScript

// Metro module 15361 (f15362).
import { useCallback, useMemo, useContext, useEffect } from "react";
import { View } from "react-native";
import ThreadMessageStore from "../../threads/ThreadMessageStore";
import ChannelStore from "../../../stores/ChannelStore";
import GuildMemberStore from "../../../stores/GuildMemberStore";
import PermissionStore from "../../../stores/PermissionStore";
import UserStore from "../../../stores/UserStore";
import { AnalyticsObjectTypes, AnalyticsObjects, AnalyticsPages, DEFAULT_ROLE_COLOR_HEX, MAX_MESSAGES_FOR_JUMP, MessageFlags, Permissions, Routes } from "../../../Constants";
import { Fragment } from "react/jsx-runtime";
import { ContentType } from "../ICYMITypes";
import { FireIcon } from "../../../design/components/Icon/native/redesign/generated/FireIcon";
import { ImageIcon } from "../../../design/components/Icon/native/redesign/generated/ImageIcon";
import { CirclePlayIcon } from "../../../design/components/Icon/native/redesign/generated/CirclePlayIcon";
import { LightbulbIcon } from "../../../design/components/Icon/native/redesign/generated/LightbulbIcon";
import { ack } from "../../../actions/ReadStateActionCreators";
import safeTransitionTo_native from "../../links/safeTransitionTo.native";
import { runAfterInteractions } from "../../../utils/native/RunAfterInteractionsUtils";
import MessageActionCreators from "../../../actions/MessageActionCreators";
import { JumpType } from "../../../flow/Client";
import { createICYMIStyles } from "./createICYMIStyles";
import { isAndroid } from "../../../utils/PlatformUtils";
import Module712 from "../../../_bundle/Module712";
import { Avatar, AvatarSizes } from "../../../design/void/native";
import ClipView, { CutoutShape } from "../../../design/components/Icon/native/ClipView";
import GuildIcon, { GuildIconSizes } from "../../guild/native/GuildIcon";
import { useFontScale } from "../../screen/native/useFontScale";
import { PressableHighlight, PressableOpacity } from "../../../design/void/Pressables/native/Pressables";
import { Text } from "../../../design/components/Text/native/Text";
import { getRelativeTimestamp } from "../../notification_center/NotificationCenterUtils";
import { openDetailsActionSheet } from "./util/openDetailsActionSheet";
import { MoreHorizontalIcon } from "../../../design/components/Icon/native/redesign/generated/MoreHorizontalIcon";
import { useStateFromStores, useStateFromStoresObject } from "../../../_bundle/Module589";
import { useName } from "../../../utils/NicknameUtils";
import ICYMIActionCreators from "../ICYMIActionCreators";
import showUserProfileActionSheet from "../../user_profile/native/showUserProfileActionSheet";
import { AnnouncementsIcon } from "../../../design/components/Icon/native/redesign/generated/AnnouncementsIcon";
import { contentTypeToText, determineContentType } from "../ICYMIUtils";
import { useEnsureHydratedUsers } from "../../main_tabs_v2/native/shared_components/happening_now/useEnsureHydratedUsers";
import { CalendarIcon } from "../../../design/components/Icon/native/redesign/generated/CalendarIcon";
import { intl, t } from "../../../intl/index.native";
import { ICYMIContext } from "./ICYMIContext";
import useTheme from "../../../hooks/useTheme";
import { useToken } from "../../../design/tokens/native/useToken";
import { hexWithOpacity } from "../../../utils/ColorUtils";
import * as _ReanimatedRexport from "../../reanimated/ReanimatedRexport";
import ReanimatedRexport, { useSharedValue, useAnimatedStyle, withSequence, withDelay } from "../../reanimated/ReanimatedRexport";
import { withTiming } from "../../../design/animation/reanimated/timing/timing";
import ChannelActionCreators from "../../../actions/ChannelActionCreators";
import { ChatIcon } from "../../../design/components/Icon/native/redesign/generated/ChatIcon";
import { ChevronSmallRightIcon } from "../../../design/components/Icon/native/redesign/generated/ChevronSmallRightIcon";
import MarkupUtils from "../../markup/MarkupUtils";
let styles, CutoutGuildIconWithUser, CutoutGuildIcon, GuildContentPost, __initData;
const Separator = function Separator() {
return <View style={styles().separator} />;
};
CutoutGuildIconWithUser = function CutoutGuildIconWithUser(props) {
const { guild: guild6, author: author3 } = props;
return <CutoutGuildIcon guild={guild6} icon={<Avatar animate style={styles().authorIcon} guildId={guild6.id} user={author3} size={AvatarSizes.XSMALL} />} />;
};
CutoutGuildIcon = function CutoutGuildIcon(props) {
return <View style={{
width: 40,
height: 40
}}>
<ClipView cutouts={[{
shape: CutoutShape.Circle,
x: 16,
y: 14,
size: 32
}]}>
<GuildIcon guild={props.guild} size={GuildIconSizes.NORMAL} />
</ClipView>
{props.icon}
</View>;
};
GuildContentPost = function GuildContentPost(props) {
const { guild, channel, timestamp, hideTimestamp, children, avatar, title, subtitle, id, type, onHeaderPress, onHeaderLongPress, disableInteractions } = props;
const r20 = styles();
return <>
<PressableHighlight onPress={onHeaderPress} onLongPress={onHeaderLongPress} style={r20.content}>
<View style={useFontScale() > 1.8 ? r20.channelNameAndAccessoryLarge : r20.channelNameAndAccessory}>
<View style={r20.header}>
{avatar}
<View style={r20.headerInfo}>
<View style={r20.title}>
<View style={r20.titleLeft}>
{title}
{hideTimestamp ? !hideTimestamp : <Text lineClamp={1} variant="text-xs/normal" color="text-muted">
{getRelativeTimestamp(timestamp)}
</Text>}
</View>
{disableInteractions ? null : guild == null ? null : <PressableOpacity onPress={() => openDetailsActionSheet({
guildId: guild.id,
channelId: channel == null ? void 0 : channel.id,
id,
type
})} style={r20.subtitleTrailing} hitSlop={8}>
<MoreHorizontalIcon color={Module712.colors.ICON_MUTED} size="sm" />
</PressableOpacity>}
</View>
<View style={r20.subTitleContainer}>
<View style={r20.subtitle}>{subtitle}</View>
</View>
</View>
</View>
</View>
</PressableHighlight>
{children}
</>;
};
styles = createICYMIStyles((a0) => ({
simplePostContent: {
flex: 1,
marginTop: isAndroid() ? -2 : 0,
overflow: "hidden"
},
content: {
flex: 1,
marginTop: isAndroid() ? -2 : 0,
overflow: "hidden",
paddingTop: a0.margin
},
insetIconWrapper: {
position: "absolute",
right: -4,
bottom: -2,
padding: 4,
borderRadius: Module712.radii.round,
backgroundColor: Module712.colors.REDESIGN_BUTTON_TERTIARY_BACKGROUND
},
authorIcon: {
position: "absolute",
right: -4,
bottom: -2
},
moreDetailsIcon: {
tintColor: Module712.colors.TEXT_MUTED
},
channelNameAndAccessory: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingBottom: a0.margin,
marginHorizontal: a0.margin
},
channelNameAndAccessoryLarge: {
flexDirection: "column",
paddingBottom: a0.margin,
marginHorizontal: a0.margin
},
header: {
flexDirection: "row",
flexGrow: 1
},
headerInfo: {
flexGrow: 1,
flexShrink: 1,
marginLeft: a0.margin
},
title: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 2
},
titleLeft: {
flexShrink: 1,
flexGrow: 0,
flexDirection: "row",
alignItems: "center",
gap: 6
},
subTitleContainer: {
flexDirection: "row",
justifyContent: "space-between",
borderRadius: Module712.radii.sm
},
subtitle: {
flexShrink: 1,
flexGrow: 0,
width: "100%"
},
genContentSubtitle: {
flexDirection: "row",
alignItems: "center",
gap: Module712.space.PX_4
},
genContentSubtitleChannel: {
flexDirection: "row",
alignItems: "center",
gap: 2,
flex: 1
},
subtitleTrailing: {
paddingVertical: 1
},
separator: {
height: 1,
width: "100%",
backgroundColor: Module712.colors.BORDER_SUBTLE
},
eventsSubtitle: {
flexDirection: "row",
alignItems: "center"
},
comments: {
padding: 8,
backgroundColor: Module712.colors.REDESIGN_BUTTON_TERTIARY_BACKGROUND,
borderRadius: Module712.radii.md,
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 8
},
recentCommentText: {
flexGrow: 1,
flexShrink: 1,
marginRight: 12
},
commentCount: {
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 2,
justifySelf: "end"
},
commentsIcon: {
width: 20,
height: 20,
tintColor: Module712.colors.INTERACTIVE_TEXT_DEFAULT
},
chevron: {
tintColor: Module712.colors.TEXT_MUTED
}
}));
__initData = {
code: "function ICYMISharedTsx1(){const{interpolateColor,progress,bgColor,bgColorHighlighted}=this.__closure;return{backgroundColor:interpolateColor(progress.get(),[0,1],[bgColor,bgColorHighlighted])};}"
};
export function navigateToPost(a0, a1, a2) {
const channelId = a0;
const messageId = a2;
setTimeout(() => {
ack(channelId, {
page: AnalyticsPages.ICYMI,
object: AnalyticsObjects.ACK_MESSAGE_VIEWED,
objectType: AnalyticsObjectTypes.ACK_SEMI_AUTOMATIC
}, true, true, messageId);
}, 1500);
safeTransitionTo_native(Routes.CHANNEL(a1, a0, a2), {
openChannel: true,
navigationReplace: false
});
if (a2 != null) {
runAfterInteractions(() => {
MessageActionCreators.fetchMessages({
channelId,
limit: MAX_MESSAGES_FOR_JUMP,
jump: {
messageId,
flash: true,
jumpType: JumpType.ANIMATED
}
});
}, 150);
}
}
export { Separator };
export function truncateUsername(a0) {
return a0.length > 20 ? `${a0.slice(0, 17)}...` : a0;
}
export { CutoutGuildIcon };
export { GuildContentPost };
export function AnnouncementContentPost(props) {
const { guild: guild2, channel: channel2, author, timestamp, children, id: id2, mentioned, onHeaderPress, onHeaderLongPress } = props;
const r15 = styles();
let color = useStateFromStores([GuildMemberStore], () => GuildMemberStore.getMember(guild2.id, author.id))?.colorString;
if (color == null) {
color = DEFAULT_ROLE_COLOR_HEX;
}
const name = useName(guild2.id, channel2.id, author);
return <GuildContentPost guild={guild2} channel={channel2} timestamp={timestamp} avatar={<CutoutGuildIconWithUser guild={guild2} author={author} />} title={<Text key={channel2.id} style={{
maxWidth: 225
}} lineClamp={1} variant="text-sm/medium" color="text-muted">{guild2.name}</Text>} subtitle={<Text lineClamp={2} variant="text-md/normal" color="text-default" style={r15.subtitle}>
<Text variant="text-md/semibold" onPress={useCallback(() => {
ICYMIActionCreators.itemInteracted(id2, "announcement", "open_profile");
ICYMIActionCreators.feedItemActioned({
itemId: id2,
itemType: "announcement",
actionParameters: {
actionGestureType: "press",
actionTargetElement: "item_header",
actionIntentType: "open",
actionDestinationType: "user"
}
});
showUserProfileActionSheet({
userId: author.id,
channelId: channel2.id
});
}, [author.id, channel2.id, id2])} style={{
color
}}>
{(name.length > 20 ? `${name.slice(0, 17)}...` : name) + " "}
</Text>
<AnnouncementsIcon size="sm" color={Module712.colors.TEXT_SUBTLE} />
{" " + contentTypeToText(ContentType.ANNOUNCEMENT, mentioned)}
</Text>} onHeaderPress={onHeaderPress} onHeaderLongPress={onHeaderLongPress} id={id2} type="announcement">{children}</GuildContentPost>;
}
export function GuildEventPost(props) {
let props3;
let stateFromStores2;
const { guild: guild3, channel: channel3, children, event, type: type2, onHeaderPress } = props;
stateFromStores2 = void 0;
const r14 = styles();
let host_id = event.host_id;
if (host_id == null) {
host_id = event.creator_id;
}
const creator_id = host_id;
useEnsureHydratedUsers(event.guild_id, creator_id != null ? [creator_id] : []);
const stateFromStores = useStateFromStores([UserStore], () => UserStore.getUser(creator_id));
stateFromStores2 = useStateFromStores([GuildMemberStore], () => creator_id != null ? GuildMemberStore.getMember(guild3.id, creator_id) : null);
const r47 = [stateFromStores, channel3?.id, event.id, , ,];
const highestRoleId = stateFromStores2?.highestRoleId;
const r52 = () => {
if (stateFromStores != null) {
ICYMIActionCreators.itemInteracted(event.id, type2, "open_profile");
ICYMIActionCreators.feedItemActioned({
itemId: event.id,
itemType: "guild_event",
actionParameters: {
actionGestureType: "press",
actionTargetElement: "item_header",
actionIntentType: "open",
actionDestinationType: "user"
}
});
showUserProfileActionSheet({
userId: stateFromStores.id,
roleId: stateFromStores2?.highestRoleId,
channelId: channel3?.id
});
}
};
r47[3] = highestRoleId;
r47[4] = type2;
const callback = useCallback(r52, r47);
let color = stateFromStores2?.colorString;
if (color == null) {
color = DEFAULT_ROLE_COLOR_HEX;
}
const r55 = event.host_id != null;
const props2 = {
guild: guild3,
channel: channel3,
timestamp: 0,
hideTimestamp: true,
avatar: stateFromStores != null ? <CutoutGuildIconWithUser guild={guild3} author={stateFromStores} /> : <GuildIcon guild={guild3} size={GuildIconSizes.NORMAL} />,
title: <Text key={event.id} style={{
maxWidth: 225
}} lineClamp={1} variant="text-sm/medium" color="text-muted">{guild3.name}</Text>,
subtitle: null,
id: null,
type: null,
onHeaderPress: null,
children: null
};
if (stateFromStores != null) {
const username = stateFromStores.username;
props3 = {
children: [<Text variant="text-md/semibold" onPress={callback} style={{
color
}}>
{(username.length > 20 ? `${username.slice(0, 17)}...` : username) + " "}
</Text>, <CalendarIcon size="sm" color={Module712.colors.TEXT_SUBTLE} />, r55 ? " " + intl.string(t["42OrO4"]) : " " + intl.string(t.Vu15se)]
};
} else {
props3 = {
children: [<CalendarIcon size="sm" color={Module712.colors.TEXT_SUBTLE} />, " " + intl.string(t.T7MIsc)]
};
}
props2.subtitle = <Text lineClamp={2} variant="text-md/normal" color="text-default" style={r14.subtitle}>
<Fragment {...props3} />
</Text>;
props2.id = event.id;
props2.type = type2;
props2.onHeaderPress = onHeaderPress;
props2.children = children;
return <GuildContentPost {...props2} />;
}
export function MessageContentPost(props) {
const { guild: guild4, channel: channel4, timestamp, author: author2, message, children, id: id3, type: type3, onHeaderPress, onHeaderLongPress } = props;
const memo = useMemo(() => determineContentType(channel4, message), [channel4, message]);
let color = useStateFromStores([GuildMemberStore], () => GuildMemberStore.getMember(guild4.id, author2.id))?.colorString;
if (color == null) {
color = DEFAULT_ROLE_COLOR_HEX;
}
const name = useName(guild4.id, channel4.id, author2);
const Component = ContentType.POPULAR_MESSAGE === memo ? FireIcon : ContentType.IMAGE === memo ? ImageIcon : ContentType.VIDEO === memo ? CirclePlayIcon : LightbulbIcon;
const callback = useCallback(() => {
ICYMIActionCreators.itemInteracted(id3, type3, "open_profile");
ICYMIActionCreators.feedItemActioned({
itemId: id3,
itemType: "message",
actionParameters: {
actionGestureType: "press",
actionTargetElement: "item_header",
actionIntentType: "open",
actionDestinationType: "user"
}
});
showUserProfileActionSheet({
userId: author2.id,
channelId: channel4.id
});
}, [author2.id, channel4.id, id3, type3]);
return <GuildContentPost guild={guild4} channel={channel4} timestamp={timestamp} avatar={<CutoutGuildIconWithUser guild={guild4} author={author2} />} title={<Text key={channel4.id} style={{
maxWidth: 225
}} lineClamp={1} variant="text-sm/medium" color="text-default">{guild4.name}</Text>} subtitle={<Text lineClamp={2} variant="text-md/normal" color="text-default" style={{
marginRight: useContext(ICYMIContext).margin
}}>
<Text style={{
color
}} onPress={callback} variant="text-md/semibold">
{(name.length > 20 ? `${name.slice(0, 17)}...` : name) + " "}
</Text>
<Component size="sm" color={Module712.colors.TEXT_SUBTLE} />
{" " + contentTypeToText(memo)}
</Text>} onHeaderPress={onHeaderPress} onHeaderLongPress={onHeaderLongPress} id={id3} type={type3}>{children}</GuildContentPost>;
}
export function SimplePost(props) {
let props2;
const { children, hideDivider, highlight = false } = props;
let c21903_0 = highlight;
const r13 = styles();
const token = useToken(Module712.colors.MESSAGE_HIGHLIGHT_BACKGROUND_DEFAULT, useTheme());
const bgColorHighlighted = hexWithOpacity(Module712.unsafe_rawColors.BRAND_360, 0.25);
let c21903_2 = bgColorHighlighted;
const sharedValue = useSharedValue(0);
const c = function c() {
return {
backgroundColor: _ReanimatedRexport.interpolateColor(sharedValue.get(), [0, 1], [token, c21903_2])
};
};
c.__closure = {
interpolateColor: _ReanimatedRexport.interpolateColor,
progress: sharedValue,
bgColor: token,
bgColorHighlighted
};
c.__workletHash = 11116019021445;
c.__initData = __initData;
const animatedStyle = useAnimatedStyle(c);
useEffect(() => {
if (c21903_0) {
sharedValue.set(withSequence(withTiming(1, {
duration: 500
}), withDelay(500, withTiming(0, {
duration: 350
}))));
}
}, [highlight, sharedValue]);
const r56 = {
children: null
};
if (highlight) {
r56.children = [<ReanimatedRexport.View style={[r13.simplePostContent, animatedStyle]}>{children}</ReanimatedRexport.View>, hideDivider ? null : <Separator />];
props2 = r56;
} else {
r56.children = [<View style={r13.simplePostContent}>{children}</View>, hideDivider ? null : <Separator />];
props2 = r56;
}
return <Fragment {...props2} />;
}
export function ThreadAsComments(props) {
const { guild: guild6, parentMessage, onPress: onPress2, style } = props;
let inForum = props.inForum;
const r14 = styles();
if (inForum == null) {
inForum = false;
}
const guild5 = guild6;
const parentMessage2 = parentMessage;
let c21905_2 = inForum;
const stateFromStoresObject = useStateFromStoresObject([ChannelStore, ThreadMessageStore], () => {
const r2 = {
thread: ChannelStore.getChannel(parentMessage2.id),
messageCount: null,
mostRecentMessage: null
};
let messageCount = ThreadMessageStore.getCount(parentMessage2.id);
if (messageCount == null) {
messageCount = 0;
}
r2.messageCount = messageCount;
r2.mostRecentMessage = ThreadMessageStore.getMostRecentMessage(parentMessage2.id);
return r2;
});
const { thread, messageCount, mostRecentMessage } = stateFromStoresObject;
useEffect(() => {
if ((parentMessage2.hasFlag(MessageFlags.HAS_THREAD) || c21905_2) && ThreadMessageStore.getMostRecentMessage(parentMessage2.id) == null) {
ChannelActionCreators.preload(guild5.id, parentMessage2.id);
MessageActionCreators.fetchMessages({
channelId: parentMessage2.id,
isPreload: true,
limit: 25
});
}
}, [guild5.id, inForum, parentMessage]);
if (useStateFromStores([PermissionStore], () => PermissionStore.canWithPartialContext(Permissions.VIEW_CHANNEL, {
channelId: parentMessage.id
}))) {
if (thread != null && mostRecentMessage != null) {
const r42 = messageCount > 99 ? "99+" : messageCount;
return <PressableHighlight style={[r14.comments, style]} onPress={onPress2}>
<Avatar user={mostRecentMessage?.author} guildId={thread.guild_id} size={AvatarSizes.XSMALL} />
<Text variant="text-sm/semibold" lineClamp={1} style={r14.recentCommentText}>
{mostRecentMessage.content.length > 0 ? MarkupUtils.parseInlineReply(mostRecentMessage.content, true) : intl.string(t["6kp9H2"])}
</Text>
<View style={r14.commentCount}>
<ChatIcon style={r14.commentsIcon} />
<Text variant="text-sm/bold" color="interactive-text-default">{r42}</Text>
<ChevronSmallRightIcon style={r14.chevron} size="xxs" />
</View>
</PressableHighlight>;
}
return <PressableHighlight style={[r14.comments, style]} onPress={onPress2}>
<Text variant="text-md/semibold" color="text-muted" lineClamp={1} style={r14.recentCommentText}>
{intl.string(t.VMWjXW)}
</Text>
<View style={r14.commentCount}>
<ChatIcon style={r14.commentsIcon} />
<ChevronSmallRightIcon style={r14.chevron} size="xxs" />
</View>
</PressableHighlight>;
}
return null;
}