Skip to content

Commit a71c940

Browse files
committed
feat: codeblock scrollable view
1 parent a959729 commit a71c940

File tree

1 file changed

+75
-3
lines changed

1 file changed

+75
-3
lines changed

package/src/components/Message/MessageSimple/utils/renderText.tsx

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
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
515
import Markdown from 'react-native-markdown-package';
16+
import { runOnJS } from 'react-native-reanimated';
617

718
import {
819
DefaultRules,
@@ -26,7 +37,54 @@ import type { DefaultStreamChatGenerics } from '../../../../types/types';
2637
import { escapeRegExp } from '../../../../utils/utils';
2738
import 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+
2982
const 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

Comments
 (0)