1- import React , { PropsWithChildren } from 'react' ;
2- import { GestureResponderEvent , Linking , Text , TextProps , View , ViewProps } from 'react-native' ;
3-
1+ import React , { PropsWithChildren , ReactNode , useCallback , useRef , useState } from 'react' ;
2+ import {
3+ GestureResponderEvent ,
4+ Linking ,
5+ Platform ,
6+ ScrollView ,
7+ Text ,
8+ TextProps ,
9+ View ,
10+ ViewProps ,
11+ } from 'react-native' ;
12+
13+ import { Gesture , GestureDetector } from 'react-native-gesture-handler' ;
414// @ts -expect-error
515import Markdown from 'react-native-markdown-package' ;
16+ import { runOnJS } from 'react-native-reanimated' ;
617
718import {
819 DefaultRules ,
@@ -26,7 +37,54 @@ import type { DefaultStreamChatGenerics } from '../../../../types/types';
2637import { escapeRegExp } from '../../../../utils/utils' ;
2738import type { MessageType } from '../../../MessageList/hooks/useMessageList' ;
2839
40+ const ReactiveScrollView = ( { children } : { children : ReactNode } ) => {
41+ const [ scrollViewXOffset , setScrollViewXOffset ] = useState ( 0 ) ;
42+ const scrollViewRef = useRef < ScrollView > ( null ) ;
43+
44+ const scrollTo = useCallback ( ( translation : number ) => {
45+ if ( scrollViewRef . current ) {
46+ scrollViewRef . current . scrollTo ( {
47+ animated : false ,
48+ x : translation ,
49+ } ) ;
50+ }
51+ } , [ ] ) ;
52+
53+ const panGesture = Gesture . Pan ( )
54+ . activeOffsetX ( [ - 10 , 10 ] )
55+ . onUpdate ( ( event ) => {
56+ const { translationX } = event ;
57+
58+ if ( scrollViewRef . current ) {
59+ runOnJS ( scrollTo ) ( scrollViewXOffset - translationX ) ;
60+ }
61+ } )
62+ . onEnd ( ( event ) => {
63+ const { translationX } = event ;
64+
65+ runOnJS ( setScrollViewXOffset ) ( scrollViewXOffset - translationX ) ;
66+ } ) ;
67+
68+ return (
69+ < GestureDetector gesture = { panGesture } >
70+ < ScrollView
71+ contentContainerStyle = { { flexGrow : 1 } }
72+ horizontal
73+ nestedScrollEnabled = { true }
74+ ref = { scrollViewRef }
75+ >
76+ { children }
77+ </ ScrollView >
78+ </ GestureDetector >
79+ ) ;
80+ } ;
81+
2982const defaultMarkdownStyles : MarkdownStyle = {
83+ codeBlock : {
84+ backgroundColor : '#DDDDDD' ,
85+ fontFamily : Platform . OS === 'ios' ? 'Courier' : 'Monospace' ,
86+ fontWeight : '500' ,
87+ } ,
3088 inlineCode : {
3189 fontSize : 13 ,
3290 padding : 3 ,
@@ -113,6 +171,11 @@ export const renderText = <
113171 color : colors . accent_blue ,
114172 ...markdownStyles ?. autolink ,
115173 } ,
174+ codeBlock : {
175+ ...defaultMarkdownStyles . codeBlock ,
176+ padding : 8 ,
177+ ...markdownStyles ?. codeBlock ,
178+ } ,
116179 inlineCode : {
117180 ...defaultMarkdownStyles . inlineCode ,
118181 backgroundColor : colors . white_smoke ,
@@ -263,6 +326,14 @@ export const renderText = <
263326 />
264327 ) ;
265328
329+ const CodeBlockReact : ReactNodeOutput = ( node , _ , state ) => (
330+ < ReactiveScrollView >
331+ < Text key = { state . key } style = { styles . codeBlock } >
332+ { node . content }
333+ </ Text >
334+ </ ReactiveScrollView >
335+ ) ;
336+
266337 const customRules = {
267338 // do not render images, we will scrape them out of the message and show on attachment card component
268339 image : { match : ( ) => null } ,
@@ -283,6 +354,7 @@ export const renderText = <
283354 } ,
284355 }
285356 : { } ) ,
357+ codeBlock : { react : CodeBlockReact } ,
286358 } ;
287359
288360 return (
0 commit comments