@smart-link/rn-im 1.1.1 → 1.1.3

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.
Files changed (35) hide show
  1. package/dist/components/Camera/Camera.js +1 -2
  2. package/dist/components/Camera/CameraCapture.js +7 -2
  3. package/dist/components/ChatAvatar/ChatAvatarLocal.js +4 -0
  4. package/dist/components/Highlighter.d.ts +2 -1
  5. package/dist/components/Highlighter.js +2 -2
  6. package/dist/components/LocalImage.js +1 -0
  7. package/dist/pages/conversation/{List.d.ts → ConversationList.d.ts} +2 -2
  8. package/dist/pages/conversation/{List.js → ConversationList.js} +23 -4
  9. package/dist/pages/conversation/conversation.routes.js +2 -2
  10. package/dist/pages/conversation/setting/GroupTransfer.js +9 -2
  11. package/dist/pages/conversation/setting/Setting.js +17 -5
  12. package/dist/pages/message/ChooseMember.js +8 -11
  13. package/dist/pages/message/FileSelector.js +3 -3
  14. package/dist/pages/message/MessageList.js +3 -3
  15. package/dist/pages/message/components/MessagePayload.js +4 -3
  16. package/dist/pages/message/components/Payload/PayloadNotify.js +4 -1
  17. package/dist/pages/message/components/Payload/PayloadText.js +1 -1
  18. package/dist/pages/message/components/Payload/PayloadWrapper.d.ts +1 -0
  19. package/dist/pages/message/components/Payload/PayloadWrapper.js +4 -1
  20. package/dist/pages/message/components/messageBar/EmojiPanel.d.ts +2 -2
  21. package/dist/pages/message/components/messageBar/EmojiPanel.js +22 -5
  22. package/dist/pages/message/components/messageBar/MessageBar.js +31 -19
  23. package/dist/pages/message/components/messageBar/MessageInput.d.ts +5 -7
  24. package/dist/pages/message/components/messageBar/MessageInput.js +2 -2
  25. package/dist/pages/message/components/messageBar/OptionPanel.d.ts +2 -3
  26. package/dist/pages/message/components/messageBar/OptionPanel.js +22 -5
  27. package/dist/pages/search/components/SearchUser.js +7 -2
  28. package/dist/utils/event.d.ts +13 -0
  29. package/dist/utils/event.js +32 -0
  30. package/dist/utils/file.d.ts +2 -0
  31. package/dist/utils/file.js +12 -2
  32. package/dist/utils/request.d.ts +2 -1
  33. package/dist/utils/request.js +4 -1
  34. package/dist/utils/upload.js +37 -7
  35. package/package.json +6 -3
@@ -44,8 +44,7 @@ const Camera = memo(props => {
44
44
  }
45
45
  }
46
46
  });
