Skip to content

Commit dafde12

Browse files
committed
feat(app): support paints in reply body
1 parent c67c5f5 commit dafde12

3 files changed

Lines changed: 49 additions & 19 deletions

File tree

src/components/Chat/Chat.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,11 @@ export const Chat = memo(({ channelName, channelId }: ChatProps) => {
658658
.peek()
659659
.find(m => m?.message_id === message.message_id);
660660

661+
const twitchUserId = message.userstate['user-id'];
662+
if (twitchUserId) {
663+
void fetchUserCosmetics(twitchUserId);
664+
}
665+
661666
setReplyTo({
662667
messageId: message.message_id,
663668
username: message.sender,
@@ -667,9 +672,10 @@ export const Chat = memo(({ channelName, channelId }: ChatProps) => {
667672
parentMessage?.message as ParsedPart[],
668673
),
669674
color: message.userstate.color,
675+
userId: twitchUserId || undefined,
670676
});
671677
},
672-
[messages$],
678+
[fetchUserCosmetics, messages$],
673679
);
674680

675681
const handleEmoteSelect = useCallback((item: EmotePickerItem) => {

src/components/Chat/components/ChatInputSection.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Button } from '@app/components/Button/Button';
2+
import { PaintedUsername } from '@app/components/Chat/components/ChatMessage/CosmeticUsername/CosmeticUsername';
23
import { Icon } from '@app/components/Icon/Icon';
34
import { Text } from '@app/components/Text/Text';
45
import type { SanitisedEmote } from '@app/types/emote';
@@ -21,6 +22,10 @@ export interface ReplyToData {
2122
replyParentUserLogin: string;
2223
parentMessage: string;
2324
color?: string;
25+
/**
26+
* Twitch user-id for 7TV paint lookup in the reply preview
27+
*/
28+
userId?: string;
2429
}
2530

2631
interface ChatInputSectionProps {
@@ -81,17 +86,18 @@ export const ChatInputSection = memo(
8186
<View style={styles.replyPreview}>
8287
<View style={styles.replyIndicator} />
8388
<View style={styles.replyContent}>
84-
<Text style={styles.replyLabel}>
85-
Replying to{' '}
86-
<Text
87-
style={[
88-
styles.replyUsername,
89-
replyTo.color && { color: lightenColor(replyTo.color) },
90-
]}
91-
>
92-
{replyTo.username}
93-
</Text>
94-
</Text>
89+
<View style={styles.replyLabelRow}>
90+
<Text style={styles.replyLabel}>Replying to </Text>
91+
<PaintedUsername
92+
username={replyTo.username}
93+
userId={replyTo.userId}
94+
showColon={false}
95+
usernameTextStyle={styles.replyPaintedUsername}
96+
fallbackColor={
97+
replyTo.color ? lightenColor(replyTo.color) : undefined
98+
}
99+
/>
100+
</View>
95101
{replyTo.message && (
96102
<Text style={styles.replyMessagePreview} numberOfLines={1}>
97103
{truncate(replyTo.message.trim() || replyTo.message, 60)}
@@ -227,13 +233,18 @@ const styles = StyleSheet.create(theme => ({
227233
replyContent: {
228234
flex: 1,
229235
},
236+
replyLabelRow: {
237+
flexDirection: 'row',
238+
flexWrap: 'wrap',
239+
alignItems: 'center',
240+
},
230241
replyLabel: {
231242
fontSize: theme.font.fontSize.xs,
232243
opacity: 0.7,
233244
},
234-
replyUsername: {
245+
replyPaintedUsername: {
246+
fontSize: theme.font.fontSize.xs,
235247
fontWeight: '600',
236-
opacity: 1,
237248
},
238249
replyMessagePreview: {
239250
fontSize: theme.font.fontSize.sm,

src/components/Chat/components/ChatMessage/CosmeticUsername/CosmeticUsername.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useSelector } from '@legendapp/state/react';
66
import MaskedView from '@react-native-masked-view/masked-view';
77
import { LinearGradient } from 'expo-linear-gradient';
88
import { memo, useMemo } from 'react';
9-
import { View } from 'react-native';
9+
import { type StyleProp, TextStyle, View } from 'react-native';
1010
import Svg, {
1111
Defs,
1212
RadialGradient as SvgRadialGradient,
@@ -32,14 +32,22 @@ interface PaintedUsernameProps {
3232
*/
3333
userId?: string;
3434
fallbackColor?: string;
35+
/**
36+
* When false, omits the trailing ":" (e.g. reply preview). Default true for chat lines.
37+
* */
38+
showColon?: boolean;
39+
usernameTextStyle?: StyleProp<TextStyle>;
3540
}
3641

3742
function PaintedUsernameComponent({
3843
username,
3944
paint: paintProp,
4045
userId,
4146
fallbackColor = '#FFFFFF',
47+
showColon = true,
48+
usernameTextStyle,
4249
}: PaintedUsernameProps) {
50+
const displayUsername = showColon ? `${username}:` : username;
4351
const paintId = useSelector(() =>
4452
userId ? chatStore$.userPaintIds[userId]?.get() : null,
4553
);
@@ -125,7 +133,9 @@ function PaintedUsernameComponent({
125133
</Svg>
126134
</View>
127135
{/* Invisible text to size the gradient correctly */}
128-
<Text style={[styles.hiddenText, shadowStyle]}>{username}:</Text>
136+
<Text style={[styles.hiddenText, usernameTextStyle, shadowStyle]}>
137+
{displayUsername}
138+
</Text>
129139
</View>
130140
);
131141
}
@@ -138,8 +148,9 @@ function PaintedUsernameComponent({
138148
end={gradientConfig.end}
139149
style={styles.gradient}
140150
>
141-
{/* Invisible text to size the gradient correctly */}
142-
<Text style={[styles.hiddenText, shadowStyle]}>{username}:</Text>
151+
<Text style={[styles.hiddenText, usernameTextStyle, shadowStyle]}>
152+
{displayUsername}
153+
</Text>
143154
</LinearGradient>
144155
);
145156
};
@@ -148,7 +159,9 @@ function PaintedUsernameComponent({
148159
<MaskedView
149160
maskElement={
150161
<View style={styles.maskContainer}>
151-
<Text style={[styles.maskText, shadowStyle]}>{username}:</Text>
162+
<Text style={[styles.maskText, usernameTextStyle, shadowStyle]}>
163+
{displayUsername}
164+
</Text>
152165
</View>
153166
}
154167
>

0 commit comments

Comments
 (0)