1- import React from 'react' ;
2- import {
3- View ,
4- Text ,
5- StyleSheet ,
6- TouchableOpacity ,
7- } from 'react-native' ;
8- import { Ionicons } from '@expo/vector-icons' ;
9- import Markdown from 'react-native-markdown-display' ;
10- import { Message } from '../../types/api' ;
11- import { spacing , fontSize , borderRadius } from '../../constants/theme' ;
12- import { useTheme } from '../../contexts/ThemeContext' ;
1+ import React from "react" ;
2+ import { View , Text , StyleSheet , Linking } from "react-native" ;
3+ import Markdown from "react-native-markdown-display" ;
4+ import { ThinkingAnimation } from "./ThinkingAnimation" ;
5+ import { Message } from "../../types/api" ;
6+ import { spacing , fontSize , borderRadius } from "../../constants/theme" ;
7+ import { useTheme } from "../../contexts/ThemeContext" ;
138
149interface MessageBubbleProps {
1510 message : Message ;
16- onCopy ?: ( ) => void ;
1711}
1812
19- export const MessageBubble : React . FC < MessageBubbleProps > = ( {
20- message,
21- onCopy,
22- } ) => {
13+ export const MessageBubble : React . FC < MessageBubbleProps > = ( { message } ) => {
2314 const { colors } = useTheme ( ) ;
24- const isUser = message . role === ' user' ;
15+ const isUser = message . role === " user" ;
2516 const styles = createStyles ( colors , isUser ) ;
2617
2718 return (
28- < View style = { [
29- styles . container ,
30- isUser ? styles . userContainer : styles . agentContainer ,
31- ] } >
19+ < View
20+ style = { [
21+ styles . container ,
22+ isUser ? styles . userContainer : styles . agentContainer ,
23+ ] }
24+ >
3225 < View style = { styles . bubble } >
3326 { isUser ? (
34- < Text style = { [
35- styles . messageText ,
36- styles . userText ,
37- ] } >
27+ < Text style = { [ styles . messageText , styles . userText ] } >
3828 { message . content }
3929 </ Text >
40- ) : (
30+ ) : message . content ? (
4131 < Markdown
4232 style = { {
4333 body : {
@@ -62,14 +52,15 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({
6252 } ,
6353 strong : {
6454 color : colors . foreground ,
65- fontWeight : ' 700' ,
55+ fontWeight : " 700" ,
6656 } ,
6757 em : {
6858 color : colors . foreground ,
69- fontStyle : ' italic' ,
59+ fontStyle : " italic" ,
7060 } ,
7161 link : {
7262 color : colors . primary ,
63+ textDecorationLine : "underline" ,
7364 } ,
7465 blockquote : {
7566 backgroundColor : colors . muted ,
@@ -81,19 +72,19 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({
8172 } ,
8273 heading1 : {
8374 fontSize : fontSize . xl ,
84- fontWeight : ' bold' ,
75+ fontWeight : " bold" ,
8576 color : colors . foreground ,
8677 marginVertical : spacing . xs ,
8778 } ,
8879 heading2 : {
8980 fontSize : fontSize . lg ,
90- fontWeight : ' bold' ,
81+ fontWeight : " bold" ,
9182 color : colors . foreground ,
9283 marginVertical : spacing . xs ,
9384 } ,
9485 heading3 : {
9586 fontSize : fontSize . base ,
96- fontWeight : ' bold' ,
87+ fontWeight : " bold" ,
9788 color : colors . foreground ,
9889 marginVertical : spacing . xs ,
9990 } ,
@@ -102,78 +93,62 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({
10293 fontSize : fontSize . base ,
10394 } ,
10495 } }
96+ onLinkPress = { ( url ) => {
97+ Linking . openURL ( url ) . catch ( ( ) => {
98+ // Failed to open link
99+ } ) ;
100+ return false ;
101+ } }
105102 >
106103 { message . content }
107104 </ Markdown >
108- ) }
109-
110- { ! isUser && onCopy && (
111- < TouchableOpacity
112- style = { styles . copyButton }
113- onPress = { onCopy }
114- hitSlop = { { top : 8 , bottom : 8 , left : 8 , right : 8 } }
115- >
116- < Ionicons
117- name = "copy-outline"
118- size = { 16 }
119- color = { colors . mutedForeground }
120- />
121- </ TouchableOpacity >
105+ ) : (
106+ < ThinkingAnimation />
122107 ) }
123108 </ View >
124-
125- < Text style = { [
126- styles . timestamp ,
127- { color : colors . mutedForeground }
128- ] } >
129- { new Date ( message . createdAt ) . toLocaleTimeString ( [ ] , {
130- hour : '2-digit' ,
131- minute : '2-digit'
109+
110+ < Text style = { [ styles . timestamp , { color : colors . mutedForeground } ] } >
111+ { new Date ( message . createdAt ) . toLocaleTimeString ( [ ] , {
112+ hour : "2-digit" ,
113+ minute : "2-digit" ,
132114 } ) }
133115 </ Text >
134116 </ View >
135117 ) ;
136118} ;
137119
138- const createStyles = ( colors : any , isUser : boolean ) => StyleSheet . create ( {
139- container : {
140- marginVertical : spacing . xs ,
141- paddingHorizontal : spacing . md ,
142- } ,
143- userContainer : {
144- alignItems : 'flex-end' ,
145- } ,
146- agentContainer : {
147- alignItems : 'flex-start' ,
148- } ,
149- bubble : {
150- maxWidth : '85%' ,
151- paddingHorizontal : spacing . md ,
152- paddingVertical : spacing . sm ,
153- borderRadius : borderRadius . lg ,
154- position : 'relative' ,
155- backgroundColor : isUser ? colors . primary : colors . secondary ,
156- borderBottomRightRadius : isUser ? 6 : borderRadius . lg ,
157- borderBottomLeftRadius : isUser ? borderRadius . lg : 6 ,
158- } ,
159- messageText : {
160- fontSize : fontSize . base ,
161- lineHeight : 22 ,
162- } ,
163- userText : {
164- color : colors . primaryForeground ,
165- } ,
166- copyButton : {
167- position : 'absolute' ,
168- top : spacing . xs ,
169- right : spacing . xs ,
170- padding : spacing . xs ,
171- backgroundColor : colors . background ,
172- borderRadius : 12 ,
173- } ,
174- timestamp : {
175- fontSize : fontSize . xs ,
176- marginTop : spacing . xs ,
177- marginHorizontal : spacing . xs ,
178- } ,
179- } ) ;
120+ const createStyles = ( colors : any , isUser : boolean ) =>
121+ StyleSheet . create ( {
122+ container : {
123+ marginVertical : spacing . xs ,
124+ paddingHorizontal : spacing . md ,
125+ } ,
126+ userContainer : {
127+ alignItems : "flex-end" ,
128+ } ,
129+ agentContainer : {
130+ alignItems : "flex-start" ,
131+ } ,
132+ bubble : {
133+ maxWidth : "85%" ,
134+ paddingHorizontal : spacing . md ,
135+ paddingVertical : spacing . sm ,
136+ borderRadius : borderRadius . lg ,
137+ position : "relative" ,
138+ backgroundColor : isUser ? colors . primary : colors . secondary ,
139+ borderBottomRightRadius : isUser ? 6 : borderRadius . lg ,
140+ borderBottomLeftRadius : isUser ? borderRadius . lg : 6 ,
141+ } ,
142+ messageText : {
143+ fontSize : fontSize . base ,
144+ lineHeight : 22 ,
145+ } ,
146+ userText : {
147+ color : colors . primaryForeground ,
148+ } ,
149+ timestamp : {
150+ fontSize : fontSize . xs ,
151+ marginTop : spacing . xs ,
152+ marginHorizontal : spacing . xs ,
153+ } ,
154+ } ) ;
0 commit comments