@smart-link/rn-im 1.0.24 → 1.1.1

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 (125) hide show
  1. package/assets/file-dir.jpg +0 -0
  2. package/dist/api/addressList.d.ts +16 -15
  3. package/dist/api/addressList.js +15 -1
  4. package/dist/api/user.js +1 -1
  5. package/dist/components/Camera/Camera.js +3 -2
  6. package/dist/components/Camera/CameraCapture.js +61 -12
  7. package/dist/components/ChatAvatar/ChatAvatar.d.ts +3 -3
  8. package/dist/components/ChatAvatar/ChatAvatar.js +51 -49
  9. package/dist/components/ChatAvatar/ChatAvatarId.d.ts +2 -2
  10. package/dist/components/ChatAvatar/ChatAvatarId.js +19 -46
  11. package/dist/components/ChatAvatar/ChatAvatarLocal.js +22 -10
  12. package/dist/components/Favicon.js +1 -1
  13. package/dist/components/LocalImage.js +3 -1
  14. package/dist/components/styles.d.ts +7 -1
  15. package/dist/components/styles.js +16 -11
  16. package/dist/default-assets.d.ts +1 -0
  17. package/dist/default-assets.js +1 -0
  18. package/dist/hooks/useAnimatedValue.d.ts +2 -0
  19. package/dist/hooks/useAnimatedValue.js +9 -0
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +1 -0
  22. package/dist/init.d.ts +2 -2
  23. package/dist/init.js +8 -5
  24. package/dist/interface.d.ts +15 -0
  25. package/dist/pages/address-list/AddressList.js +22 -40
  26. package/dist/pages/address-list/ChooseContact.js +209 -45
  27. package/dist/pages/address-list/MyGroups.d.ts +6 -0
  28. package/dist/pages/address-list/MyGroups.js +90 -0
  29. package/dist/pages/address-list/Organization.d.ts +0 -7
  30. package/dist/pages/address-list/Organization.js +24 -108
  31. package/dist/pages/address-list/UserDetail.js +5 -5
  32. package/dist/pages/address-list/UserSearch.js +6 -7
  33. package/dist/pages/address-list/addressList.routes.d.ts +9 -0
  34. package/dist/pages/address-list/addressList.routes.js +8 -0
  35. package/dist/pages/address-list/components/Icons.d.ts +8 -0
  36. package/dist/pages/address-list/components/Icons.js +15 -0
  37. package/dist/pages/address-list/components/OrgPath.d.ts +12 -0
  38. package/dist/pages/address-list/components/OrgPath.js +59 -0
  39. package/dist/pages/address-list/components/RenderUserItem.d.ts +9 -0
  40. package/dist/pages/address-list/components/RenderUserItem.js +14 -0
  41. package/dist/pages/address-list/{UserJobs.js → components/UserJobs.js} +2 -2
  42. package/dist/pages/address-list/useGroupCategory.d.ts +2 -0
  43. package/dist/pages/address-list/useGroupCategory.js +11 -0
  44. package/dist/pages/address-list/useRoleList.d.ts +2 -0
  45. package/dist/pages/address-list/useRoleList.js +11 -0
  46. package/dist/pages/collection/Collection.js +52 -8
  47. package/dist/pages/collection/ContentFactory.js +50 -3
  48. package/dist/pages/conversation/ForwardToConversation.js +4 -2
  49. package/dist/pages/conversation/List.js +15 -7
  50. package/dist/pages/conversation/components/ConversationCard.js +1 -7
  51. package/dist/pages/conversation/setting/OptionAvatars.d.ts +1 -1
  52. package/dist/pages/conversation/setting/OptionAvatars.js +0 -1
  53. package/dist/pages/conversation/setting/OptionGroup.d.ts +2 -2
  54. package/dist/pages/conversation/setting/OptionGroup.js +4 -4
  55. package/dist/pages/conversation/setting/OptionGroupManage.js +4 -8
  56. package/dist/pages/conversation/setting/OptionGroupMoreMember.js +37 -18
  57. package/dist/pages/conversation/setting/Setting.js +5 -5
  58. package/dist/pages/conversation/setting/SettingChatBg.js +32 -16
  59. package/dist/pages/message/ChooseMember.js +2 -2
  60. package/dist/pages/message/FileSelector.d.ts +6 -0
  61. package/dist/pages/message/FileSelector.js +272 -0
  62. package/dist/pages/message/MessageList.js +81 -43
  63. package/dist/pages/message/MessageRecord.js +34 -23
  64. package/dist/pages/message/components/MessageItem.d.ts +7 -7
  65. package/dist/pages/message/components/MessageItem.js +29 -20
  66. package/dist/pages/message/components/MessageOption.d.ts +7 -3
  67. package/dist/pages/message/components/MessageOption.js +14 -16
  68. package/dist/pages/message/components/MessagePayload.d.ts +2 -1
  69. package/dist/pages/message/components/MessagePictureAlbum.js +6 -4
  70. package/dist/pages/message/components/Payload/PayloadFile.d.ts +1 -1
  71. package/dist/pages/message/components/Payload/PayloadFile.js +34 -16
  72. package/dist/pages/message/components/Payload/PayloadMultiple.js +12 -8
  73. package/dist/pages/message/components/Payload/PayloadNotify.js +14 -5
  74. package/dist/pages/message/components/Payload/PayloadPicture.d.ts +1 -1
  75. package/dist/pages/message/components/Payload/PayloadPicture.js +9 -7
  76. package/dist/pages/message/components/Payload/PayloadText.js +7 -8
  77. package/dist/pages/message/components/Payload/PayloadVideo.js +2 -1
  78. package/dist/pages/message/components/Payload/PayloadVoice.js +7 -13
  79. package/dist/pages/message/components/Payload/PayloadWrapper.d.ts +2 -1
  80. package/dist/pages/message/components/Payload/PayloadWrapper.js +46 -20
  81. package/dist/pages/message/components/Payload/type.d.ts +3 -1
  82. package/dist/pages/message/components/ReceiptBack.js +2 -2
  83. package/dist/pages/message/components/TextMixMessage.d.ts +2 -0
  84. package/dist/pages/message/components/TextMixMessage.js +10 -3
  85. package/dist/pages/message/components/TextMixQuote.js +10 -15
  86. package/dist/pages/message/components/TextMixQuoteMessage.d.ts +2 -1
  87. package/dist/pages/message/components/TextMixQuoteMessage.js +5 -5
  88. package/dist/pages/message/components/UploadProgress.d.ts +1 -1
  89. package/dist/pages/message/components/UploadProgress.js +1 -1
  90. package/dist/pages/message/components/messageBar/EmojiPanel.js +8 -9
  91. package/dist/pages/message/components/messageBar/MessageBar.d.ts +1 -1
  92. package/dist/pages/message/components/messageBar/MessageBar.js +14 -12
  93. package/dist/pages/message/components/messageBar/MessageInput.d.ts +1 -0
  94. package/dist/pages/message/components/messageBar/MessageInput.js +19 -1
  95. package/dist/pages/message/components/messageBar/OptionPanel.js +22 -44
  96. package/dist/pages/message/message.routes.js +10 -0
  97. package/dist/pages/search/components/SearchUser.js +2 -2
  98. package/dist/pages/types.d.ts +6 -2
  99. package/dist/slice/contact/contact.slice.js +1 -0
  100. package/dist/slice/video/video.action.js +9 -1
  101. package/dist/utils/common-action-sheet.d.ts +1 -0
  102. package/dist/utils/common-action-sheet.js +18 -0
  103. package/dist/utils/file-icon.js +29 -2
  104. package/dist/utils/file.d.ts +21 -2
  105. package/dist/utils/file.js +209 -42
  106. package/dist/utils/phone.d.ts +1 -1
  107. package/dist/utils/phone.js +1 -9
  108. package/dist/utils/request.d.ts +2 -1
  109. package/dist/utils/request.js +17 -6
  110. package/dist/utils/upload.d.ts +2 -4
  111. package/dist/utils/upload.js +13 -49
  112. package/package.json +5 -5
  113. package/dist/pages/address-list/Icons.d.ts +0 -5
  114. package/dist/pages/address-list/Icons.js +0 -9
  115. package/dist/slice/contact/contact.action.d.ts +0 -1
  116. package/dist/slice/contact/contact.action.js +0 -1
  117. package/dist/utils/cookie.d.ts +0 -2
  118. package/dist/utils/cookie.js +0 -25
  119. package/dist/utils/file-operate.d.ts +0 -1
  120. package/dist/utils/file-operate.js +0 -3
  121. package/dist/utils/text-mix.d.ts +0 -9
  122. package/dist/utils/text-mix.js +0 -75
  123. /package/dist/pages/address-list/{CardInfo.d.ts → components/CardInfo.d.ts} +0 -0
  124. /package/dist/pages/address-list/{CardInfo.js → components/CardInfo.js} +0 -0
  125. /package/dist/pages/address-list/{UserJobs.d.ts → components/UserJobs.d.ts} +0 -0