47
- return (<Modal animationType="slide" transparent={false} visible={true} onRequestClose={() => {
48
- }}>
47
+ return (<Modal animationType="slide" visible={true} transparent statusBarTranslucent onRequestClose={cancelCamera}>
49
48
  {Platform.OS === 'android' &&
50
49
  <StatusBar backgroundColor={'#000'}/>}
51
50
  {resultSource &&
@@ -33,7 +33,12 @@ const CameraCapture = memo(props => {
33
33
  const microphone = useMicrophonePermission();
34
34
  const device = useCameraDevice(CameraTypeValue[cameraTypeIndex]);
35
35
  const format = useCameraFormat(device, [
36
- { videoAspectRatio: SCREEN_HEIGHT / SCREEN_WIDTH, fps: 60, photoResolution: 'max' },
36
+ {
37
+ videoAspectRatio: SCREEN_HEIGHT / SCREEN_WIDTH,
38
+ fps: 30,
39
+ photoResolution: 'max',
40
+ videoHdr: false
41
+ },
37
42
  {
38
43
  photoAspectRatio: SCREEN_HEIGHT / SCREEN_WIDTH,
39
44
  photoResolution: 'max',
@@ -148,7 +153,7 @@ const CameraCapture = memo(props => {
148
153
  };
149
154
  }, []);
150
155
  return (<View style={styles.camera}>
151
- {device ? (<Camera ref={camera} device={device} format={format} isActive={true} style={styles.preview} photo={true} video={true} audio={microphone.hasPermission} outputOrientation="portrait" onInitialized={() => {
156
+ {device ? (<Camera ref={camera} device={device} format={format} isActive={true} style={styles.preview} photo={true} video={true} pixelFormat={'yuv'} audio={microphone.hasPermission} outputOrientation="portrait" onInitialized={() => {
152
157
  console.log('Camera initialized!');
153
158
  }} onError={error => {
154
159
  console.log(error);
@@ -99,6 +99,10 @@ const ChatAvatarLocal = ({ fileId, size, name, defaultAvatar }) => {
99
99
  return (<LocalImage localPath={avatar} style={size ? { width: size, height: size, borderRadius: size / 2 } : { width: 40, height: 40, borderRadius: 20 }}/>);
100
100
  }
101
101
  else {
102
+ // console.log(
103
+ // '[ChatAvatarLocal] defaultAvatar:',
104
+ // defaultAvatar
105
+ // );
102
106
  return (<Avatar icon={<Image style={{ width: size, height: size }} source={defaultAvatar}/>} style={size ? { width: size, height: size } : {}}>
103
107
  {name === null || name === void 0 ? void 0 : name[0]}
104
108
  </Avatar>);
@@ -4,6 +4,7 @@ type HighlighterProps = {
4
4
  style?: TextStyle;
5
5
  searchWord: string;
6
6
  textToHighlight: string;
7
+ numberOfLines?: number;
7
8
  };
8
- declare const _default: React.MemoExoticComponent<({ style, searchWord, textToHighlight }: HighlighterProps) => React.JSX.Element>;
9
+ declare const _default: React.MemoExoticComponent<({ style, searchWord, textToHighlight, numberOfLines }: HighlighterProps) => React.JSX.Element>;
9
10
  export default _default;
@@ -1,10 +1,10 @@
1
1
  import { useTheme } from '@smart-link/rn-ui';
2
2
  import React, { memo } from 'react';
3
3
  import { Text } from 'react-native';
4
- const Highlighter = ({ style, searchWord, textToHighlight }) => {
4
+ const Highlighter = ({ style, searchWord, textToHighlight, numberOfLines }) => {
5
5
  const theme = useTheme();
6
6
  // 高亮显示搜索关键字
7
- return (<Text style={style}>
7
+ return (<Text style={style} numberOfLines={numberOfLines}>
8
8
  {textToHighlight.split(new RegExp(`(${searchWord})`, 'gi')).map((part, index) => {
9
9
  return part.toLowerCase() === searchWord.toLowerCase() ? (<Text key={index} style={{
10
10
  color: theme.primaryColor,
@@ -7,6 +7,7 @@ const LocalImage = memo(props => {
7
7
  if (Object.prototype.toString.call(localPath) === '[object String]') {
8
8
  source = { uri: toAbsolutePath(localPath) };
9
9
  }
10
+ // console.log('LocalImage: ', source);
10
11
  return <Image style={style} resizeMode={resizeMode} source={source} onError={(e) => {
11
12
  console.log('local image error: ', localPath);
12
13
  }}/>;
@@ -2,5 +2,5 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack/lib/types
2
2
  import React from 'react';
3
3
  import { IMPageParamList } from '../../pages';
4
4
  type ConversationListProps = NativeStackScreenProps<IMPageParamList, 'ConversationList'>;
5
- declare const List: ({ navigation }: ConversationListProps) => React.JSX.Element;
6
- export default List;
5
+ declare const ConversationList: ({ navigation }: ConversationListProps) => React.JSX.Element;
6
+ export default ConversationList;
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { ConnectStatus, ConversationActions } from '@smart-link/im-base';
11
11
  import { CaihIcon, dp, Popover, SearchBar } from '@smart-link/rn-ui';
12
- import React, { useEffect, useRef, useState } from 'react';
12
+ import React, { memo, useEffect, useRef, useState } from 'react';
13
13
  import { StyleSheet, View, FlatList, TouchableOpacity, Vibration, DevSettings, } from 'react-native';
14
14
  import IndicatorText from '../../components/IndicatorText';
15
15
  import NetworkUnconnected from '../../components/NetworkUnconnected';
@@ -20,7 +20,13 @@ import ConversationCard from './components/ConversationCard';
20
20
  import ConversationOption from './components/ConversationOption';
21
21
  import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
22
22
  import { startCreateGroup } from '../../slice/contact/contact.slice';
23
- const List = ({ navigation }) => {
23
+ import AntDesign from "react-native-vector-icons/AntDesign";
24
+ const BackButton = memo(({ onBack }) => {
25
+ return (<TouchableOpacity style={styles.backButton} onPress={onBack}>
26
+ <AntDesign name='up' size={dp(20)} color={'#999'}/>
27
+ </TouchableOpacity>);
28
+ });
29
+ const ConversationList = ({ navigation }) => {
24
30
  const { t } = useTranslation();
25
31
  const { connectStatus, conversations } = useConversation();
26
32
  const [nativeEvent, setNativeEvent] = useState();
@@ -29,6 +35,7 @@ const List = ({ navigation }) => {
29
35
  const lastPressTime = useRef(0);
30
36
  const lastIndex = useRef(0);
31
37
  const flatListRef = useRef(null);
38
+ const [scrollY, setScrollY] = useState(0);
32
39
  useEffect(() => {
33
40
  let headerTitle = t('message');
34
41
  switch (connectStatus) {
@@ -173,11 +180,23 @@ const List = ({ navigation }) => {
173
180
  index,
174
181
  });
175
182
  return (<View style={{ flex: 1 }}>
176
- <FlatList ref={flatListRef} contentContainerStyle={styles.contentContainerStyle} keyboardShouldPersistTaps={'handled'} data={conversations} ListHeaderComponent={listHeaderComponent} ListFooterComponent={listFooterComponent} ItemSeparatorComponent={itemSeparatorComponent} getItemLayout={getItemLayout} renderItem={renderItem} numColumns={1} keyExtractor={keyExtractor}/>
183
+ <FlatList ref={flatListRef} contentContainerStyle={styles.contentContainerStyle} keyboardShouldPersistTaps={'handled'} data={conversations} ListHeaderComponent={listHeaderComponent} ListFooterComponent={listFooterComponent} ItemSeparatorComponent={itemSeparatorComponent} getItemLayout={getItemLayout} renderItem={renderItem} numColumns={1} keyExtractor={keyExtractor} onScroll={(e) => {
184
+ setScrollY(e.nativeEvent.contentOffset.y);
185
+ }}/>
177
186
  <ConversationOption conversation={conversation} nativeEvent={nativeEvent} visible={popoverVisible} onClose={() => {
178
187
  setPopoverVisible(false);
179
188
  setConversation(undefined);
180
189
  }}/>
190
+
191
+ {scrollY > dp(50) && (<BackButton onBack={() => {
192
+ var _a;
193
+ (_a = flatListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToOffset({
194
+ offset: 0,
195
+ animated: true,
196
+ });
197
+ }}/>)}
198
+
199
+
181
200
  </View>);
182
201
  };
183
202
  const styles = StyleSheet.create({
@@ -213,4 +232,4 @@ const styles = StyleSheet.create({
213
232
  right: dp(15),
214
233
  },
215
234
  });
216
- export default List;
235
+ export default ConversationList;
@@ -1,4 +1,4 @@
1
- import List from './List';
1
+ import ConversationList from './ConversationList';
2
2
  import Setting from './setting/Setting';
3
3
  import SettingChatBg from './setting/SettingChatBg';
4
4
  import ForwardToConversation from './ForwardToConversation';
@@ -9,7 +9,7 @@ import OptionGroupMoreMember from "./setting/OptionGroupMoreMember";
9
9
  export const conversationRoutes = [
10
10
  {
11
11
  name: 'ConversationList',
12
- component: List,
12
+ component: ConversationList,
13
13
  options: {
14
14
  title: '',
15
15
  }
@@ -15,14 +15,20 @@ import { ConversationActions } from '@smart-link/im-base';
15
15
  import { Checkbox, ListRow, NavigationPage, SearchInput } from '@smart-link/rn-ui';
16
16
  import React, { useEffect, useMemo, useState } from 'react';
17
17
  import { View, StyleSheet, FlatList, Text, TouchableOpacity } from 'react-native';
18
+ import { Event, EventName } from "../../../utils/event";
18
19
  const GroupTransfer = ({ navigation }) => {
19
20
  const { t } = useTranslation();
20
21
  const { allMembers } = useConversation();
21
22
  const [checkedMember, setCheckedMember] = useState(null);
22
23
  const [keyword, setKeyword] = useState('');
23
24
  const list = useMemo(() => {
24
- return allMembers === null || allMembers === void 0 ? void 0 : allMembers.filter(item => item.memberLevel !== 'owner');
25
- }, [allMembers]);
25
+ return allMembers === null || allMembers === void 0 ? void 0 : allMembers.filter(item => {
26
+ if (keyword) {
27
+ return item.memberName.indexOf(keyword) > -1 && item.memberLevel !== 'owner';
28
+ }
29
+ return item.memberLevel !== 'owner';
30
+ });
31
+ }, [allMembers, keyword]);
26
32
  useEffect(() => {
27
33
  navigation.setOptions({
28
34
  title: t('chooseNewOwner'),
@@ -32,6 +38,7 @@ const GroupTransfer = ({ navigation }) => {
32
38
  const imManager = getImManager();
33
39
  yield imManager.store.dispatch(ConversationActions.groupTransfer(imManager, checkedMember));
34
40
  navigation.goBack();
41
+ Event.emit(EventName.ON_GROUP_TRANSFER_DONE);
35
42
  }
36
43
  })} style={{ marginRight: 10 }}>
37
44
  <Text style={{ color: tintColor }}>{t('confirm')}</Text>
@@ -11,6 +11,7 @@ import OptionGroup from '../../../pages/conversation/setting/OptionGroup';
11
11
  import OptionCancelGroup from '../../../pages/conversation/setting/OptionCancelGroup';
12
12
  import { startAddMember, startCreateGroup } from '../../../slice/contact/contact.slice';
13
13
  import { StackActions } from '@react-navigation/native';
14
+ import { Event, EventName } from "../../../utils/event";
14
15
  const { MEMBER, MGR, OWNER } = MemberLevel;
15
16
  const { LIAISON } = GroupType;
16
17
  // 最大显示人数
@@ -60,11 +61,22 @@ const ConversationSetting = ({ navigation }) => {
60
61
  ConfirmActionSheets({
61
62
  title: t('leaveGroup'),
62
63
  confirm: () => {
63
- imManager.store.dispatch(ConversationActions.doLeaveGroup(imManager, { chatGroupId: id }, () => {
64
- navigation.dispatch(StackActions.popToTop());
65
- }, () => {
66
- Toast.error(t('leaveGroupFail'));
67
- }));
64
+ const doLeave = () => {
65
+ imManager.store.dispatch(ConversationActions.doLeaveGroup(imManager, { chatGroupId: id }, () => {
66
+ navigation.dispatch(StackActions.popToTop());
67
+ }, () => {
68
+ Toast.error(t('leaveGroupFail'));
69
+ }));
70
+ };
71
+ if (memberLevel === OWNER) {
72
+ // 群主离开群,需要先转让群
73
+ Event.on(EventName.ON_GROUP_TRANSFER_DONE, () => {
74
+ doLeave();
75
+ });
76
+ navigation.navigate("GroupTransfer");
77
+ return;
78
+ }
79
+ doLeave();
68
80
  },
69
81
  t,
70
82
  });
@@ -40,7 +40,7 @@ const ChooseMember = ({ navigation, route: { params } }) => {
40
40
  headerTitle: params.atOther ? t('selectRemainder') : t('selectContacts'),
41
41
  headerRight: ({ tintColor }) => {
42
42
  return (<TouchableOpacity activeOpacity={0.5} style={{ padding: dp(10) }} disabled={!checked.length} onPress={confirm}>
43
- <Text style={{ color: tintColor }}>{t('confirm')}</Text>
43
+ <Text style={{ color: tintColor }}>{checked.length ? `${t('confirm')}(${checked.length})` : t('confirm')}</Text>
44
44
  </TouchableOpacity>);
45
45
  },
46
46
  });
@@ -50,9 +50,11 @@ const ChooseMember = ({ navigation, route: { params } }) => {
50
50
  const members = list.filter(item => checked.includes(item.userId));
51
51
  if (params.atOther) {
52
52
  if ((_a = imManager.inputRef) === null || _a === void 0 ? void 0 : _a.current) {
53
- (_c = (_b = imManager.inputRef) === null || _b === void 0 ? void 0 : _b.current) === null || _c === void 0 ? void 0 : _c.inertText(`${members[0].memberName} `);
53
+ const text = members.map(item => `@${item.memberName}`)
54
+ .join(' ').slice(1) + ' ';
55
+ (_c = (_b = imManager.inputRef) === null || _b === void 0 ? void 0 : _b.current) === null || _c === void 0 ? void 0 : _c.inertText(text);
54
56
  }
55
- imManager.store.dispatch(MessageActions.addAtUser(members[0]));
57
+ imManager.store.dispatch(MessageActions.addAtUsers(members));
56
58
  }
57
59
  if (params.removeMember) {
58
60
  imManager.store.dispatch(ConversationActions.removeGroupMembers({
@@ -64,16 +66,11 @@ const ChooseMember = ({ navigation, route: { params } }) => {
64
66
  const renderItem = ({ item }) => {
65
67
  const enableTopStyles = item.enableTop === 'on' ? styles.enableTop : null;
66
68
  return (<TouchableOpacity activeOpacity={0.5} style={[styles.row, enableTopStyles]} onPress={() => {
67
- if (params.atOther) {
68
- setChecked([item.userId]);
69
+ if (checked.includes(item.userId)) {
70
+ setChecked(checked.filter(id => id !== item.userId));
69
71
  }
70
72
  else {
71
- if (checked.includes(item.userId)) {
72
- setChecked(checked.filter(id => id !== item.userId));
73
- }
74
- else {
75
- setChecked([...checked, item.userId]);
76
- }
73
+ setChecked([...checked, item.userId]);
77
74
  }
78
75
  }}>
79
76
  <View style={{ marginRight: dp(10) }}>
@@ -70,10 +70,10 @@ const FileSelector = ({ navigation }) => {
70
70
  };
71
71
  navigation.goBack();
72
72
  if (isImage(file.localPath)) {
73
- imManager.sendPictureMessage(currentConversation, file);
73
+ imManager.sendPictureMessage(Object.assign({}, currentConversation), file);
74
74
  }
75
75
  else {
76
- imManager.sendFileMessage(currentConversation, file);
76
+ imManager.sendFileMessage(Object.assign({}, currentConversation), file);
77
77
  }
78
78
  return;
79
79
  }
@@ -99,7 +99,7 @@ const FileSelector = ({ navigation }) => {
99
99
  // ios 中文名称编码了,需要解码
100
100
  fileInfo.uri = decodeURI(fileInfo.uri);
101
101
  const localPath = yield copyFile(fileInfo.uri, fileInfo.name, 'file');
102
- imManager.sendFileMessage(currentConversation, {
102
+ imManager.sendFileMessage(Object.assign({}, currentConversation), {
103
103
  localPath: localPath,
104
104
  filename: fileInfo.name,
105
105
  size: fileInfo.size,
@@ -1,7 +1,7 @@
1
1
  import { ConversationType, MessageActions, MessagePanelActions, PayloadType, TipsType, showTimeLine, } from '@smart-link/im-base';
2
2
  import { dp, HeaderBackButton, Input, NavigationPage } from '@smart-link/rn-ui';
3
3
  import React, { useCallback, useEffect, useRef, useState } from 'react';
4
- import { View, Text, FlatList, TouchableOpacity, Keyboard, InteractionManager, StyleSheet, ActivityIndicator, TouchableWithoutFeedback } from 'react-native';
4
+ import { View, Text, FlatList, TouchableOpacity, Keyboard, StyleSheet, ActivityIndicator, TouchableWithoutFeedback } from 'react-native';
5
5
  import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
6
6
  import { useAuth, useConversation, useMessage } from '../../hooks/useImSelector';
7
7
  import useTranslation from '../../hooks/useTranslation';
@@ -71,10 +71,10 @@ const MessageList = ({ navigation }) => {
71
71
  imManager.store.dispatch(MessagePanelActions.cancelMultipleSelect());
72
72
  }
73
73
  else {
74
- InteractionManager.runAfterInteractions(() => {
74
+ requestIdleCallback(() => {
75
75
  imManager.store.dispatch(MessagePanelActions.saveDraftText(imManager));
76
76
  imManager.leaveConversation();
77
- imManager.store.dispatch(MessageActions.resetState({}));
77
+ imManager.store.dispatch(MessageActions.resetState());
78
78
  });
79
79
  }
80
80
  });
@@ -24,12 +24,13 @@ import PayloadMultiple from './Payload/PayloadMultiple';
24
24
  import PayloadWrapper from './Payload/PayloadWrapper';
25
25
  import PayloadShare from "../../../pages/message/components/Payload/PayloadShare";
26
26
  const MessagePayload = props => {
27
- const { style: outStyle } = props, retProps = __rest(props, ["style"]);
27
+ const { style: outStyle, isRecord } = props, retProps = __rest(props, ["style", "isRecord"]);
28
28
  const { t } = useTranslation();
29
29
  const payloadType = props.payloadType;
30
30
  const noStylePayloadType = [PayloadType.PICTURE, PayloadType.VIDEO];
31
31
  const style = !noStylePayloadType.includes(payloadType) ? outStyle : [];
32
- const mergeProps = Object.assign(Object.assign({}, retProps), { style });
32
+ const mergeProps = Object.assign(Object.assign({}, retProps), { isRecord,
33
+ style });
33
34
  let content;
34
35
  switch (payloadType) {
35
36
  case PayloadType.TEXT:
@@ -58,7 +59,7 @@ const MessagePayload = props => {
58
59
  break;
59
60
  default:
60
61
  console.log('暂不支持的消息类型:', payloadType);
61
- content = (<PayloadWrapper style={style} direction={mergeProps.direction}>
62
+ content = (<PayloadWrapper style={style} direction={mergeProps.direction} isRecord={isRecord}>
62
63
  <Text style={plain}>[{t('msgTypeNotSupported')}]</Text>
63
64
  </PayloadWrapper>);
64
65
  break;
@@ -39,7 +39,10 @@ const UpdateManager = ({ payload }) => {
39
39
  const { t } = useTranslation();
40
40
  console.log('UpdateManager: ', payload);
41
41
  const { targetUserName, memberLevel, opUserName } = payload !== null && payload !== void 0 ? payload : {};
42
- const content = memberLevel === MemberLevel.MGR ? t('setToMgr', { userName: targetUserName }) : t('cancelMgr', { userName: targetUserName });
42
+ let content = memberLevel === MemberLevel.MGR ? t('setToMgr', { userName: targetUserName }) : t('cancelMgr', { userName: targetUserName });
43
+ if (memberLevel === MemberLevel.OWNER) {
44
+ content = t('setToOwner', { userName: targetUserName });
45
+ }
43
46
  return <Text style={styles.text}>{opUserName} {content}</Text>;
44
47
  };
45
48
  const PayloadNotify = ({ payload, payloadType }) => {
@@ -84,7 +84,7 @@ const PayloadText = ({ payload, onLongPress, onPress, isRecord, showArrow = true
84
84
  const imManager = getImManager();
85
85
  imManager.store.dispatch(MessagePanelActions.onPressQuote(imManager, quoteMessageSeq));
86
86
  };
87
- return (<PayloadWrapper direction={direction} showArrow={showArrow} onLongPress={onLongPress} style={[style, plain]}>
87
+ return (<PayloadWrapper isRecord={isRecord} direction={direction} showArrow={showArrow} onLongPress={onLongPress} style={[style, plain]}>
88
88
  <TextMixQuoteMessage text={text} quote={quote} textStyle={[styles.text]} quoteStyle={styles.quote} quoteBgColor='#fff' onLongPressPhone={onLongPressPhone} onLongPressURL={onLongPressURL} onPressQuote={onPressQuote} atList={atList}/>
89
89
  </PayloadWrapper>);
90
90
  };
@@ -3,6 +3,7 @@ import { GestureResponderEvent } from 'react-native';
3
3
  import { NativeEventLayout } from "../../../types";
4
4
  export interface PayloadWrapperProps {
5
5
  showArrow?: boolean;
6
+ isRecord?: boolean;
6
7
  direction?: 'left' | 'right';
7
8
  children: React.ReactNode;
8
9
  style?: any;
@@ -29,13 +29,16 @@ const SvgFromUri = ({ direction, focus }) => {
29
29
  </Svg>);
30
30
  };
31
31
  const PayloadWrapper = memo((_a) => {
32
- var { children, direction, showArrow, onLongPress, style } = _a, retProps = __rest(_a, ["children", "direction", "showArrow", "onLongPress", "style"]);
32
+ var { children, direction, isRecord, showArrow, onLongPress, style } = _a, retProps = __rest(_a, ["children", "direction", "isRecord", "showArrow", "onLongPress", "style"]);
33
33
  const [layout, setLayout] = useState();
34
34
  const [focus, setFocus] = useState(false);
35
35
  let backgroundColor = direction === 'right' ? '#cde6f9' : '#fff';
36
36
  if (focus) {
37
37
  backgroundColor = Color(backgroundColor).darken(0.08).hex();
38
38
  }
39
+ if (isRecord) {
40
+ backgroundColor = 'transparent';
41
+ }
39
42
  return (<TouchableOpacity {...retProps} style={[
40
43
  style,
41
44
  {
@@ -4,8 +4,8 @@ type EmojiPanelProps = {
4
4
  onBackspace: () => void;
5
5
  };
6
6
  export type EmojiPanelMethods = {
7
- show: (height: number) => void;
8
- hide: () => void;
7
+ show: (height: number, animated?: boolean) => void;
8
+ hide: (animated?: boolean) => void;
9
9
  };
10
10
  declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<EmojiPanelProps & React.RefAttributes<EmojiPanelMethods>>>;
11
11
  export default _default;
@@ -22,16 +22,33 @@ const EmojiPanel = forwardRef(({ onCheck, onBackspace }, ref) => {
22
22
  <Text style={{ fontSize: 22, color: '#333' }}>{item}</Text>
23
23
  </TouchableOpacity>);
24
24
  useImperativeHandle(ref, () => ({
25
- show: (height) => {
25
+ show: (height, animated) => {
26
26
  setVisible(true);
27
- heightAnim.setValue(height);
27
+ if (animated) {
28
+ Animated.timing(heightAnim, {
29
+ toValue: height,
30
+ duration: 300,
31
+ useNativeDriver: false,
32
+ }).start();
33
+ }
34
+ else {
35
+ heightAnim.setValue(height);
36
+ }
28
37
  },
29
- hide: () => {
38
+ hide: (animated) => {
30
39
  setVisible(false);
31
- heightAnim.setValue(0);
40
+ if (animated) {
41
+ Animated.timing(heightAnim, {
42
+ toValue: 0,
43
+ duration: 300,
44
+ useNativeDriver: false,
45
+ }).start();
46
+ }
47
+ else {
48
+ heightAnim.setValue(0);
49
+ }
32
50
  },
33
51
  }), []);
34
- console.log('EmojiPanel: ', heightAnim);
35
52
  return (<Animated.View style={{
36
53
  height: heightAnim,
37
54
  position: 'relative',
@@ -33,15 +33,14 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
33
33
  const lazy = useRef(false);
34
34
  const tempText = useRef('');
35
35
  const { currentConversation } = useConversation();
36
- const { quote, messageText } = useMessage();
36
+ const { quote } = useMessage();
37
37
  const [showSend, setShowSend] = useState(false);
38
- const keyboardDuration = useRef(100);
38
+ const keyboardDuration = useRef(150);
39
39
  const modeRef = useRef('text');
40
40
  const [inputMode, setInputMode] = useState('text');
41
41
  const keyboardHeight = useRef(240);
42
42
  const handleChange = useCallback((text) => {
43
43
  tempText.current = text;
44
- // const trimTxt = text.trim();
45
44
  if (text.length && !lazy.current) {
46
45
  setShowSend(true);
47
46
  lazy.current = true;
@@ -89,31 +88,41 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
89
88
  }, timeout);
90
89
  }, []);
91
90
  const switchEmojiInputMode = useCallback(() => {
92
- var _a, _b, _c;
91
+ const animated = modeRef.current !== 'option';
93
92
  modeRef.current = 'emoji';
94
93
  setInputMode('emoji');
95
94
  Keyboard.dismiss();
96
- (_a = optionPanelRef.current) === null || _a === void 0 ? void 0 : _a.hide();
97
- (_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.show(keyboardHeight.current);
98
- (_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.focusNoSoftKeyboard();
95
+ setTimeout(() => {
96
+ var _a, _b, _c;
97
+ (_a = optionPanelRef.current) === null || _a === void 0 ? void 0 : _a.hide();
98
+ (_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.show(keyboardHeight.current, animated);
99
+ (_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.focusNoSoftKeyboard();
100
+ }, keyboardDuration.current);
99
101
  }, []);
100
102
  const switchOptionPanelMode = useMemoizedFn(() => {
101
- var _a;
102
- if ((_a = optionPanelRef.current) === null || _a === void 0 ? void 0 : _a.getVisible()) {
103
+ if (modeRef.current === 'option') {
103
104
  switchKeyboardInputMode();
104
105
  return;
105
106
  }
107
+ const animated = modeRef.current !== 'emoji';
106
108
  setInputMode('option');
107
109
  modeRef.current = 'option';
108
110
  Keyboard.dismiss();
109
111
  setTimeout(() => {
110
112
  var _a, _b;
111
113
  (_a = emojiPanelRef.current) === null || _a === void 0 ? void 0 : _a.hide();
112
- (_b = optionPanelRef.current) === null || _b === void 0 ? void 0 : _b.show(keyboardHeight.current);
114
+ (_b = optionPanelRef.current) === null || _b === void 0 ? void 0 : _b.show(keyboardHeight.current, animated);
113
115
  }, keyboardDuration.current);
114
116
  });
117
+ const onFocus = useCallback(() => {
118
+ var _a, _b;
119
+ if (modeRef.current === 'text') {
120
+ (_a = emojiPanelRef.current) === null || _a === void 0 ? void 0 : _a.hide();
121
+ (_b = optionPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide();
122
+ }
123
+ }, []);
115
124
  const onKeyPress = useCallback((e) => {
116
- if (['@', '@'].includes(e.nativeEvent.key)) {
125
+ if (['@'].includes(e.nativeEvent.key)) {
117
126
  if (!debounceAt.current) {
118
127
  debounceAt.current = debounce(() => {
119
128
  onKeyboardAt && onKeyboardAt();
@@ -155,14 +164,16 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
155
164
  reset: () => {
156
165
  var _a, _b, _c;
157
166
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
158
- (_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide();
159
- (_c = optionPanelRef.current) === null || _c === void 0 ? void 0 : _c.hide();
167
+ (_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide(true);
168
+ (_c = optionPanelRef.current) === null || _c === void 0 ? void 0 : _c.hide(true);
169
+ setInputMode('text');
170
+ modeRef.current = 'text';
160
171
  },
161
172
  focusTextInput: () => {
162
173
  var _a, _b, _c;
163
174
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
164
- (_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide();
165
- (_c = optionPanelRef.current) === null || _c === void 0 ? void 0 : _c.hide();
175
+ (_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide(true);
176
+ (_c = optionPanelRef.current) === null || _c === void 0 ? void 0 : _c.hide(true);
166
177
  },
167
178
  inertText: (text) => {
168
179
  var _a, _b, _c;
@@ -200,14 +211,15 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
200
211
  }
201
212
  const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', event => {
202
213
  var _a, _b;
203
- console.log(event.endCoordinates.height);
204
- keyboardHeight.current = Math.max(event.endCoordinates.height, 240);
205
- keyboardDuration.current = event.duration;
206
214
  (_a = emojiPanelRef.current) === null || _a === void 0 ? void 0 : _a.hide();
207
215
  (_b = optionPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide();
208
216
  });
217
+ const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', (event) => {
218
+ keyboardDuration.current = event.duration;
219
+ });
209
220
  return () => {
210
221
  keyboardDidShowListener.remove();
222
+ keyboardDidHideListener.remove();
211
223
  };
212
224
  }, []);
213
225
  return (<>
@@ -221,7 +233,7 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
221
233
  <VoiceSvg />
222
234
  </TouchableOpacity>)}
223
235
  </View>
224
- {inputMode === 'voice' ? <VoiceBar /> : <MessageInput ref={inputRef} onChangeText={handleChange} onKeyPress={onKeyPress}/>}
236
+ {inputMode === 'voice' ? <VoiceBar /> : <MessageInput ref={inputRef} onChangeText={handleChange} onFocus={onFocus} onKeyPress={onKeyPress}/>}
225
237
 
226
238
  <View style={[styles.switchInput, { paddingRight: 0 }]}>
227
239
  {inputMode === 'emoji' ? (<TouchableOpacity onPress={switchKeyboardInputMode}>
@@ -1,11 +1,6 @@
1
1
  import React from 'react';
2
- import { StyleProp, ViewStyle, NativeSyntheticEvent, TextInputKeyPressEventData } from 'react-native';
2
+ import { StyleProp, ViewStyle, TextInputProps } from 'react-native';
3
3
  export type InputMode = 'text' | 'emoji' | 'voice' | 'option';
4
- type MessageInputProps = {
5
- style?: StyleProp<ViewStyle>;
6
- onChangeText?: (text: string) => void;
7
- onKeyPress?: (e: NativeSyntheticEvent<TextInputKeyPressEventData>) => void;
8
- };
9
4
  export type MessageInputMethods = {
10
5
  isFocused: () => boolean;
11
6
  focus: () => void;
@@ -18,5 +13,8 @@ export type MessageInputMethods = {
18
13
  insertText: (text: string) => void;
19
14
  backspace: () => void;
20
15
  };
21
- declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<MessageInputProps & React.RefAttributes<MessageInputMethods>>>;
16
+ declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<{
17
+ style?: StyleProp<ViewStyle>;
18
+ onChangeText?: ((text: string) => void) | undefined;
19
+ } & Pick<TextInputProps, "onFocus" | "onKeyPress"> & React.RefAttributes<MessageInputMethods>>>;
22
20
  export default _default;
@@ -1,7 +1,7 @@
1
1
  import { dp } from '@smart-link/rn-ui';
2
2
  import React, { forwardRef, memo, useImperativeHandle, useRef, useState, } from 'react';
3
3
  import { TextInput, View, StyleSheet, Platform } from 'react-native';
4
- const MessageInput = forwardRef(({ onChangeText, onKeyPress, style }, ref) => {
4
+ const MessageInput = forwardRef(({ onChangeText, onKeyPress, onFocus, style }, ref) => {
5
5
  const inputRef = useRef(null);
6
6
  const [text, setText] = useState('');
7
7
  const [showSoftInputOnFocus, setShowSoftInputOnFocus] = useState(true);
@@ -162,7 +162,7 @@ const MessageInput = forwardRef(({ onChangeText, onKeyPress, style }, ref) => {
162
162
  innerText.current = n;
163
163
  setText(n);
164
164
  onChangeText === null || onChangeText === void 0 ? void 0 : onChangeText(n);
165
- }} onSelectionChange={({ nativeEvent }) => {
165
+ }} onFocus={onFocus} onSelectionChange={({ nativeEvent }) => {
166
166
  setSelection(nativeEvent.selection);
167
167
  selectionIos.current = nativeEvent.selection;
168
168
  }}/>
@@ -1,8 +1,7 @@
1
1
  import React from 'react';
2
2
  export type OptionPanelMethods = {
3
- getVisible: () => boolean;
4
- show: (height: number) => void;
5
- hide: () => void;
3
+ show: (height: number, animated?: boolean) => void;
4
+ hide: (animated?: boolean) => void;
6
5
  };
7
6
  declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<React.RefAttributes<OptionPanelMethods>>>;
8
7
  export default _default;
@@ -31,13 +31,30 @@ const OptionPanel = forwardRef((props, ref) => {
31
31
  const { t } = useTranslation();
32
32
  const imManager = getImManager();
33
33
  useImperativeHandle(ref, () => ({
34
- getVisible: () => visible,
35
- show: height => {
36
- heightAnim.setValue(height);
34
+ show: (height, animated) => {
35
+ if (animated) {
36
+ Animated.timing(heightAnim, {
37
+ toValue: height,
38
+ duration: 300,
39
+ useNativeDriver: false,
40
+ }).start();
41
+ }
42
+ else {
43
+ heightAnim.setValue(height);
44
+ }
37
45
  setVisible(true);
38
46
  },
39
- hide: () => {
40
- heightAnim.setValue(0);
47
+ hide: (animated) => {
48
+ if (animated) {
49
+ Animated.timing(heightAnim, {
50
+ toValue: 0,
51
+ duration: 300,
52
+ useNativeDriver: false,
53
+ }).start();
54
+ }
55
+ else {
56
+ heightAnim.setValue(0);
57
+ }
41
58
  setVisible(false);
42
59
  },
43
60
  }), [visible]);
@@ -16,9 +16,14 @@ import Highlighter from '../../../components/Highlighter';
16
16
  import useTranslation from '../../../hooks/useTranslation';
17
17
  import { getImManager } from '../../../init';
18
18
  const RenderUserItem = (props) => {
19
+ const { keyword, data } = props;
19
20
  return (<View style={{ flexDirection: 'row', alignItems: 'center' }}>
20
- <ChatAvatar needUpdate id={props.data.userId} url={props.data.avatars} name={props.data.userName}/>
21
- <Highlighter style={{ marginLeft: dp(10), flex: 1 }} searchWord={props.keyword} textToHighlight={props.data.userName}/>
21
+ <ChatAvatar needUpdate id={data.userId} url={data.avatars} name={data.userName}/>
22
+ <View style={{ marginLeft: dp(10), flex: 1 }}>
23
+ <Highlighter searchWord={keyword} textToHighlight={data.userName} numberOfLines={1}/>
24
+ <Text numberOfLines={1} style={{ fontSize: dp(12), color: '#999' }}>{data.groupNamePaths}</Text>
25
+ </View>
26
+
22
27
  </View>);
23
28
  };
24
29
  const SearchUser = ({ keyword }) => {
@@ -0,0 +1,13 @@
1
+ export declare enum EventName {
2
+ ON_GROUP_TRANSFER_DONE = "ON_GROUP_TRANSFER_DONE"
3
+ }
4
+ declare class EventEmitter {
5
+ events: Map<string, Function[]>;
6
+ constructor();
7
+ on(eventName: EventName, callback: Function): () => void;
8
+ off(eventName: EventName, callback: Function): void;
9
+ removeAll(eventName: EventName): void;
10
+ emit(eventName: EventName, ...args: any[]): void;
11
+ }
12
+ export declare const Event: EventEmitter;
13
+ export {};
@@ -0,0 +1,32 @@
1
+ export var EventName;
2
+ (function (EventName) {
3
+ EventName["ON_GROUP_TRANSFER_DONE"] = "ON_GROUP_TRANSFER_DONE";
4
+ })(EventName || (EventName = {}));
5
+ class EventEmitter {
6
+ constructor() {
7
+ this.events = new Map();
8
+ }
9
+ on(eventName, callback) {
10
+ if (this.events.has(eventName)) {
11
+ this.events.get(eventName).push(callback);
12
+ }
13
+ else {
14
+ this.events.set(eventName, [callback]);
15
+ }
16
+ return () => {
17
+ this.off(eventName, callback);
18
+ };
19
+ }
20
+ off(eventName, callback) {
21
+ const index = this.events.get(eventName).indexOf(callback);
22
+ this.events.get(eventName).splice(index, 1);
23
+ }
24
+ removeAll(eventName) {
25
+ this.events.set(eventName, []);
26
+ }
27
+ emit(eventName, ...args) {
28
+ const tasks = this.events.get(eventName) || [];
29
+ tasks.forEach(fn => fn.call(null, ...args));
30
+ }
31
+ }
32
+ export const Event = new EventEmitter();
@@ -7,6 +7,7 @@ declare const resourceDirBase: {
7
7
  readonly video: "/video";
8
8
  readonly file: "/file";
9
9
  readonly background: "/background";
10
+ readonly cache: "/cache";
10
11
  };
11
12
  type ResourceDir = keyof typeof resourceDirBase;
12
13
  export declare function initDir(user: IUser): Promise<void>;
@@ -39,4 +40,5 @@ export declare function getSafeFilename(filename: string): string;
39
40
  */
40
41
  export declare function truncateFilenameMiddle(filename: string, maxLength?: number): string;
41
42
  export declare function getFileSize(filePath: string): Promise<number>;
43
+ export declare function sliceFile(filePath: string, start: number, end: number): Promise<string>;
42
44
  export {};
@@ -19,6 +19,7 @@ const resourceDirBase = {
19
19
  video: '/video',
20
20
  file: '/file',
21
21
  background: '/background',
22
+ cache: '/cache',
22
23
  };
23
24
  const resourceDir = {};
24
25
  let userPath = '';
@@ -112,7 +113,7 @@ export const download = ({ fileId, resourceType, filename, onProgress, onStart }
112
113
  progressDivider: 5,
113
114
  begin: (e) => {
114
115
  // 这个方法不能注释,否则 ios 不响应进度
115
- // console.log('download begin: ', e);
116
+ console.log('download begin: ', e.jobId);
116
117
  },
117
118
  progress: (e) => {
118
119
  // console.log('download progress: ', e);
@@ -127,7 +128,7 @@ export const download = ({ fileId, resourceType, filename, onProgress, onStart }
127
128
  return relativePath;
128
129
  }
129
130
  catch (error) {
130
- console.error('下载失败:', error);
131
+ console.warn('下载失败:', error);
131
132
  throw error;
132
133
  }
133
134
  });
@@ -412,3 +413,12 @@ export function getFileSize(filePath) {
412
413
  return stat.size;
413
414
  });
414
415
  }
416
+ export function sliceFile(filePath, start, end) {
417
+ return __awaiter(this, void 0, void 0, function* () {
418
+ // const destPath = `${userPath}/cache/${uuidv4()}.tmp`;
419
+ // 分片读取文件写入缓存文件
420
+ const base64 = yield RNFS.read(toAbsolutePath(filePath), end - start, start, 'base64');
421
+ // await RNFS.writeFile(destPath, base64, 'base64');
422
+ return base64;
423
+ });
424
+ }
@@ -29,7 +29,8 @@ interface IRequest {
29
29
  postForm<R, Q = any>(url: string, data?: Q, config?: AxiosRequestConfig): Promise<IResponse<R>>;
30
30
  postFile<R, Q = any>(url: string, data?: Q, config?: AxiosRequestConfig): Promise<IResponse<R>>;
31
31
  }
32
+ export declare const getStorage: () => Storage;
32
33
  export declare const getBaseURL: () => string;
33
- export declare const createRequest: (base: string, storage: Storage) => AxiosInstance;
34
+ export declare const createRequest: (base: string, _storage: Storage) => AxiosInstance;
34
35
  declare const request: IRequest;
35
36
  export default request;
@@ -21,9 +21,12 @@ export const Headers = {
21
21
  };
22
22
  let instance = null;
23
23
  let baseURL = '';
24
+ let storage;
25
+ export const getStorage = () => storage;
24
26
  export const getBaseURL = () => baseURL;
25
- export const createRequest = (base, storage) => {
27
+ export const createRequest = (base, _storage) => {
26
28
  baseURL = base;
29
+ storage = _storage;
27
30
  instance = axios.create({
28
31
  baseURL: base,
29
32
  timeout: 10000,
@@ -7,18 +7,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import request, { getBaseURL } from './request';
11
- import { toAbsolutePath } from './file';
10
+ import request, { getBaseURL, getStorage } from './request';
11
+ import { toAbsolutePath, toFullPath } from './file';
12
+ import { uploadFile as chunkUpload } from '@smart-link/chunk-upload';
13
+ const CHUNK_SIZE = 1024 * 1024 * 2;
12
14
  const uploadFile = (filePath, type, onProgress) => __awaiter(void 0, void 0, void 0, function* () {
13
- const url = getBaseURL();
14
- const uploadUrl = `${url}/api-file/file/uploadFile`;
15
+ const uploadUrl = `/api-file/file/uploadFile`;
16
+ const local = yield fetch(toAbsolutePath(filePath));
17
+ const file = yield local.blob();
18
+ const fileName = filePath.split('/').pop();
19
+ //
20
+ if (file.size > CHUNK_SIZE) {
21
+ return uploadBigFile(toFullPath(filePath), fileName, onProgress);
22
+ }
15
23
  const formData = new FormData();
16
- // @ts-ignore
17
24
  formData.append('file', {
18
25
  // @ts-ignore
19
26
  uri: toAbsolutePath(filePath),
20
- name: filePath.split('/').pop(),
21
- type: type,
27
+ name: fileName,
28
+ type,
22
29
  });
23
30
  const resp = yield request.postFile(uploadUrl, formData, {
24
31
  onUploadProgress: onProgress,
@@ -26,4 +33,27 @@ const uploadFile = (filePath, type, onProgress) => __awaiter(void 0, void 0, voi
26
33
  console.log('uploadFile: ', resp);
27
34
  return resp.data;
28
35
  });
36
+ const uploadBigFile = (filePath, fileName, onProgress) => __awaiter(void 0, void 0, void 0, function* () {
37
+ const uploadBigFileURL = `/api-file/file/uploadBigFile`;
38
+ const cookie = yield getStorage().getItem('cookies');
39
+ const resp = yield chunkUpload({
40
+ filePath,
41
+ fileName,
42
+ chunkSize: CHUNK_SIZE,
43
+ uploadUrl: getBaseURL() + uploadBigFileURL,
44
+ headers: {
45
+ 'Cookie': cookie,
46
+ },
47
+ onProgress: (o) => {
48
+ console.log('uploadBigFile: ', o);
49
+ onProgress && onProgress({
50
+ loaded: o.loaded,
51
+ total: o.total,
52
+ lengthComputable: true,
53
+ bytes: o.loaded,
54
+ });
55
+ },
56
+ });
57
+ return resp.data;
58
+ });
29
59
  export default uploadFile;
package/package.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "name": "@smart-link/rn-im",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "",
5
+ "type": "module",
5
6
  "main": "./dist/index.js",
7
+ "module": "./src/index.ts",
6
8
  "dependencies": {
7
9
  "color": "^5.0.0",
8
10
  "lodash-es": "^4.17.21",
@@ -34,6 +36,7 @@
34
36
  "@react-navigation/native": "6.1.7",
35
37
  "@react-navigation/native-stack": "6.9.13",
36
38
  "@reduxjs/toolkit": "^1.5.0",
39
+ "@smart-link/chunk-upload": "^1.0.0",
37
40
  "ahooks": "^3.8.0",
38
41
  "axios": "^0.30.0",
39
42
  "dayjs": "^1.11.9",
@@ -62,8 +65,8 @@
62
65
  "react-redux": "^8.1.3",
63
66
  "redux": "^4.2.1",
64
67
  "redux-thunk": "^2.4.2",
65
- "@smart-link/rn-ui": "^1.1.0",
66
- "@smart-link/im-base": "^1.1.1"
68
+ "@smart-link/rn-ui": "^1.1.1",
69
+ "@smart-link/im-base": "^1.1.2"
67
70
  },
68
71
  "files": [
69
72
  "dist/**",