movius-chats 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,7 @@
1
1
  import { ViewStyle } from "react-native";
2
- export declare const CheckAllIcon: ({ style }: {
2
+ interface IconProps {
3
3
  style?: ViewStyle;
4
- }) => import("react/jsx-runtime").JSX.Element;
4
+ fill?: string;
5
+ }
6
+ export declare const CheckAllIcon: ({ style, fill }: IconProps) => import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -1,4 +1,7 @@
1
1
  import { ViewStyle } from "react-native";
2
- export declare const CheckIcon: ({ style }: {
2
+ interface IconProps {
3
3
  style?: ViewStyle;
4
- }) => import("react/jsx-runtime").JSX.Element;
4
+ fill?: string;
5
+ }
6
+ export declare const CheckIcon: ({ style, fill }: IconProps) => import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -1,5 +1,5 @@
1
- import React from "react";
2
- import { StyleProp, TextStyle } from "react-native";
1
+ import React from 'react';
2
+ import { StyleProp, TextStyle } from 'react-native';
3
3
  declare const _default: React.MemoExoticComponent<({ fileName, style, }: {
4
4
  fileName: string;
5
5
  style?: StyleProp<TextStyle>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Returns bottom padding to apply when the software keyboard is open.
3
+ * More reliable than KeyboardAvoidingView alone on Android chat layouts.
4
+ */
5
+ export declare function useKeyboardInset(keyboardVerticalOffset?: number, enabled?: boolean): number;
@@ -20,6 +20,10 @@ export interface ChatScreenProps {
20
20
  onAudioRecordEnd?: () => void;
21
21
  onAudioRecordStart?: () => void;
22
22
  onCameraPress?: () => void;
23
+ /** Extra space subtracted from keyboard height (header, tab bar, safe area). */
24
+ keyboardVerticalOffset?: number;
25
+ /** Set true if your screen already handles keyboard insets. */
26
+ disableKeyboardAvoiding?: boolean;
23
27
  typingUsers?: Array<{
24
28
  id: string;
25
29
  avatar: string;
@@ -44,6 +48,7 @@ export interface ChatScreenProps {
44
48
  uri: string;
45
49
  }>;
46
50
  theme?: {
51
+ fontFamily?: string;
47
52
  colors?: {
48
53
  sentMessageTailColor?: string;
49
54
  receivedMessageTailColor?: string;
@@ -54,6 +59,14 @@ export interface ChatScreenProps {
54
59
  audioPlayIconColor?: string;
55
60
  audioPauseIconColor?: string;
56
61
  videoPlayIconColor?: string;
62
+ inputTextColor?: string;
63
+ sentIconColor?: string;
64
+ deliveredIconColor?: string;
65
+ readIconColor?: string;
66
+ };
67
+ sizes?: {
68
+ /** Twrnc classes (e.g. `"h-8 w-8"`) or pixel size (e.g. `28`). */
69
+ inputIconSize?: string | number;
57
70
  };
58
71
  bubbleStyle?: {
59
72
  sent?: ViewStyle;
@@ -0,0 +1,6 @@
1
+ import { StyleProp, TextStyle, ViewStyle } from 'react-native';
2
+ /** Resolves theme.sizes.inputIconSize (twrnc class string or pixel number). */
3
+ export declare function getInputIconStyle(size?: string | number, extraClass?: string): ViewStyle;
4
+ /** Applies theme.fontFamily to any Text / ParsedText style array. */
5
+ export declare function withFontFamily(style: StyleProp<TextStyle>, fontFamily?: string): StyleProp<TextStyle>;
6
+ export declare function getFontFamilyStyle(fontFamily?: string): TextStyle | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "movius-chats",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "A highly customizable, feature-rich chat interface component for React Native applications",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -1,11 +1,16 @@
1
1
 
2
2
  import { ViewStyle } from "react-native";
3
3
  import Svg, { Path } from "react-native-svg";
4
+ interface IconProps {
5
+ style?: ViewStyle;
6
+ fill?: string;
7
+ }
4
8
 
5
- export const CheckAllIcon = ({ style }: { style?: ViewStyle }) => {
9
+ export const CheckAllIcon = ({ style, fill }: IconProps) => {
6
10
  return (
7
11
  <Svg style={style} viewBox="0 0 24 24">
8
12
  <Path
13
+ fill={fill}
9
14
  d="M.41 13.41L6 19l1.41-1.42L1.83 12m20.41-6.42L11.66 16.17L7.5 12l-1.43 1.41L11.66 19l12-12M18 7l-1.41-1.42l-6.35 6.35l1.42 1.41z"
10
15
  ></Path>
11
16
  </Svg>
@@ -1,11 +1,15 @@
1
1
 
2
2
  import { ViewStyle } from "react-native";
3
3
  import Svg, { Path } from "react-native-svg";
4
+ interface IconProps {
5
+ style?: ViewStyle;
6
+ fill?: string;
7
+ }
4
8
 
5
- export const CheckIcon = ({ style }: { style?: ViewStyle }) => {
9
+ export const CheckIcon = ({ style, fill }: IconProps) => {
6
10
  return (
7
11
  <Svg style={style} viewBox="0 0 24 24">
8
- <Path d="m9.55 18l-5.7-5.7l1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></Path>
12
+ <Path d="m9.55 18l-5.7-5.7l1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z" fill={fill}></Path>
9
13
  </Svg>
10
14
  );
11
15
  };
@@ -12,6 +12,7 @@ import { PlayIcon } from '../../assets/Icons/PlayIcon';
12
12
  import { useAudio } from '../../context/AudioContext';
13
13
  import { useChatContext } from '../../context/ChatContext';
14
14
  import { formatDuration } from '../../utils/datefunc';
15
+ import { withFontFamily } from '../../utils/theme';
15
16
  import { AudioPlayerProps } from './types';
16
17
 
17
18
  const AudioPlayer: React.FC<AudioPlayerProps> = ({
@@ -244,10 +245,13 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({
244
245
  </View>
245
246
  <View style={tw`px-4 py-1`}>
246
247
  <Text
247
- style={[
248
- tw`text-xs text-gray-500`,
249
- theme?.messageStyle?.audioDurationStyle,
250
- ]}
248
+ style={withFontFamily(
249
+ [
250
+ tw`text-xs text-gray-500`,
251
+ theme?.messageStyle?.audioDurationStyle,
252
+ ],
253
+ theme?.fontFamily
254
+ )}
251
255
  >
252
256
  {!isNaN(currentTime) ? formatDuration(currentTime) : '0:00'}
253
257
  </Text>
@@ -3,6 +3,7 @@ import { Image, Pressable, Text, View } from 'react-native';
3
3
  import tw from 'twrnc';
4
4
  import { ArrowBack2RoundedIcon } from '../../assets/Icons/ArrowBack2RoundedIcon';
5
5
  import { useChatContext } from '../../context/ChatContext';
6
+ import { withFontFamily } from '../../utils/theme';
6
7
  import MessageContent from './MessageContent';
7
8
  import MessageStatus from './MessageStatus';
8
9
  import { ChatBubbleProps } from './types';
@@ -71,10 +72,13 @@ const ChatBubble: React.FC<ChatBubbleProps> = ({
71
72
  />
72
73
  ) : (
73
74
  <Text
74
- style={[
75
- tw`text-sm text-black font-semibold capitalize rounded-full bg-zinc-300 w-full h-full text-center pt-0.5`,
76
- theme?.bubbleStyle?.avatarTextStyle,
77
- ]}
75
+ style={withFontFamily(
76
+ [
77
+ tw`text-sm text-black font-semibold capitalize rounded-full bg-zinc-300 w-full h-full text-center pt-0.5`,
78
+ theme?.bubbleStyle?.avatarTextStyle,
79
+ ],
80
+ theme?.fontFamily
81
+ )}
78
82
  >
79
83
  {message.senderName?.charAt(0)}
80
84
  </Text>
@@ -82,10 +86,13 @@ const ChatBubble: React.FC<ChatBubbleProps> = ({
82
86
  </View>
83
87
  {showUserNames && message.senderName && (
84
88
  <Text
85
- style={[
86
- tw`text-sm text-black font-semibold mt-1 capitalize`,
87
- theme?.bubbleStyle?.userNameStyle,
88
- ]}
89
+ style={withFontFamily(
90
+ [
91
+ tw`text-sm text-black font-semibold mt-1 capitalize`,
92
+ theme?.bubbleStyle?.userNameStyle,
93
+ ],
94
+ theme?.fontFamily
95
+ )}
89
96
  >
90
97
  {message.senderName}
91
98
  </Text>
@@ -6,6 +6,7 @@ import { LoadingIcon } from '../../assets/Icons/LoadingIcon';
6
6
  import { PlayIcon } from '../../assets/Icons/PlayIcon';
7
7
  import { useChatContext } from '../../context/ChatContext';
8
8
  import { formatDuration } from '../../utils/datefunc';
9
+ import { getFontFamilyStyle, withFontFamily } from '../../utils/theme';
9
10
  import AudioPlayer from '../AudioPlayer/AudioPlayer';
10
11
  import { MessageContentProps } from './types';
11
12
  import ParsedText from 'react-native-parsed-text';
@@ -86,7 +87,12 @@ const MessageContent: React.FC<MessageContentProps> = ({
86
87
  <View
87
88
  style={tw`absolute inset-0 flex items-center justify-center bg-red-500/60 p-2`}
88
89
  >
89
- <Text style={tw`text-white font-bold`}>
90
+ <Text
91
+ style={withFontFamily(
92
+ tw`text-white font-bold`,
93
+ theme?.fontFamily
94
+ )}
95
+ >
90
96
  Failed to load video
91
97
  </Text>
92
98
  </View>
@@ -127,18 +133,25 @@ const MessageContent: React.FC<MessageContentProps> = ({
127
133
 
128
134
  {message.text && (
129
135
  <ParsedText
130
- style={[
131
- tw`pt-1`,
132
- showMessageStatus ? tw`pb-0` : tw`pb-2`,
133
- { wordBreak: 'break-word', overflowWrap: 'break-word' },
134
- isCurrentUser
135
- ? theme?.messageStyle?.sentTextStyle
136
- : theme?.messageStyle?.receivedTextStyle,
137
- ]}
136
+ style={withFontFamily(
137
+ [
138
+ tw`pt-1`,
139
+ showMessageStatus ? tw`pb-0` : tw`pb-2`,
140
+ { wordBreak: 'break-word', overflowWrap: 'break-word' },
141
+ isCurrentUser
142
+ ? theme?.messageStyle?.sentTextStyle
143
+ : theme?.messageStyle?.receivedTextStyle,
144
+ ],
145
+ theme?.fontFamily
146
+ )}
138
147
  parse={[
139
148
  {
140
149
  type: 'url',
141
- style: { color: 'blue', textDecorationLine: 'underline' },
150
+ style: {
151
+ color: 'blue',
152
+ textDecorationLine: 'underline',
153
+ ...getFontFamilyStyle(theme?.fontFamily),
154
+ },
142
155
  onPress: (url) =>
143
156
  Linking.openURL(
144
157
  url.startsWith('http') ? url : `https://${url}`
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Text, View } from 'react-native';
3
3
  import tw from 'twrnc';
4
+ import { withFontFamily } from '../../utils/theme';
4
5
  import { CheckAllIcon } from '../../assets/Icons/CheckAllIcon';
5
6
  import { CheckIcon } from '../../assets/Icons/CheckIcon';
6
7
  import { useChatContext } from '../../context/ChatContext';
@@ -28,15 +29,18 @@ const MessageStatus: React.FC<MessageStatusProps> = ({
28
29
  ]}
29
30
  >
30
31
  <Text
31
- style={[
32
- tw`text-xs`,
33
- {
34
- color:
35
- hasText || hasAudio
36
- ? theme?.colors?.timestamp || 'rgba(107, 114, 128, 0.7)'
37
- : 'white',
38
- },
39
- ]}
32
+ style={withFontFamily(
33
+ [
34
+ tw`text-xs`,
35
+ {
36
+ color:
37
+ hasText || hasAudio
38
+ ? theme?.colors?.timestamp || 'rgba(107, 114, 128, 0.7)'
39
+ : 'white',
40
+ },
41
+ ],
42
+ theme?.fontFamily
43
+ )}
40
44
  >
41
45
  {time}
42
46
  </Text>
@@ -44,17 +48,20 @@ const MessageStatus: React.FC<MessageStatusProps> = ({
44
48
  <View style={tw`ml-1 flex-row items-center`}>
45
49
  {status === 'sent' && (
46
50
  <CheckIcon
47
- style={tw.style('fill-[#6B7280] h-4 w-4', { opacity: 0.7 })}
51
+ style={tw.style('h-4 w-4', { opacity: 0.7 })}
52
+ fill={theme?.colors?.sentIconColor || '#6B7280'}
48
53
  />
49
54
  )}
50
55
  {status === 'delivered' && (
51
56
  <CheckAllIcon
52
- style={tw.style('fill-[#6B7280] h-4 w-4', { opacity: 0.7 })}
57
+ style={tw.style('h-4 w-4', { opacity: 0.7 })}
58
+ fill={theme?.colors?.deliveredIconColor || '#6B7280'}
53
59
  />
54
60
  )}
55
61
  {status === 'read' && (
56
62
  <CheckAllIcon
57
- style={tw.style('fill-[#3B82F6] h-4 w-4', { opacity: 0.9 })}
63
+ style={tw.style('h-4 w-4', { opacity: 0.9 })}
64
+ fill={theme?.colors?.readIconColor || '#3B82F6'}
58
65
  />
59
66
  )}
60
67
  </View>
@@ -7,6 +7,7 @@ import { MicrophoneIcon } from '../../assets/Icons/MicrophoneIcon';
7
7
  import { PaperClipIcon } from '../../assets/Icons/PaperClipIcon';
8
8
  import { PaperPlaneIcon } from '../../assets/Icons/PaperPlaneIcon';
9
9
  import { useChatContext } from '../../context/ChatContext';
10
+ import { getInputIconStyle, withFontFamily } from '../../utils/theme';
10
11
  import FilePreview from './FilePreview';
11
12
  import { ChatInputProps, InputHeightState } from './types';
12
13
 
@@ -47,6 +48,13 @@ const ChatInput: React.FC<ChatInputProps> = ({
47
48
  closePreview,
48
49
  } = useChatContext();
49
50
 
51
+ const inputIconStyle = getInputIconStyle(
52
+ theme?.sizes?.inputIconSize,
53
+ inputHeight.isMultiline ? 'pb-14' : undefined
54
+ );
55
+ const inputIconStyleInline = getInputIconStyle(theme?.sizes?.inputIconSize);
56
+ const sendIconStyle = getInputIconStyle(theme?.sizes?.inputIconSize);
57
+
50
58
  const handleContentSizeChange = useCallback(
51
59
  (event: { nativeEvent: { contentSize: { height: number } } }) => {
52
60
  const newHeight = Math.min(
@@ -87,7 +95,6 @@ const ChatInput: React.FC<ChatInputProps> = ({
87
95
 
88
96
  return (
89
97
  <View style={tw`w-full px-2`}>
90
- {/* File Preview above the input */}
91
98
  {previewData && (
92
99
  <FilePreview
93
100
  previewData={previewData}
@@ -105,7 +112,6 @@ const ChatInput: React.FC<ChatInputProps> = ({
105
112
  theme?.inputStyles?.inputSectionContainerStyle,
106
113
  ]}
107
114
  >
108
- {/* LEFT: Input and icons */}
109
115
  <View
110
116
  style={[
111
117
  tw`flex-1 px-3.5 bg-white gap-1 flex-row justify-between`,
@@ -121,10 +127,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
121
127
  <CustomEmojiIcon />
122
128
  ) : (
123
129
  <EmojiFunnySquareIcon
124
- style={tw.style(
125
- `${Platform.OS === 'ios' ? 'h-6 w-6' : 'w-7 h-7'}`,
126
- inputHeight.isMultiline ? 'pb-14' : 'pb-0'
127
- )}
130
+ style={inputIconStyle}
128
131
  color={theme?.colors?.inputsIconsColor || 'rgba(0,0,0,0.7)'}
129
132
  />
130
133
  )}
@@ -135,11 +138,18 @@ const ChatInput: React.FC<ChatInputProps> = ({
135
138
  value={inputText}
136
139
  onChangeText={setInputText}
137
140
  placeholder={placeholder || 'Message'}
138
- style={[
139
- tw`bg-transparent flex-1 pl-2 my-3`,
140
- Platform.OS === 'ios' ? tw`text-[17px]` : tw`text-[16px]`,
141
- { minHeight: MIN_INPUT_HEIGHT, maxHeight: MAX_INPUT_HEIGHT },
142
- ]}
141
+ style={withFontFamily(
142
+ [
143
+ tw`bg-transparent flex-1 pl-2 my-3`,
144
+ Platform.OS === 'ios' ? tw`text-[17px]` : tw`text-[16px]`,
145
+ { minHeight: MIN_INPUT_HEIGHT, maxHeight: MAX_INPUT_HEIGHT },
146
+ {
147
+ color:
148
+ theme?.colors?.inputTextColor || 'rgba(0, 0, 0, 0.87)',
149
+ },
150
+ ],
151
+ theme?.fontFamily
152
+ )}
143
153
  placeholderTextColor={
144
154
  theme?.colors?.placeholderTextColor || 'rgba(0, 0, 0, 0.4)'
145
155
  }
@@ -160,9 +170,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
160
170
  <CustomAttachmentIcon />
161
171
  ) : (
162
172
  <PaperClipIcon
163
- style={tw.style(
164
- Platform.OS === 'ios' ? 'h-6 w-6' : 'w-7 h-7'
165
- )}
173
+ style={inputIconStyleInline}
166
174
  color={theme?.colors?.inputsIconsColor || 'rgba(0,0,0,0.7)'}
167
175
  />
168
176
  )}
@@ -174,9 +182,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
174
182
  <CustomCameraIcon />
175
183
  ) : (
176
184
  <CameraIcon
177
- style={tw.style(
178
- Platform.OS === 'ios' ? 'h-6 w-6' : 'w-7 h-7'
179
- )}
185
+ style={inputIconStyleInline}
180
186
  color={theme?.colors?.inputsIconsColor || 'rgba(0,0,0,0.7)'}
181
187
  />
182
188
  )}
@@ -185,7 +191,6 @@ const ChatInput: React.FC<ChatInputProps> = ({
185
191
  </View>
186
192
  </View>
187
193
 
188
- {/* RIGHT: Send / Mic Button always at bottom */}
189
194
  <Pressable
190
195
  style={[
191
196
  tw`p-2 rounded-full bg-green-600 justify-center items-center`,
@@ -208,7 +213,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
208
213
  <CustomSendIcon />
209
214
  ) : (
210
215
  <PaperPlaneIcon
211
- style={tw.style('h-6 w-6')}
216
+ style={sendIconStyle}
212
217
  color={theme?.colors?.sendIconsColor || 'rgba(255,255,255,0.7)'}
213
218
  />
214
219
  )
@@ -217,7 +222,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
217
222
  <CustomMicrophoneIcon />
218
223
  ) : (
219
224
  <MicrophoneIcon
220
- style={tw.style('h-8 w-8')}
225
+ style={sendIconStyle}
221
226
  color={theme?.colors?.sendIconsColor || 'rgba(255,255,255,0.7)'}
222
227
  />
223
228
  )
@@ -225,7 +230,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
225
230
  <CustomSendIcon />
226
231
  ) : (
227
232
  <PaperPlaneIcon
228
- style={tw.style('h-6 w-6')}
233
+ style={sendIconStyle}
229
234
  color={theme?.colors?.sendIconsColor || 'rgba(255,255,255,0.7)'}
230
235
  />
231
236
  )}
@@ -233,7 +238,6 @@ const ChatInput: React.FC<ChatInputProps> = ({
233
238
  </View>
234
239
  </View>
235
240
  );
236
-
237
241
  };
238
242
 
239
243
  export default React.memo(ChatInput);
@@ -3,6 +3,7 @@ import { Image, Pressable, Text, View } from 'react-native';
3
3
  import Video from 'react-native-video';
4
4
  import { FileIcon } from '../../assets/Icons/FileIcon';
5
5
  import { useChatContext } from '../../context/ChatContext';
6
+ import { withFontFamily } from '../../utils/theme';
6
7
  import TruncateFileName from './TruncateFileName';
7
8
  import { ChatInputProps } from './types';
8
9
 
@@ -57,7 +58,14 @@ const FilePreview: React.FC<Omit<ChatInputProps, 'onSendMessage'>> = ({
57
58
  alignItems: 'center',
58
59
  }}
59
60
  >
60
- <Text style={{ fontSize: 12, color: 'black' }}>X</Text>
61
+ <Text
62
+ style={withFontFamily(
63
+ { fontSize: 12, color: 'black' },
64
+ theme?.fontFamily
65
+ )}
66
+ >
67
+ X
68
+ </Text>
61
69
  </Pressable>
62
70
 
63
71
  {isImage ? (
@@ -1,5 +1,7 @@
1
- import React from "react";
2
- import { StyleProp, Text, TextStyle } from "react-native";
1
+ import React from 'react';
2
+ import { StyleProp, Text, TextStyle } from 'react-native';
3
+ import { useChatContext } from '../../context/ChatContext';
4
+ import { withFontFamily } from '../../utils/theme';
3
5
 
4
6
  const TruncateFileName = ({
5
7
  fileName,
@@ -8,6 +10,7 @@ const TruncateFileName = ({
8
10
  fileName: string;
9
11
  style?: StyleProp<TextStyle>;
10
12
  }) => {
13
+ const { theme } = useChatContext();
11
14
  const getTruncatedName = (fullName: string) => {
12
15
  const lastDot = fullName.lastIndexOf('.');
13
16
  if (lastDot === -1) return fullName;
@@ -23,7 +26,10 @@ const TruncateFileName = ({
23
26
  <Text
24
27
  numberOfLines={2}
25
28
  ellipsizeMode="tail"
26
- style={[{ fontSize: 14, fontWeight: '600', color: 'black' }, style]}
29
+ style={withFontFamily(
30
+ [{ fontSize: 14, fontWeight: '600', color: 'black' }, style],
31
+ theme?.fontFamily
32
+ )}
27
33
  >
28
34
  {getTruncatedName(fileName)}
29
35
  </Text>
@@ -5,6 +5,8 @@ import Video, { VideoRef } from 'react-native-video';
5
5
  import tw from 'twrnc';
6
6
  import { LoadingIcon } from '../../assets/Icons/LoadingIcon';
7
7
  import { XIcon } from '../../assets/Icons/XIcon';
8
+ import { useChatContext } from '../../context/ChatContext';
9
+ import { withFontFamily } from '../../utils/theme';
8
10
  import { MediaViewerProps } from './types';
9
11
 
10
12
  const MediaViewer: React.FC<MediaViewerProps> = ({
@@ -12,6 +14,7 @@ const MediaViewer: React.FC<MediaViewerProps> = ({
12
14
  videoUrl,
13
15
  onClose,
14
16
  }) => {
17
+ const { theme } = useChatContext();
15
18
  const videoRef = useRef<VideoRef>(null);
16
19
  const [videoIsLoading, setVideoIsLoading] = useState(false);
17
20
  const [videoHasError, setVideoHasError] = useState(false);
@@ -84,7 +87,12 @@ const MediaViewer: React.FC<MediaViewerProps> = ({
84
87
  <View
85
88
  style={tw`absolute inset-0 flex items-center justify-center bg-red-500/60 p-2`}
86
89
  >
87
- <Text style={tw`text-white font-bold`}>
90
+ <Text
91
+ style={withFontFamily(
92
+ tw`text-white font-bold`,
93
+ theme?.fontFamily
94
+ )}
95
+ >
88
96
  Failed to load video
89
97
  </Text>
90
98
  </View>
@@ -2,6 +2,7 @@ import { Image, Text, View } from 'react-native';
2
2
  import tw from 'twrnc';
3
3
  import { ArrowBack2RoundedIcon } from '../../assets/Icons/ArrowBack2RoundedIcon';
4
4
  import { useChatContext } from '../../context/ChatContext';
5
+ import { withFontFamily } from '../../utils/theme';
5
6
 
6
7
  export interface TypingUser {
7
8
  id: string;
@@ -55,10 +56,13 @@ export const TypingIndicator = ({
55
56
  />
56
57
  ) : (
57
58
  <Text
58
- style={[
59
- tw`text-sm text-black font-semibold capitalize rounded-full bg-zinc-300 w-full h-full text-center pt-0.5`,
60
- theme?.bubbleStyle?.avatarTextStyle,
61
- ]}
59
+ style={withFontFamily(
60
+ [
61
+ tw`text-sm text-black font-semibold capitalize rounded-full bg-zinc-300 w-full h-full text-center pt-0.5`,
62
+ theme?.bubbleStyle?.avatarTextStyle,
63
+ ],
64
+ theme?.fontFamily
65
+ )}
62
66
  >
63
67
  {user.name?.charAt(0)}
64
68
  </Text>
@@ -77,10 +81,13 @@ export const TypingIndicator = ({
77
81
  ]}
78
82
  >
79
83
  <Text
80
- style={[
81
- tw`text-white text-xs font-semibold`,
82
- theme?.bubbleStyle?.additionalTypingUsersTextStyle,
83
- ]}
84
+ style={withFontFamily(
85
+ [
86
+ tw`text-white text-xs font-semibold`,
87
+ theme?.bubbleStyle?.additionalTypingUsersTextStyle,
88
+ ],
89
+ theme?.fontFamily
90
+ )}
84
91
  >
85
92
  +{additionalUsers}
86
93
  </Text>
@@ -107,7 +114,9 @@ export const TypingIndicator = ({
107
114
  renderCustomTyping()
108
115
  ) : (
109
116
  <View style={tw`flex-row items-center py-3 px-2 justify-center`}>
110
- <Text style={tw`text-gray-600`}>Typing...</Text>
117
+ <Text style={withFontFamily(tw`text-gray-600`, theme?.fontFamily)}>
118
+ Typing...
119
+ </Text>
111
120
  </View>
112
121
  )}
113
122
  </View>
@@ -0,0 +1,42 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { Keyboard, KeyboardEvent, Platform } from 'react-native';
3
+
4
+ /**
5
+ * Returns bottom padding to apply when the software keyboard is open.
6
+ * More reliable than KeyboardAvoidingView alone on Android chat layouts.
7
+ */
8
+ export function useKeyboardInset(
9
+ keyboardVerticalOffset = 0,
10
+ enabled = true
11
+ ) {
12
+ const [inset, setInset] = useState(0);
13
+
14
+ useEffect(() => {
15
+ if (!enabled) {
16
+ setInset(0);
17
+ return;
18
+ }
19
+
20
+ const showEvent =
21
+ Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
22
+ const hideEvent =
23
+ Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
24
+
25
+ const onShow = (event: KeyboardEvent) => {
26
+ const height = event.endCoordinates.height;
27
+ setInset(Math.max(0, height - keyboardVerticalOffset));
28
+ };
29
+
30
+ const onHide = () => setInset(0);
31
+
32
+ const showSub = Keyboard.addListener(showEvent, onShow);
33
+ const hideSub = Keyboard.addListener(hideEvent, onHide);
34
+
35
+ return () => {
36
+ showSub.remove();
37
+ hideSub.remove();
38
+ };
39
+ }, [keyboardVerticalOffset, enabled]);
40
+
41
+ return enabled ? inset : 0;
42
+ }