@@ -0,0 +1,272 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ // @flow
11
+ import * as React from 'react';
12
+ import { dp, NavigationPage, SearchInput, Toast } from "@smart-link/rn-ui";
13
+ import * as DocumentPicker from '@react-native-documents/picker';
14
+ import { View, StyleSheet, TouchableOpacity, ScrollView, Text, FlatList } from "react-native";
15
+ import { Fragment, useCallback, useEffect, useState } from "react";
16
+ import { copyFile, getExtensionFromMime, getFileRootItem, getFileType, isImage, numberToFileSize, readFileDir, selectFile } from "../../utils/file";
17
+ import { getImManager } from "../../init";
18
+ import AntDesign from "react-native-vector-icons/AntDesign";
19
+ import useTranslation from "../../hooks/useTranslation";
20
+ import { findSvgIcon } from "../../utils/file-icon";
21
+ import FastImage from "react-native-fast-image";
22
+ import { dirImg } from "../../default-assets";
23
+ import { formatTime } from "../../hooks/useFormatMsgTime";
24
+ import { useConversation } from "../../hooks/useImSelector";
25
+ const getIcon = (item) => {
26
+ if (item.isDir) {
27
+ return <FastImage style={styles.fileImage} source={dirImg}/>;
28
+ }
29
+ if (isImage(item.path)) {
30
+ return <FastImage style={styles.fileImage} source={{ uri: 'file://' + item.path }}/>;
31
+ }
32
+ if (item.thumbPath) {
33
+ return <FastImage style={styles.fileImage} source={{ uri: 'file://' + item.thumbPath }}/>;
34
+ }
35
+ const Icon = findSvgIcon(getFileType(item.path));
36
+ return <Icon style={styles.fileImage}/>;
37
+ };
38
+ const FileSelector = ({ navigation }) => {
39
+ const scrollHolder = {};
40
+ const [data, setData] = useState([]);
41
+ const [keyword, setKeyword] = useState('');
42
+ const [pathItems, setPathItems] = useState([]);
43
+ const imManager = getImManager();
44
+ const { t, i18n } = useTranslation();
45
+ const { currentConversation } = useConversation();
46
+ useEffect(() => {
47
+ navigation.setOptions({
48
+ headerLeft({ tintColor }) {
49
+ return (<TouchableOpacity activeOpacity={0.5} style={{ padding: dp(10) }} onPress={() => {
50
+ navigation.goBack();
51
+ }}>
52
+ <Text style={{ color: tintColor }}>{t('cancel')}</Text>
53
+ </TouchableOpacity>);
54
+ },
55
+ title: t('file')
56
+ });
57
+ getFileRootItem(imManager).then(items => {
58
+ setData(items);
59
+ });
60
+ }, [navigation, t]);
61
+ const onPressItem = (item) => __awaiter(void 0, void 0, void 0, function* () {
62
+ if (!item.isDir) {
63
+ const localPath = yield selectFile(imManager, item.path, item.name);
64
+ const file = {
65
+ filename: item.name,
66
+ localPath: localPath,
67
+ size: item.size,
68
+ type: getFileType(item.path),
69
+ lastModified: item.time,
70
+ };
71
+ navigation.goBack();
72
+ if (isImage(file.localPath)) {
73
+ imManager.sendPictureMessage(currentConversation, file);
74
+ }
75
+ else {
76
+ imManager.sendFileMessage(currentConversation, file);
77
+ }
78
+ return;
79
+ }
80
+ if (item.path === 'outside') {
81
+ try {
82
+ const files = yield DocumentPicker.pick({
83
+ type: [DocumentPicker.types.allFiles],
84
+ });
85
+ const fileInfo = files[0];
86
+ console.log('chooseFile: ', fileInfo);
87
+ if (!fileInfo.uri) {
88
+ Toast.error(t("failedToObtainFilePath"));
89
+ return;
90
+ }
91
+ const ext = getExtensionFromMime(fileInfo.type);
92
+ if (!ext) {
93
+ Toast.error(t("unsupportedFileTypes"));
94
+ return;
95
+ }
96
+ if (!fileInfo.name.endsWith(ext)) {
97
+ fileInfo.name = fileInfo.name + ext;
98
+ }
99
+ // ios 中文名称编码了,需要解码
100
+ fileInfo.uri = decodeURI(fileInfo.uri);
101
+ const localPath = yield copyFile(fileInfo.uri, fileInfo.name, 'file');
102
+ imManager.sendFileMessage(currentConversation, {
103
+ localPath: localPath,
104
+ filename: fileInfo.name,
105
+ size: fileInfo.size,
106
+ type: fileInfo.type,
107
+ lastModified: Date.now(),
108
+ });
109
+ navigation.goBack();
110
+ }
111
+ catch (e) {
112
+ console.log('pick file: ', e);
113
+ }
114
+ return;
115
+ }
116
+ setPathItems([...pathItems.filter(pathItem => pathItem.path !== item.path), item]);
117
+ scrollHolder.scroll.scrollToEnd();
118
+ readFileDir(imManager, item.path, keyword).then(items => {
119
+ setData(items);
120
+ });
121
+ });
122
+ const onChangeText = useCallback((keyword) => {
123
+ setKeyword(keyword);
124
+ if (pathItems.length === 0) {
125
+ return;
126
+ }
127
+ readFileDir(imManager, pathItems[pathItems.length - 1].path, keyword).then(items => {
128
+ setData(items);
129
+ });
130
+ }, [pathItems]);
131
+ const onPressPathRoot = useCallback(() => {
132
+ setPathItems([]);
133
+ getFileRootItem(imManager).then(items => {
134
+ setData(items);
135
+ });
136
+ }, []);
137
+ const onPressPathItem = useCallback((item) => {
138
+ setPathItems([
139
+ ...pathItems.filter(source => source.path.indexOf(item.path) === -1),
140
+ item
141
+ ]);
142
+ readFileDir(imManager, item.path, keyword).then(items => {
143
+ setData(items);
144
+ });
145
+ }, [pathItems, keyword]);
146
+ const listHeaderComponent = data.length === 0 ? null : <View style={styles.listHeaderLine}/>;
147
+ const listFooterComponent = data.length === 0 ? null : <View style={styles.listFooterLine}/>;
148
+ const itemSeparatorComponent = () => <View style={styles.separator}/>;
149
+ const keyExtractor = (item) => item.name + '_' + item.size;
150
+ const renderItem = ({ item }) => {
151
+ const icon = getIcon(item);
152
+ const extText = item.isDir ? (item.childrenCount + t('items')) : numberToFileSize(item.size);
153
+ return (<TouchableOpacity style={styles.item} onPress={() => {
154
+ onPressItem && onPressItem(item);
155
+ }}>
156
+
157
+ {icon}
158
+ <View style={styles.width15}/>
159
+ <View style={styles.left}>
160
+ <Text style={styles.name} numberOfLines={1} ellipsizeMode={'middle'}>
161
+ {item.name}
162
+ </Text>
163
+ <Text style={styles.time} numberOfLines={1} ellipsizeMode={'middle'}>
164
+ {(item.time ? (formatTime(item.time, t, i18n) + ' - ') : '') + extText}
165
+ </Text>
166
+ </View>
167
+ {item.isDir &&
168
+ <AntDesign name='right' size={dp(14)} color={'#666'} style={styles.accessory}/>}
169
+
170
+ </TouchableOpacity>);
171
+ };
172
+ return (<NavigationPage noPadding>
173
+ <View style={styles.selector}>
174
+ <View style={styles.search}>
175
+ <SearchInput style={{
176
+ height: dp(32),
177
+ backgroundColor: '#ececec',
178
+ }} value={keyword} onChange={onChangeText} onSubmitEditing={() => { }} placeholder={t('search')}/>
179
+ </View>
180
+ <View style={styles.scroll}>
181
+ <TouchableOpacity onPress={onPressPathRoot} style={styles.scrollVertical}>
182
+ <Text style={styles.pathText}>{t('position')}</Text>
183
+ </TouchableOpacity>
184
+ <AntDesign name='right' size={dp(16)} color={'#333'} style={styles.scrollVertical}/>
185
+ <ScrollView style={styles.scrollVertical} horizontal={true} ref={v => scrollHolder.scroll = v}>
186
+
187
+ {pathItems.map((pathItem, index) => {
188
+ const disabled = index === pathItems.length - 1;
189
+ return (<Fragment key={index}>
190
+ <TouchableOpacity disabled={disabled} onPress={() => {
191
+ onPressPathItem && onPressPathItem(pathItem);
192
+ }}>
193
+ <Text style={styles.pathText}>{pathItem.name}</Text>
194
+ </TouchableOpacity>
195
+ <AntDesign name='right' size={dp(16)} color={'#333'}/>
196
+ </Fragment>);
197
+ })}
198
+ </ScrollView>
199
+ </View>
200
+ <FlatList contentContainerStyle={styles.list} ItemSeparatorComponent={itemSeparatorComponent} ListHeaderComponent={listHeaderComponent} ListFooterComponent={listFooterComponent} initialNumToRender={20} numColumns={1} data={data} keyboardShouldPersistTaps={'handled'} keyExtractor={keyExtractor} renderItem={renderItem}/>
201
+ </View>
202
+ </NavigationPage>);
203
+ };
204
+ const styles = StyleSheet.create({
205
+ selector: {
206
+ flex: 1,
207
+ backgroundColor: '#fff',
208
+ },
209
+ scroll: {
210
+ flexDirection: 'row',
211
+ paddingHorizontal: dp(8),
212
+ },
213
+ scrollVertical: {
214
+ paddingBottom: dp(10),
215
+ },
216
+ pathText: {
217
+ paddingHorizontal: dp(5),
218
+ fontSize: dp(14),
219
+ lineHeight: dp(16),
220
+ color: '#666',
221
+ },
222
+ search: {
223
+ backgroundColor: '#fff',
224
+ padding: dp(10),
225
+ },
226
+ list: {
227
+ paddingHorizontal: dp(15),
228
+ },
229
+ listFooterLine: {},
230
+ listHeaderLine: {},
231
+ separator: {
232
+ marginLeft: dp(65),
233
+ marginVertical: dp(10),
234
+ height: dp(1),
235
+ backgroundColor: '#ececec'
236
+ },
237
+ width15: {
238
+ width: dp(15),
239
+ },
240
+ width10: {
241
+ width: dp(10),
242
+ },
243
+ item: {
244
+ flexDirection: 'row',
245
+ alignItems: 'center',
246
+ },
247
+ fileImage: {
248
+ borderRadius: dp(5),
249
+ width: dp(49),
250
+ height: dp(49)
251
+ },
252
+ left: {
253
+ justifyContent: 'space-around'
254
+ },
255
+ name: {
256
+ color: '#333',
257
+ fontSize: dp(15),
258
+ lineHeight: dp(20),
259
+ width: dp(260),
260
+ },
261
+ time: {
262
+ color: '#666',
263
+ fontSize: dp(12),
264
+ lineHeight: dp(18),
265
+ },
266
+ accessory: {
267
+ alignSelf: 'center',
268
+ position: 'absolute',
269
+ right: dp(15),
270
+ }
271
+ });
272
+ export default FileSelector;
@@ -20,19 +20,22 @@ import { noMessageImg } from "../../default-assets";
20
20
  const MessageList = ({ navigation }) => {
21
21
  const { t } = useTranslation();
22
22
  const messageListRef = useRef(null);
23
- const { currentConversation } = useConversation();
24
- const { messages, rangeList, quote, tips, isMultiple, multipleSelect, isSearch } = useMessage();
23
+ const { currentConversation, onlineClients } = useConversation();
24
+ const { messages, focusMessageIndex, quote, tips, isMultiple, multipleSelect } = useMessage();
25
25
  const { user } = useAuth();
26
26
  const bar = useRef(null);
27
27
  const [showAlbum, setShowAlbum] = useState(false);
28
28
  const [targetMessageSeq, setTargetMessageSeq] = useState('');
29
29
  const [selectMessage, setSelectMessage] = useState();
30
- const [nativeEvent, setNativeEvent] = useState();
31
30
  const [loading, setLoading] = useState(false);
32
- const itemLayout = useRef({});
31
+ const [touchPoint, setTouchPoint] = useState();
32
+ const [targetLayout, setTargetLayout] = useState();
33
+ const itemLayout = useRef({}).current;
34
+ const listHeight = useRef(0);
35
+ const init = useRef(true);
33
36
  const isB2C = (currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.type) === ConversationType.B2C;
34
37
  useEffect(() => {
35
- console.log('[进入会话]: ', currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.name);
38
+ // console.log('[进入会话]: ', currentConversation?.name);
36
39
  if (currentConversation) {
37
40
  navigation.setOptions({
38
41
  headerLeft: isMultiple
@@ -43,7 +46,16 @@ const MessageList = ({ navigation }) => {
43
46
  </TouchableOpacity>)
44
47
  : HeaderBackButton,
45
48
  headerTitle: ({ tintColor }) => (<View style={{ width: dp(220) }}>
46
- <Text numberOfLines={1} style={{ color: tintColor, fontSize: dp(18), textAlign: 'center' }}>{currentConversation.name}</Text>
49
+ <Text numberOfLines={1} style={{
50
+ color: tintColor,
51
+ fontSize: dp(18),
52
+ textAlign: 'center'
53
+ }}>{currentConversation.name}</Text>
54
+ {onlineClients.length > 0 && <Text style={{
55
+ color: tintColor,
56
+ fontSize: dp(12),
57
+ textAlign: 'center'
58
+ }}>({onlineClients.join(',')})</Text>}
47
59
  </View>),
48
60
  headerRight: ({ tintColor }) => (<TouchableOpacity onPress={() => {
49
61
  navigation.navigate('ConversationSetting');
@@ -62,36 +74,36 @@ const MessageList = ({ navigation }) => {
62
74
  InteractionManager.runAfterInteractions(() => {
63
75
  imManager.store.dispatch(MessagePanelActions.saveDraftText(imManager));
64
76
  imManager.leaveConversation();
65
- imManager.store.dispatch(MessageActions.resetState());
77
+ imManager.store.dispatch(MessageActions.resetState({}));
66
78
  });
67
79
  }
68
80
  });
69
81
  return () => {
70
82
  remover();
71
83
  };
72
- }, [isMultiple, navigation, currentConversation]);
84
+ }, [isMultiple, navigation, currentConversation, onlineClients]);
73
85
  useEffect(() => {
74
86
  getImManager().setFlatListRef(messageListRef);
75
87
  getImManager().setInputRef(bar);
76
88
  }, []);
77
- useEffect(() => {
78
- var _a;
79
- if (isSearch) {
80
- (_a = messageListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToOffset({ animated: false, offset: 0 });
81
- getImManager().store.dispatch(MessageActions.setIsSearch(false));
89
+ const onPressPopover = useCallback((message, nativeEvent) => {
90
+ if (message.payloadType === PayloadType.PICTURE) {
91
+ setShowAlbum(true);
92
+ setSelectMessage(undefined);
93
+ setTargetMessageSeq(message.messageSeq);
82
94
  }
83
- }, [isSearch]);
84
- const onLongPressPopover = useCallback((message, nativeEvent) => {
85
- setNativeEvent(nativeEvent);
95
+ }, []);
96
+ const onLongPressPopover = useCallback((message, nativeEvent, layout) => {
86
97
  setSelectMessage(message);
98
+ setTouchPoint({
99
+ x: nativeEvent.pageX - nativeEvent.locationX,
100
+ y: nativeEvent.pageY - nativeEvent.locationY
101
+ });
102
+ setTargetLayout(layout);
103
+ }, []);
104
+ const onLayoutItem = useCallback((message, index, e) => {
105
+ itemLayout[index] = e.nativeEvent.layout.height;
87
106
  }, []);
88
- const onLayoutItem = useMemoizedFn((message, e) => {
89
- itemLayout.current[message.messageSeq] = e.nativeEvent.layout.height;
90
- if (rangeList.length > 0 && message.messageSeq === (tips === null || tips === void 0 ? void 0 : tips.messageSeq)) {
91
- const imManager = getImManager();
92
- imManager.store.dispatch(MessagePanelActions.onRangeLayout(imManager));
93
- }
94
- });
95
107
  const onLoadBefore = useMemoizedFn(() => {
96
108
  getImManager()
97
109
  .loadBeforeMessages(currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.id, { limit: 15 })
@@ -107,25 +119,41 @@ const MessageList = ({ navigation }) => {
107
119
  });
108
120
  });
109
121
  const renderItem = useMemoizedFn(({ item, index }) => {
110
- return (<MessageItem message={item} conversation={currentConversation} mine={item.messageFrom === user.userId} showTimeLine={showTimeLine(item, index, messages)} isMultiple={isMultiple} multipleSelect={multipleSelect} onLongPressPopover={onLongPressPopover} onPressPopover={onPressPopover} onLayoutItem={onLayoutItem}/>);
122
+ return (<MessageItem {...item} key={item.messageSeq} mine={item.messageFrom === user.userId} showTimeLine={showTimeLine(item, index, messages)} isMultiple={isMultiple} multipleSelect={multipleSelect} onLongPressPopover={onLongPressPopover} onPressPopover={onPressPopover} onLayoutItem={onLayoutItem} index={index}/>);
111
123
  });
112
124
  const onTouchStart = useCallback(() => {
113
125
  var _a;
114
126
  (_a = bar.current) === null || _a === void 0 ? void 0 : _a.reset();
115
127
  }, []);
116
- const onPressPopover = useCallback((message, nativeEvent) => {
117
- if (message.payloadType === PayloadType.PICTURE) {
118
- setNativeEvent(nativeEvent);
119
- setShowAlbum(true);
120
- setTargetMessageSeq(message.messageSeq);
121
- }
122
- }, []);
123
128
  const onKeyboardAt = () => {
124
129
  if ((currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.type) === ConversationType.C2G) {
125
130
  Keyboard.dismiss();
126
131
  navigation.navigate('ChooseMember', { atOther: true });
127
132
  }
128
133
  };
134
+ useEffect(() => {
135
+ if (focusMessageIndex != null) {
136
+ const jump = () => {
137
+ var _a;
138
+ let offset = 0;
139
+ for (let i = 0; i <= focusMessageIndex; i++) {
140
+ if (!itemLayout[i]) {
141
+ setTimeout(jump, 16);
142
+ return;
143
+ }
144
+ offset += itemLayout[i];
145
+ }
146
+ console.log('listHeight: ', listHeight);
147
+ (_a = messageListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToOffset({
148
+ offset: offset - listHeight.current / 2,
149
+ animated: true,
150
+ });
151
+ const imManager = getImManager();
152
+ imManager.store.dispatch(MessageActions.setFocusMessageIndex(null));
153
+ };
154
+ jump();
155
+ }
156
+ }, [focusMessageIndex]);
129
157
  return (<NavigationPage noPadding>
130
158
  {(currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.type) === ConversationType.B2C && (<View style={{ padding: dp(8), backgroundColor: '#f5f5f5', elevation: 1 }}>
131
159
  <Input shape="normal" placeholder={t('search')} wrapStyle={{ height: dp(40) }}/>
@@ -141,31 +169,33 @@ const MessageList = ({ navigation }) => {
141
169
  </TouchableWithoutFeedback>)}
142
170
 
143
171
  {messages.length > 0 && (<ChatBg fileId={currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.imageBackground}>
144
- <>
145
- <FlatList ref={messageListRef} inverted contentContainerStyle={{
146
- paddingVertical: dp(20),
147
- justifyContent: 'flex-end',
148
- flexGrow: 1,
149
- overflow: 'scroll',
150
- }} numColumns={1} onEndReachedThreshold={0.1} keyboardShouldPersistTaps={'handled'} keyExtractor={item => item.messageSeq} renderItem={renderItem} data={messages} onEndReached={onLoadBefore} refreshing={false}
172
+ <View style={styles.container}>
173
+ <FlatList ref={messageListRef} inverted contentContainerStyle={styles.list} numColumns={1} onEndReachedThreshold={0.1} keyboardShouldPersistTaps={'handled'} keyExtractor={item => item.messageSeq} renderItem={renderItem} initialNumToRender={messages.length} data={messages} onEndReached={onLoadBefore} refreshing={false}
151
174
  // 在头部增加数据时,保持滚动位置,0.72 以上可以使用
152
- maintainVisibleContentPosition={{
175
+ maintainVisibleContentPosition={messages.length > 10 ? {
153
176
  minIndexForVisible: 0,
154
- }} onRefresh={onLoadAfter} onTouchStart={onTouchStart}/>
177
+ } : undefined} onRefresh={onLoadAfter} onTouchStart={onTouchStart} onLayout={(e) => {
178
+ listHeight.current = e.nativeEvent.layout.height;
179
+ if (init.current) {
180
+ init.current = false;
181
+ messageListRef.current.scrollToOffset({ animated: false, offset: 0 });
182
+ }
183
+ }}/>
155
184
  {quote && (<MessageItemQuote {...quote} onDeleteQuote={() => {
156
185
  getImManager().store.dispatch(MessageActions.setQuote(null));
157
186
  }}/>)}
158
- </>
187
+ </View>
159
188
  </ChatBg>)}
160
189
 
161
190
  {!isB2C && (isMultiple ? <MultipleBar /> : <MessageBar ref={bar} onKeyboardAt={onKeyboardAt}/>)}
162
191
 
163
192
  {tips && tips.type !== TipsType.NONE && (<MessageItemTips {...tips}/>)}
164
- <MessageOption visible={Boolean(selectMessage)} nativeEvent={nativeEvent} message={selectMessage} setLoading={(value) => {
193
+ <MessageOption visible={Boolean(selectMessage)} touchPoint={touchPoint} nativeEventLayout={targetLayout} message={selectMessage} setLoading={(value) => {
165
194
  setLoading(value);
166
195
  }} onClose={() => {
167
- setNativeEvent(undefined);
168
196
  setSelectMessage(undefined);
197
+ setTargetLayout(undefined);
198
+ setTouchPoint(undefined);
169
199
  }}/>
170
200
 
171
201
  <MessagePictureAlbum visible={showAlbum} messages={messages} messageSeq={targetMessageSeq} onClose={() => {
@@ -195,5 +225,13 @@ const styles = StyleSheet.create({
195
225
  lineHeight: dp(16),
196
226
  color: "#999999"
197
227
  },
228
+ container: {
229
+ flex: 1,
230
+ },
231
+ list: {
232
+ paddingVertical: dp(20),
233
+ justifyContent: 'flex-end',
234
+ flexGrow: 1
235
+ }
198
236
  });
199
237
  export default MessageList;
@@ -1,50 +1,66 @@
1
1
  import ChatAvatar from '../../components/ChatAvatar/ChatAvatar';
2
2
  import FormatTimeText from '../../components/FormatTimeText';
3
3
  import { dp, NavigationPage } from '@smart-link/rn-ui';
4
- import React, { useEffect } from 'react';
4
+ import React, { useEffect, useMemo } from 'react';
5
5
  import { View, StyleSheet, FlatList, Text } from 'react-native';
6
6
  import MessagePayload from './components/MessagePayload';
7
+ import { MessageListActions } from '@smart-link/im-base';
7
8
  import MessagePictureAlbum from "./components/MessagePictureAlbum";
8
- import { cloneDeep } from "lodash-es";
9
+ import { useMessage } from "../../hooks/useImSelector";
10
+ import { getImManager } from "../../init";
11
+ import { loadVideoPlayer } from "../../slice/video/video.action";
9
12
  const MessageRecord = ({ navigation, route: { params } }) => {
10
- const { messages, title } = params;
13
+ const { messageSeq, title } = params;
14
+ const { messageListData } = useMessage();
11
15
  useEffect(() => {
12
16
  navigation.setOptions({
13
17
  title,
14
18
  });
19
+ return () => {
20
+ console.log('back');
21
+ getImManager().store.dispatch(MessageListActions.removeMessageListData());
22
+ };
15
23
  }, [navigation, title]);
16
24
  const listRef = React.useRef(null);
17
- const [updateMessages, setUpdateMessages] = React.useState([]);
18
25
  const [previewMessages, setPreviewMessages] = React.useState([]);
19
26
  const [showAlbum, setShowAlbum] = React.useState(false);
20
27
  const [targetMessageSeq, setTargetMessageSeq] = React.useState('');
28
+ const data = useMemo(() => {
29
+ var _a;
30
+ return ((_a = messageListData.find(item => item.messageSeq === messageSeq)) === null || _a === void 0 ? void 0 : _a.childMessages) || [];
31
+ }, [messageSeq, messageListData]);
21
32
  useEffect(() => {
22
33
  var _a;
23
- setUpdateMessages(cloneDeep(messages));
24
34
  (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.scrollToOffset({ animated: false, offset: 0 });
25
- }, [messages]);
35
+ }, [messageSeq]);
26
36
  const onPress = (item) => {
27
- var _a;
28
- // console.log('MessageRecord: ', item);
29
- if (((_a = item.payload.quote) === null || _a === void 0 ? void 0 : _a.quoteMessage.payloadType) === 'picture') {
30
- setShowAlbum(true);
31
- setTargetMessageSeq(item.payload.quote.quoteMessage.messageSeq);
32
- setPreviewMessages([item.payload.quote.quoteMessage]);
33
- return;
37
+ if (item.payload.quote) {
38
+ if (item.payload.quote.quoteMessage.payloadType === 'picture') {
39
+ setShowAlbum(true);
40
+ setTargetMessageSeq(item.payload.quote.quoteMessage.messageSeq);
41
+ setPreviewMessages([item.payload.quote.quoteMessage]);
42
+ return;
43
+ }
44
+ if (item.payload.quote.quoteMessage.payloadType === 'video') {
45
+ getImManager().store.dispatch(loadVideoPlayer({
46
+ messageSeq: item.payload.quote.quoteMessage.messageSeq,
47
+ payload: item.payload.quote.quoteMessage.payload,
48
+ isRecord: true
49
+ }));
50
+ }
34
51
  }
35
52
  if (item.payloadType === 'picture') {
36
53
  setShowAlbum(true);
37
54
  setTargetMessageSeq(item.messageSeq);
38
- setPreviewMessages(updateMessages);
55
+ setPreviewMessages(data);
39
56
  }
40
57
  };
41
58
  return (<NavigationPage noPadding>
42
- <FlatList ref={listRef} contentContainerStyle={styles.list} data={updateMessages} renderItem={({ item, index }) => {
43
- console.log('index: ', index);
44
- const isSamePerson = index > 0 && updateMessages.length > 1 && updateMessages[index - 1].messageFrom === item.messageFrom;
59
+ <FlatList ref={listRef} contentContainerStyle={styles.list} data={data} renderItem={({ item, index }) => {
60
+ const isSamePerson = index > 0 && data.length > 1 && data[index - 1].messageFrom === item.messageFrom;
45
61
  return (<View style={styles.messageItem}>
46
62
  <View style={styles.avatar}>
47
- {!isSamePerson && (<ChatAvatar id={item.messageFrom} url={item.payload.avatars} size={dp(40)} name={item.messageFromName}/>)}
63
+ {!isSamePerson && (<ChatAvatar id={item.messageFrom} size={dp(40)} name={item.messageFromName}/>)}
48
64
  </View>
49
65
  <View style={styles.content}>
50
66
  <View style={styles.info}>
@@ -53,11 +69,6 @@ const MessageRecord = ({ navigation, route: { params } }) => {
53
69
  </View>
54
70
  <MessagePayload {...item} isRecord direction="left" showArrow={false} onPress={() => {
55
71
  onPress(item);
56
- }} onLoad={(localPath) => {
57
- console.log('localPath: ', localPath);
58
- if (!item.payload.localPath) {
59
- item.payload.localPath = localPath;
60
- }
61
72
  }}/>
62
73
  </View>
63
74
  </View>);
@@ -1,16 +1,16 @@
1
- import { IConversation, IMessage } from '@smart-link/im-base';
1
+ import { IMessage } from '@smart-link/im-base';
2
2
  import React from 'react';
3
3
  import { NativeTouchEvent, LayoutChangeEvent } from 'react-native';
4
+ import { NativeEventLayout } from "../../types";
4
5
  type MessageItemProps = {
5
6
  isMultiple?: boolean;
6
7
  multipleSelect?: Record<string, IMessage>;
7
- conversation: IConversation;
8
- message: IMessage;
9
8
  mine: boolean;
10
9
  showTimeLine: boolean;
11
- onLongPressPopover: (message: IMessage, nativeEvent: NativeTouchEvent) => void;
10
+ index: number;
11
+ onLongPressPopover: (message: IMessage, nativeEvent: NativeTouchEvent, layout: NativeEventLayout) => void;
12
12
  onPressPopover: (message: IMessage, nativeEvent: NativeTouchEvent) => void;
13
- onLayoutItem: (item: IMessage, e: LayoutChangeEvent) => void;
14
- };
15
- declare const _default: React.MemoExoticComponent<({ isMultiple, multipleSelect, conversation, message, mine, showTimeLine, onPressPopover, onLongPressPopover, onLayoutItem, }: MessageItemProps) => React.JSX.Element>;
13
+ onLayoutItem: (item: IMessage, index: number, e: LayoutChangeEvent) => void;
14
+ } & IMessage;
15
+ declare const _default: React.MemoExoticComponent<({ isMultiple, multipleSelect, mine, index, showTimeLine, onPressPopover, onLongPressPopover, onLayoutItem, ...message }: MessageItemProps) => React.JSX.Element>;
16
16
  export default _default;