@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.
- package/assets/file-dir.jpg +0 -0
- package/dist/api/addressList.d.ts +16 -15
- package/dist/api/addressList.js +15 -1
- package/dist/api/user.js +1 -1
- package/dist/components/Camera/Camera.js +3 -2
- package/dist/components/Camera/CameraCapture.js +61 -12
- package/dist/components/ChatAvatar/ChatAvatar.d.ts +3 -3
- package/dist/components/ChatAvatar/ChatAvatar.js +51 -49
- package/dist/components/ChatAvatar/ChatAvatarId.d.ts +2 -2
- package/dist/components/ChatAvatar/ChatAvatarId.js +19 -46
- package/dist/components/ChatAvatar/ChatAvatarLocal.js +22 -10
- package/dist/components/Favicon.js +1 -1
- package/dist/components/LocalImage.js +3 -1
- package/dist/components/styles.d.ts +7 -1
- package/dist/components/styles.js +16 -11
- package/dist/default-assets.d.ts +1 -0
- package/dist/default-assets.js +1 -0
- package/dist/hooks/useAnimatedValue.d.ts +2 -0
- package/dist/hooks/useAnimatedValue.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init.d.ts +2 -2
- package/dist/init.js +8 -5
- package/dist/interface.d.ts +15 -0
- package/dist/pages/address-list/AddressList.js +22 -40
- package/dist/pages/address-list/ChooseContact.js +209 -45
- package/dist/pages/address-list/MyGroups.d.ts +6 -0
- package/dist/pages/address-list/MyGroups.js +90 -0
- package/dist/pages/address-list/Organization.d.ts +0 -7
- package/dist/pages/address-list/Organization.js +24 -108
- package/dist/pages/address-list/UserDetail.js +5 -5
- package/dist/pages/address-list/UserSearch.js +6 -7
- package/dist/pages/address-list/addressList.routes.d.ts +9 -0
- package/dist/pages/address-list/addressList.routes.js +8 -0
- package/dist/pages/address-list/components/Icons.d.ts +8 -0
- package/dist/pages/address-list/components/Icons.js +15 -0
- package/dist/pages/address-list/components/OrgPath.d.ts +12 -0
- package/dist/pages/address-list/components/OrgPath.js +59 -0
- package/dist/pages/address-list/components/RenderUserItem.d.ts +9 -0
- package/dist/pages/address-list/components/RenderUserItem.js +14 -0
- package/dist/pages/address-list/{UserJobs.js → components/UserJobs.js} +2 -2
- package/dist/pages/address-list/useGroupCategory.d.ts +2 -0
- package/dist/pages/address-list/useGroupCategory.js +11 -0
- package/dist/pages/address-list/useRoleList.d.ts +2 -0
- package/dist/pages/address-list/useRoleList.js +11 -0
- package/dist/pages/collection/Collection.js +52 -8
- package/dist/pages/collection/ContentFactory.js +50 -3
- package/dist/pages/conversation/ForwardToConversation.js +4 -2
- package/dist/pages/conversation/List.js +15 -7
- package/dist/pages/conversation/components/ConversationCard.js +1 -7
- package/dist/pages/conversation/setting/OptionAvatars.d.ts +1 -1
- package/dist/pages/conversation/setting/OptionAvatars.js +0 -1
- package/dist/pages/conversation/setting/OptionGroup.d.ts +2 -2
- package/dist/pages/conversation/setting/OptionGroup.js +4 -4
- package/dist/pages/conversation/setting/OptionGroupManage.js +4 -8
- package/dist/pages/conversation/setting/OptionGroupMoreMember.js +37 -18
- package/dist/pages/conversation/setting/Setting.js +5 -5
- package/dist/pages/conversation/setting/SettingChatBg.js +32 -16
- package/dist/pages/message/ChooseMember.js +2 -2
- package/dist/pages/message/FileSelector.d.ts +6 -0
- package/dist/pages/message/FileSelector.js +272 -0
- package/dist/pages/message/MessageList.js +81 -43
- package/dist/pages/message/MessageRecord.js +34 -23
- package/dist/pages/message/components/MessageItem.d.ts +7 -7
- package/dist/pages/message/components/MessageItem.js +29 -20
- package/dist/pages/message/components/MessageOption.d.ts +7 -3
- package/dist/pages/message/components/MessageOption.js +14 -16
- package/dist/pages/message/components/MessagePayload.d.ts +2 -1
- package/dist/pages/message/components/MessagePictureAlbum.js +6 -4
- package/dist/pages/message/components/Payload/PayloadFile.d.ts +1 -1
- package/dist/pages/message/components/Payload/PayloadFile.js +34 -16
- package/dist/pages/message/components/Payload/PayloadMultiple.js +12 -8
- package/dist/pages/message/components/Payload/PayloadNotify.js +14 -5
- package/dist/pages/message/components/Payload/PayloadPicture.d.ts +1 -1
- package/dist/pages/message/components/Payload/PayloadPicture.js +9 -7
- package/dist/pages/message/components/Payload/PayloadText.js +7 -8
- package/dist/pages/message/components/Payload/PayloadVideo.js +2 -1
- package/dist/pages/message/components/Payload/PayloadVoice.js +7 -13
- package/dist/pages/message/components/Payload/PayloadWrapper.d.ts +2 -1
- package/dist/pages/message/components/Payload/PayloadWrapper.js +46 -20
- package/dist/pages/message/components/Payload/type.d.ts +3 -1
- package/dist/pages/message/components/ReceiptBack.js +2 -2
- package/dist/pages/message/components/TextMixMessage.d.ts +2 -0
- package/dist/pages/message/components/TextMixMessage.js +10 -3
- package/dist/pages/message/components/TextMixQuote.js +10 -15
- package/dist/pages/message/components/TextMixQuoteMessage.d.ts +2 -1
- package/dist/pages/message/components/TextMixQuoteMessage.js +5 -5
- package/dist/pages/message/components/UploadProgress.d.ts +1 -1
- package/dist/pages/message/components/UploadProgress.js +1 -1
- package/dist/pages/message/components/messageBar/EmojiPanel.js +8 -9
- package/dist/pages/message/components/messageBar/MessageBar.d.ts +1 -1
- package/dist/pages/message/components/messageBar/MessageBar.js +14 -12
- package/dist/pages/message/components/messageBar/MessageInput.d.ts +1 -0
- package/dist/pages/message/components/messageBar/MessageInput.js +19 -1
- package/dist/pages/message/components/messageBar/OptionPanel.js +22 -44
- package/dist/pages/message/message.routes.js +10 -0
- package/dist/pages/search/components/SearchUser.js +2 -2
- package/dist/pages/types.d.ts +6 -2
- package/dist/slice/contact/contact.slice.js +1 -0
- package/dist/slice/video/video.action.js +9 -1
- package/dist/utils/common-action-sheet.d.ts +1 -0
- package/dist/utils/common-action-sheet.js +18 -0
- package/dist/utils/file-icon.js +29 -2
- package/dist/utils/file.d.ts +21 -2
- package/dist/utils/file.js +209 -42
- package/dist/utils/phone.d.ts +1 -1
- package/dist/utils/phone.js +1 -9
- package/dist/utils/request.d.ts +2 -1
- package/dist/utils/request.js +17 -6
- package/dist/utils/upload.d.ts +2 -4
- package/dist/utils/upload.js +13 -49
- package/package.json +5 -5
- package/dist/pages/address-list/Icons.d.ts +0 -5
- package/dist/pages/address-list/Icons.js +0 -9
- package/dist/slice/contact/contact.action.d.ts +0 -1
- package/dist/slice/contact/contact.action.js +0 -1
- package/dist/utils/cookie.d.ts +0 -2
- package/dist/utils/cookie.js +0 -25
- package/dist/utils/file-operate.d.ts +0 -1
- package/dist/utils/file-operate.js +0 -3
- package/dist/utils/text-mix.d.ts +0 -9
- package/dist/utils/text-mix.js +0 -75
- /package/dist/pages/address-list/{CardInfo.d.ts → components/CardInfo.d.ts} +0 -0
- /package/dist/pages/address-list/{CardInfo.js → components/CardInfo.js} +0 -0
- /package/dist/pages/address-list/{UserJobs.d.ts → components/UserJobs.d.ts} +0 -0
|
@@ -9,13 +9,50 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import {
|
|
13
|
-
import React, { memo } from 'react';
|
|
12
|
+
import React, { memo, useState } from 'react';
|
|
14
13
|
import { View, StyleSheet, TouchableOpacity } from 'react-native';
|
|
14
|
+
import { dp } from "@smart-link/rn-ui";
|
|
15
|
+
import Svg, { Path } from "react-native-svg";
|
|
16
|
+
import Color from "color";
|
|
17
|
+
// 通过 Svg 实现箭头
|
|
18
|
+
const SvgFromUri = ({ direction, focus }) => {
|
|
19
|
+
let fill = direction === 'left' ? '#fff' : '#cde6f9';
|
|
20
|
+
let stroke = direction === 'left' ? "#eee" : '#cde6f9';
|
|
21
|
+
if (focus) {
|
|
22
|
+
fill = Color(fill).darken(0.08).hex();
|
|
23
|
+
if (direction === 'right') {
|
|
24
|
+
stroke = Color(stroke).darken(0.08).hex();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return (<Svg width={12} height={12}>
|
|
28
|
+
<Path d={direction == 'left' ? "M 12 12 L 0 5 L 12 0" : "M 0 12 L 12 5 L 0 0"} fill={fill} stroke={stroke} strokeWidth={1}></Path>
|
|
29
|
+
</Svg>);
|
|
30
|
+
};
|
|
15
31
|
const PayloadWrapper = memo((_a) => {
|
|
16
|
-
var { children, direction, showArrow } = _a, retProps = __rest(_a, ["children", "direction", "showArrow"]);
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
var { children, direction, showArrow, onLongPress, style } = _a, retProps = __rest(_a, ["children", "direction", "showArrow", "onLongPress", "style"]);
|
|
33
|
+
const [layout, setLayout] = useState();
|
|
34
|
+
const [focus, setFocus] = useState(false);
|
|
35
|
+
let backgroundColor = direction === 'right' ? '#cde6f9' : '#fff';
|
|
36
|
+
if (focus) {
|
|
37
|
+
backgroundColor = Color(backgroundColor).darken(0.08).hex();
|
|
38
|
+
}
|
|
39
|
+
return (<TouchableOpacity {...retProps} style={[
|
|
40
|
+
style,
|
|
41
|
+
{
|
|
42
|
+
backgroundColor,
|
|
43
|
+
}
|
|
44
|
+
]} onLayout={e => {
|
|
45
|
+
setLayout(e.nativeEvent.layout);
|
|
46
|
+
}} onLongPress={(e) => {
|
|
47
|
+
onLongPress && onLongPress(e, layout);
|
|
48
|
+
}} activeOpacity={1} onPressIn={() => {
|
|
49
|
+
setFocus(true);
|
|
50
|
+
}} onPressOut={() => {
|
|
51
|
+
setFocus(false);
|
|
52
|
+
}}>
|
|
53
|
+
{showArrow && (<View style={[styles.arrow, direction === 'right' ? styles.rightArrow : styles.leftArrow]}>
|
|
54
|
+
<SvgFromUri direction={direction} focus={focus}/>
|
|
55
|
+
</View>)}
|
|
19
56
|
{children}
|
|
20
57
|
</TouchableOpacity>);
|
|
21
58
|
});
|
|
@@ -24,25 +61,14 @@ const styles = StyleSheet.create({
|
|
|
24
61
|
width: dp(12),
|
|
25
62
|
height: dp(12),
|
|
26
63
|
position: 'absolute',
|
|
27
|
-
top: dp(
|
|
28
|
-
zIndex:
|
|
29
|
-
borderStyle: 'solid',
|
|
30
|
-
borderTopWidth: dp(8),
|
|
31
|
-
borderTopColor: 'transparent',
|
|
32
|
-
borderBottomWidth: dp(8),
|
|
33
|
-
borderBottomColor: 'transparent',
|
|
64
|
+
top: dp(12),
|
|
65
|
+
zIndex: -10,
|
|
34
66
|
},
|
|
35
67
|
leftArrow: {
|
|
36
|
-
left: -dp(
|
|
37
|
-
borderRightColor: '#fff',
|
|
38
|
-
borderRightWidth: dp(8),
|
|
39
|
-
borderLeftWidth: 0,
|
|
68
|
+
left: -dp(10),
|
|
40
69
|
},
|
|
41
70
|
rightArrow: {
|
|
42
|
-
right: -dp(
|
|
43
|
-
borderLeftColor: '#e3e3e3',
|
|
44
|
-
borderLeftWidth: dp(8),
|
|
45
|
-
borderRightWidth: 0,
|
|
71
|
+
right: -dp(10),
|
|
46
72
|
},
|
|
47
73
|
});
|
|
48
74
|
export default PayloadWrapper;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { IMessage } from "@smart-link/im-base";
|
|
2
2
|
import { GestureResponderEvent, StyleProp, ViewStyle } from "react-native";
|
|
3
|
+
import { NativeEventLayout } from "../../../types";
|
|
3
4
|
export interface PayloadProps extends IMessage {
|
|
5
|
+
width?: number;
|
|
4
6
|
style?: StyleProp<ViewStyle>;
|
|
5
7
|
direction?: 'left' | 'right';
|
|
6
8
|
showArrow?: boolean;
|
|
7
9
|
isRecord?: boolean;
|
|
8
10
|
onPress?: (e: GestureResponderEvent) => void;
|
|
9
|
-
onLongPress?: (e: GestureResponderEvent) => void;
|
|
11
|
+
onLongPress?: (e: GestureResponderEvent, layout: NativeEventLayout) => void;
|
|
10
12
|
onLoad?: (localPath: string) => void;
|
|
11
13
|
}
|
|
@@ -6,11 +6,11 @@ import useTranslation from "../../../hooks/useTranslation";
|
|
|
6
6
|
import dayjs from "dayjs";
|
|
7
7
|
export const ReceiptBack = ({ message }) => {
|
|
8
8
|
const { t } = useTranslation();
|
|
9
|
-
// console.log('ReceiptBack: '
|
|
9
|
+
// console.log('ReceiptBack: ', message.receiptBackList.length, message.receiptBackList);
|
|
10
10
|
return (<View style={styles.content}>
|
|
11
11
|
<View style={{ flexDirection: 'row' }}>
|
|
12
12
|
<Text style={[styles.text, { width: dp(165) }]}>
|
|
13
|
-
{dayjs(message.messageTime).format('YYYY-MM-DD
|
|
13
|
+
{dayjs(message.messageTime).format('YYYY-MM-DD HH:mm:ss')}
|
|
14
14
|
</Text>
|
|
15
15
|
<Text style={styles.text}>{message.messageStatus === MessageStatus.EMIT_ERROR ? t('sendFail') : t('sent')}</Text>
|
|
16
16
|
</View>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { IGroupMember } from "@smart-link/im-base";
|
|
1
2
|
import React from 'react';
|
|
2
3
|
export type TextMixMessageProps = {
|
|
3
4
|
text: string;
|
|
5
|
+
atList?: IGroupMember[];
|
|
4
6
|
onLongPressPhone?: (phone: string) => void;
|
|
5
7
|
onLongPressURL?: (url: string) => void;
|
|
6
8
|
textStyle?: any;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { mix, mixAtList } from "@smart-link/im-base";
|
|
1
2
|
import React, { memo } from 'react';
|
|
2
3
|
import { Text, StyleSheet } from "react-native";
|
|
3
|
-
import { mix } from '../../../utils/text-mix';
|
|
4
4
|
const TextMixMessage = memo(props => {
|
|
5
|
-
const { text, onLongPressPhone, onLongPressURL, textStyle, } = props;
|
|
6
|
-
const viewItems = mix(text);
|
|
5
|
+
const { text, atList, onLongPressPhone, onLongPressURL, textStyle, } = props;
|
|
6
|
+
const viewItems = mixAtList(atList, mix(text));
|
|
7
7
|
return (<Text style={textStyle}>
|
|
8
8
|
{viewItems.map((item, index) => {
|
|
9
9
|
if (item.type === 'phone') {
|
|
@@ -21,6 +21,9 @@ const TextMixMessage = memo(props => {
|
|
|
21
21
|
{item.value}
|
|
22
22
|
</Text>);
|
|
23
23
|
}
|
|
24
|
+
else if (item.type === 'at') {
|
|
25
|
+
return (<Text key={index} style={styles.at}>{item.value}</Text>);
|
|
26
|
+
}
|
|
24
27
|
return item.value;
|
|
25
28
|
})}
|
|
26
29
|
</Text>);
|
|
@@ -33,5 +36,9 @@ const styles = StyleSheet.create({
|
|
|
33
36
|
color: '#208aff',
|
|
34
37
|
textDecorationLine: 'underline'
|
|
35
38
|
},
|
|
39
|
+
at: {
|
|
40
|
+
color: '#208aff',
|
|
41
|
+
fontWeight: "700"
|
|
42
|
+
}
|
|
36
43
|
});
|
|
37
44
|
export default TextMixMessage;
|
|
@@ -11,7 +11,6 @@ import LocalImage from '../../../components/LocalImage';
|
|
|
11
11
|
import useTranslation from '../../../hooks/useTranslation';
|
|
12
12
|
import { getImManager } from '../../../init';
|
|
13
13
|
import PayloadFile from './Payload/PayloadFile';
|
|
14
|
-
import useDownloadSource, { useDownloadImgSource } from "../../../hooks/useDownloadSource";
|
|
15
14
|
const voiceLeft = require('../../../../assets/voice-left.png');
|
|
16
15
|
const { width } = Dimensions.get('window');
|
|
17
16
|
export const QuoteTextMix = memo(props => {
|
|
@@ -27,10 +26,8 @@ export const QuoteTextMix = memo(props => {
|
|
|
27
26
|
});
|
|
28
27
|
export const QuotePicture = memo(props => {
|
|
29
28
|
const { quoteTitle, quoteMessage, quoteStyle, color, textStyle, borderColor, backgroundColor, onPressQuote } = props;
|
|
30
|
-
const { height, width } = quoteMessage.payload;
|
|
29
|
+
const { height, width, localPath } = quoteMessage.payload;
|
|
31
30
|
const theme = useTheme();
|
|
32
|
-
console.log('QuotePicture', quoteMessage);
|
|
33
|
-
const localImg = useDownloadSource(quoteMessage.payload);
|
|
34
31
|
const sizeStyle = calculate(height || 100, width || 61.8, 100);
|
|
35
32
|
return (<TouchableOpacity activeOpacity={0.5} onPress={onPressQuote} style={[quoteStyle, styles.leftBorder, { backgroundColor: dark(color, 6) }]}>
|
|
36
33
|
<Text style={[textStyle, styles.replyUserName]}>
|
|
@@ -38,7 +35,7 @@ export const QuotePicture = memo(props => {
|
|
|
38
35
|
{' : '}
|
|
39
36
|
</Text>
|
|
40
37
|
<View style={styles.h5}/>
|
|
41
|
-
{(!
|
|
38
|
+
{(!localPath) && (<View style={[
|
|
42
39
|
styles.pictureEmpty,
|
|
43
40
|
Object.assign({ backgroundColor,
|
|
44
41
|
borderColor }, sizeStyle),
|
|
@@ -47,23 +44,21 @@ export const QuotePicture = memo(props => {
|
|
|
47
44
|
<ActivityIndicator color={theme.primaryColor} animating={true}/>
|
|
48
45
|
</View>)}
|
|
49
46
|
|
|
50
|
-
{
|
|
47
|
+
{localPath && (<LocalImage style={sizeStyle} resizeMode="cover" localPath={localPath}/>)}
|
|
51
48
|
</TouchableOpacity>);
|
|
52
49
|
});
|
|
53
50
|
export const QuoteVideo = memo(props => {
|
|
54
51
|
const { quoteTitle, quoteMessage, quoteStyle, color, textStyle, borderColor, backgroundColor, onPressQuote } = props;
|
|
55
|
-
const { duration, height, width } = quoteMessage.payload;
|
|
52
|
+
const { duration, height, width, imagePath } = quoteMessage.payload;
|
|
56
53
|
const theme = useTheme();
|
|
57
|
-
console.log('QuoteVideo: ', quoteMessage);
|
|
58
54
|
const sizeStyle = calculate(height, width);
|
|
59
|
-
const localImg = useDownloadImgSource(quoteMessage.payload);
|
|
60
55
|
return (<TouchableOpacity activeOpacity={0.5} onPress={onPressQuote} style={[quoteStyle, styles.leftBorder, { backgroundColor: dark(color, 6) }]}>
|
|
61
56
|
<Text style={[textStyle, styles.replyUserName]}>
|
|
62
57
|
{quoteTitle}
|
|
63
58
|
{' : '}
|
|
64
59
|
</Text>
|
|
65
60
|
<View style={styles.h5}/>
|
|
66
|
-
{(!
|
|
61
|
+
{(!imagePath) && (<View style={[
|
|
67
62
|
styles.pictureEmpty,
|
|
68
63
|
Object.assign({ backgroundColor,
|
|
69
64
|
borderColor }, sizeStyle),
|
|
@@ -72,8 +67,8 @@ export const QuoteVideo = memo(props => {
|
|
|
72
67
|
<ActivityIndicator color={theme.primaryColor} animating={true}/>
|
|
73
68
|
</View>)}
|
|
74
69
|
|
|
75
|
-
{
|
|
76
|
-
<LocalImage style={sizeStyle} resizeMode="cover" localPath={
|
|
70
|
+
{imagePath && (<View style={{ width: sizeStyle.width }}>
|
|
71
|
+
<LocalImage style={sizeStyle} resizeMode="cover" localPath={imagePath}/>
|
|
77
72
|
<View style={styles.playView}>
|
|
78
73
|
<AntDesign size={30} name="playcircleo" color="#fff"/>
|
|
79
74
|
</View>
|
|
@@ -91,8 +86,8 @@ export const QuoteFile = memo(props => {
|
|
|
91
86
|
{' : '}
|
|
92
87
|
</Text>
|
|
93
88
|
<View style={styles.h5}/>
|
|
94
|
-
<View style={[styles.popoverFile, { backgroundColor: dark(color, 10) }]}>
|
|
95
|
-
<PayloadFile {...quoteMessage} style={{ flex: 1 }} showArrow={false}/>
|
|
89
|
+
<View style={[styles.popoverFile, { backgroundColor: dark(color, 10), paddingHorizontal: dp(5), flexDirection: 'column' }]}>
|
|
90
|
+
<PayloadFile {...quoteMessage} style={{ flex: 1, padding: dp(5) }} width={width - dp(155)} showArrow={false}/>
|
|
96
91
|
</View>
|
|
97
92
|
</TouchableOpacity>);
|
|
98
93
|
});
|
|
@@ -190,7 +185,7 @@ const styles = StyleSheet.create({
|
|
|
190
185
|
popoverFile: {
|
|
191
186
|
backgroundColor: '#fff',
|
|
192
187
|
flexDirection: 'row',
|
|
193
|
-
|
|
188
|
+
paddingTop: dp(8),
|
|
194
189
|
width: width - dp(145),
|
|
195
190
|
},
|
|
196
191
|
voiceImage: {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { IMessageQuote } from '@smart-link/im-base';
|
|
2
|
+
import { IGroupMember, IMessageQuote } from '@smart-link/im-base';
|
|
3
3
|
interface QuoteTextMixProps {
|
|
4
4
|
text: string;
|
|
5
5
|
quote?: IMessageQuote | null;
|
|
6
|
+
atList?: IGroupMember[];
|
|
6
7
|
textStyle?: any;
|
|
7
8
|
quoteBgColor: string;
|
|
8
9
|
quoteStyle?: any;
|
|
@@ -5,7 +5,7 @@ import TextMixMessage from './TextMixMessage';
|
|
|
5
5
|
import { PayloadType } from '@smart-link/im-base';
|
|
6
6
|
const { TEXT, VOICE, VIDEO, PICTURE, FILE, MULTIPLE } = PayloadType;
|
|
7
7
|
const TextMixQuoteMessage = memo(props => {
|
|
8
|
-
const { text, quote, textStyle, quoteStyle, quoteBgColor, onLongPressPhone, onLongPressURL, onPressQuote } = props;
|
|
8
|
+
const { text, quote, atList, textStyle, quoteStyle, quoteBgColor, onLongPressPhone, onLongPressURL, onPressQuote } = props;
|
|
9
9
|
let quotePayloadType;
|
|
10
10
|
let quoteMessageSeq;
|
|
11
11
|
const haveQuote = quote && Object.keys(quote).length > 0;
|
|
@@ -20,15 +20,15 @@ const TextMixQuoteMessage = memo(props => {
|
|
|
20
20
|
}}/>)}
|
|
21
21
|
|
|
22
22
|
{quotePayloadType === PICTURE && (<QuotePicture quoteTitle={quote.quoteTitle} quoteMessage={quote.quoteMessage} quoteStyle={quoteStyle} color={quoteBgColor} textStyle={textStyle} backgroundColor={quoteBgColor} borderColor={quoteBgColor} onPressQuote={() => {
|
|
23
|
-
onPressQuote
|
|
23
|
+
onPressQuote === null || onPressQuote === void 0 ? void 0 : onPressQuote(quoteMessageSeq);
|
|
24
24
|
}}/>)}
|
|
25
25
|
|
|
26
26
|
{quotePayloadType === VIDEO && (<QuoteVideo quoteTitle={quote.quoteTitle} quoteMessage={quote.quoteMessage} quoteStyle={quoteStyle} color={quoteBgColor} textStyle={textStyle} backgroundColor={quoteBgColor} borderColor={quoteBgColor} onPressQuote={() => {
|
|
27
|
-
onPressQuote
|
|
27
|
+
onPressQuote === null || onPressQuote === void 0 ? void 0 : onPressQuote(quoteMessageSeq);
|
|
28
28
|
}}/>)}
|
|
29
29
|
|
|
30
30
|
{quotePayloadType === FILE && (<QuoteFile quoteTitle={quote.quoteTitle} quoteMessage={quote.quoteMessage} color={quoteBgColor} textStyle={textStyle} onPressQuote={() => {
|
|
31
|
-
onPressQuote
|
|
31
|
+
onPressQuote === null || onPressQuote === void 0 ? void 0 : onPressQuote(quoteMessageSeq);
|
|
32
32
|
}}/>)}
|
|
33
33
|
|
|
34
34
|
{quotePayloadType === VOICE && (<QuoteVoice quoteTitle={quote.quoteTitle} quoteMessage={quote.quoteMessage} color={quoteBgColor} textStyle={textStyle} onPressQuote={() => {
|
|
@@ -40,7 +40,7 @@ const TextMixQuoteMessage = memo(props => {
|
|
|
40
40
|
<View style={{ height: 10 }}/>
|
|
41
41
|
</>)}
|
|
42
42
|
|
|
43
|
-
<TextMixMessage text={text} textStyle={textStyle} onLongPressPhone={onLongPressPhone} onLongPressURL={onLongPressURL}/>
|
|
43
|
+
<TextMixMessage text={text} atList={atList} textStyle={textStyle} onLongPressPhone={onLongPressPhone} onLongPressURL={onLongPressURL}/>
|
|
44
44
|
</>);
|
|
45
45
|
});
|
|
46
46
|
export default TextMixQuoteMessage;
|
|
@@ -11,7 +11,7 @@ const UploadProgress = ({ show, sendSize, totalSize, width }) => {
|
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
13
13
|
const progress = sendSize / totalSize;
|
|
14
|
-
return (<View>
|
|
14
|
+
return (<View style={{ width }}>
|
|
15
15
|
<Bar progress={progress} width={width} height={dp(3)} color={'#999'} borderRadius={dp(1)}/>
|
|
16
16
|
<Text style={styles.rate}>{numberToFileSize(sendSize) + ' / ' + numberToFileSize(totalSize)}</Text>
|
|
17
17
|
</View>);
|
|
@@ -2,16 +2,16 @@ import React, { forwardRef, memo, useImperativeHandle, useRef, useState } from '
|
|
|
2
2
|
import { View, Text, FlatList, Animated, TouchableOpacity, Dimensions } from 'react-native';
|
|
3
3
|
import { face } from '../../../../utils/emoji';
|
|
4
4
|
import { DeleteSvg } from './Icons';
|
|
5
|
+
import { useAnimatedValue } from "../../../../hooks/useAnimatedValue";
|
|
5
6
|
const emojis = Array.from(new Set([...face]));
|
|
6
7
|
const EMOJI_SIZE = (Dimensions.get('screen').width - 20) / 8;
|
|
7
8
|
const keyExtractor = (item) => item;
|
|
8
9
|
const EmojiPanel = forwardRef(({ onCheck, onBackspace }, ref) => {
|
|
9
|
-
const visible =
|
|
10
|
-
const [, forceUpdate] = useState({});
|
|
10
|
+
const [visible, setVisible] = useState(false);
|
|
11
11
|
const timer = useRef(null);
|
|
12
12
|
const start = useRef(false);
|
|
13
13
|
//通过动画设置表情面板高度
|
|
14
|
-
const
|
|
14
|
+
const heightAnim = useAnimatedValue(0);
|
|
15
15
|
const renderItem = ({ item }) => (<TouchableOpacity style={{
|
|
16
16
|
width: EMOJI_SIZE,
|
|
17
17
|
height: EMOJI_SIZE,
|
|
@@ -23,27 +23,26 @@ const EmojiPanel = forwardRef(({ onCheck, onBackspace }, ref) => {
|
|
|
23
23
|
</TouchableOpacity>);
|
|
24
24
|
useImperativeHandle(ref, () => ({
|
|
25
25
|
show: (height) => {
|
|
26
|
-
|
|
26
|
+
setVisible(true);
|
|
27
27
|
heightAnim.setValue(height);
|
|
28
|
-
forceUpdate({});
|
|
29
28
|
},
|
|
30
29
|
hide: () => {
|
|
31
|
-
|
|
30
|
+
setVisible(false);
|
|
32
31
|
heightAnim.setValue(0);
|
|
33
|
-
forceUpdate({});
|
|
34
32
|
},
|
|
35
33
|
}), []);
|
|
34
|
+
console.log('EmojiPanel: ', heightAnim);
|
|
36
35
|
return (<Animated.View style={{
|
|
37
36
|
height: heightAnim,
|
|
38
37
|
position: 'relative',
|
|
39
38
|
flexDirection: 'column',
|
|
40
|
-
paddingBottom: visible
|
|
39
|
+
paddingBottom: visible ? 20 : 0,
|
|
41
40
|
}} pointerEvents='auto'>
|
|
42
41
|
<FlatList numColumns={8} initialNumToRender={80} data={emojis} keyExtractor={keyExtractor} contentContainerStyle={{
|
|
43
42
|
paddingHorizontal: 10,
|
|
44
43
|
paddingBottom: 50,
|
|
45
44
|
}} pointerEvents='auto' keyboardShouldPersistTaps={'handled'} renderItem={renderItem} scrollEnabled/>
|
|
46
|
-
{visible
|
|
45
|
+
{visible && <View style={{
|
|
47
46
|
position: 'absolute',
|
|
48
47
|
height: EMOJI_SIZE,
|
|
49
48
|
width: 80,
|
|
@@ -5,7 +5,7 @@ export interface MessageBarProps {
|
|
|
5
5
|
export interface MessageBarMethods {
|
|
6
6
|
reset(): void;
|
|
7
7
|
focusTextInput(): void;
|
|
8
|
-
|
|
8
|
+
inertText(text: string): void;
|
|
9
9
|
setMessageText(text: string): void;
|
|
10
10
|
}
|
|
11
11
|
declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<MessageBarProps & React.RefAttributes<MessageBarMethods>>>;
|
|
@@ -56,6 +56,10 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
|
|
|
56
56
|
var _a;
|
|
57
57
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.insertEmoji(emoji);
|
|
58
58
|
}, []);
|
|
59
|
+
const onEmojiBack = useCallback(() => {
|
|
60
|
+
var _a;
|
|
61
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.backspace();
|
|
62
|
+
}, []);
|
|
59
63
|
const switchVoice = useCallback(() => {
|
|
60
64
|
var _a, _b;
|
|
61
65
|
(_a = emojiPanelRef.current) === null || _a === void 0 ? void 0 : _a.hide();
|
|
@@ -85,15 +89,13 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
|
|
|
85
89
|
}, timeout);
|
|
86
90
|
}, []);
|
|
87
91
|
const switchEmojiInputMode = useCallback(() => {
|
|
92
|
+
var _a, _b, _c;
|
|
88
93
|
modeRef.current = 'emoji';
|
|
89
94
|
setInputMode('emoji');
|
|
90
95
|
Keyboard.dismiss();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
(_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.show(keyboardHeight.current);
|
|
95
|
-
(_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.focusNoSoftKeyboard();
|
|
96
|
-
}, keyboardDuration.current);
|
|
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();
|
|
97
99
|
}, []);
|
|
98
100
|
const switchOptionPanelMode = useMemoizedFn(() => {
|
|
99
101
|
var _a;
|
|
@@ -111,7 +113,7 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
|
|
|
111
113
|
}, keyboardDuration.current);
|
|
112
114
|
});
|
|
113
115
|
const onKeyPress = useCallback((e) => {
|
|
114
|
-
if (e.nativeEvent.key
|
|
116
|
+
if (['@', '@'].includes(e.nativeEvent.key)) {
|
|
115
117
|
if (!debounceAt.current) {
|
|
116
118
|
debounceAt.current = debounce(() => {
|
|
117
119
|
onKeyboardAt && onKeyboardAt();
|
|
@@ -162,16 +164,15 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
|
|
|
162
164
|
(_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide();
|
|
163
165
|
(_c = optionPanelRef.current) === null || _c === void 0 ? void 0 : _c.hide();
|
|
164
166
|
},
|
|
165
|
-
|
|
167
|
+
inertText: (text) => {
|
|
166
168
|
var _a, _b, _c;
|
|
167
|
-
|
|
168
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.setText(tempText.current);
|
|
169
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.insertText(text);
|
|
169
170
|
setTimeout(() => {
|
|
170
171
|
var _a;
|
|
171
172
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
172
173
|
}, keyboardDuration.current);
|
|
173
174
|
(_b = emojiPanelRef.current) === null || _b === void 0 ? void 0 : _b.hide();
|
|
174
|
-
(_c = optionPanelRef.current) === null || _c === void 0 ? void 0 : _c.hide
|
|
175
|
+
(_c = optionPanelRef.current) === null || _c === void 0 ? void 0 : _c.hide;
|
|
175
176
|
},
|
|
176
177
|
setMessageText: (text) => {
|
|
177
178
|
var _a, _b, _c;
|
|
@@ -199,6 +200,7 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
|
|
|
199
200
|
}
|
|
200
201
|
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', event => {
|
|
201
202
|
var _a, _b;
|
|
203
|
+
console.log(event.endCoordinates.height);
|
|
202
204
|
keyboardHeight.current = Math.max(event.endCoordinates.height, 240);
|
|
203
205
|
keyboardDuration.current = event.duration;
|
|
204
206
|
(_a = emojiPanelRef.current) === null || _a === void 0 ? void 0 : _a.hide();
|
|
@@ -236,7 +238,7 @@ const MessageBar = forwardRef(({ onKeyboardAt }, ref) => {
|
|
|
236
238
|
</TouchableOpacity>)}
|
|
237
239
|
</View>
|
|
238
240
|
</View>
|
|
239
|
-
<EmojiPanel ref={emojiPanelRef} onCheck={handleInsertEmoji} onBackspace={
|
|
241
|
+
<EmojiPanel ref={emojiPanelRef} onCheck={handleInsertEmoji} onBackspace={onEmojiBack}/>
|
|
240
242
|
<OptionPanel ref={optionPanelRef}/>
|
|
241
243
|
</>);
|
|
242
244
|
});
|
|
@@ -15,6 +15,7 @@ export type MessageInputMethods = {
|
|
|
15
15
|
setText: (text: string) => void;
|
|
16
16
|
getText: () => string;
|
|
17
17
|
insertEmoji: (text: string) => void;
|
|
18
|
+
insertText: (text: string) => void;
|
|
18
19
|
backspace: () => void;
|
|
19
20
|
};
|
|
20
21
|
declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<MessageInputProps & React.RefAttributes<MessageInputMethods>>>;
|
|
@@ -58,6 +58,25 @@ const MessageInput = forwardRef(({ onChangeText, onKeyPress, style }, ref) => {
|
|
|
58
58
|
(_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.setSelection(start + emoji.length, start + emoji.length);
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
|
+
insertText(text) {
|
|
62
|
+
var _a, _b, _c;
|
|
63
|
+
const { start, end } = selection;
|
|
64
|
+
let newText = ((_a = innerText.current) === null || _a === void 0 ? void 0 : _a.slice(0, start)) + text + ((_b = innerText.current) === null || _b === void 0 ? void 0 : _b.slice(end));
|
|
65
|
+
onChangeText === null || onChangeText === void 0 ? void 0 : onChangeText(newText);
|
|
66
|
+
innerText.current = newText;
|
|
67
|
+
setText(newText);
|
|
68
|
+
setSelection({
|
|
69
|
+
start: start + text.length,
|
|
70
|
+
end: start + text.length,
|
|
71
|
+
});
|
|
72
|
+
if (Platform.OS === 'ios') {
|
|
73
|
+
selectionIos.current = {
|
|
74
|
+
start: start + text.length,
|
|
75
|
+
end: start + text.length,
|
|
76
|
+
};
|
|
77
|
+
(_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.setSelection(start + text.length, start + text.length);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
61
80
|
getText: () => {
|
|
62
81
|
return innerText.current;
|
|
63
82
|
},
|
|
@@ -152,7 +171,6 @@ const MessageInput = forwardRef(({ onChangeText, onKeyPress, style }, ref) => {
|
|
|
152
171
|
const styles = StyleSheet.create({
|
|
153
172
|
container: {
|
|
154
173
|
flex: 1,
|
|
155
|
-
position: 'relative',
|
|
156
174
|
marginVertical: dp(6),
|
|
157
175
|
maxHeight: dp(120),
|
|
158
176
|
},
|
|
@@ -7,8 +7,7 @@ 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 { dp,
|
|
11
|
-
import * as DocumentPicker from '@react-native-documents/picker';
|
|
10
|
+
import { dp, useNavigation } from '@smart-link/rn-ui';
|
|
12
11
|
import React, { forwardRef, memo, useImperativeHandle, useState } from 'react';
|
|
13
12
|
import { View, Text, FlatList, TouchableOpacity, Dimensions, StyleSheet, Animated } from 'react-native';
|
|
14
13
|
import { useConversation } from '../../../../hooks/useImSelector';
|
|
@@ -17,17 +16,18 @@ import { getImManager } from '../../../../init';
|
|
|
17
16
|
import { FileIcon, PhotosIcon, ShootingIcon, UserIcon } from './Icons';
|
|
18
17
|
import ImagePicker from 'react-native-image-crop-picker';
|
|
19
18
|
import { startSendContact } from '../../../../slice/contact/contact.slice';
|
|
20
|
-
import { copyFile
|
|
19
|
+
import { copyFile } from '../../../../utils/file';
|
|
21
20
|
import { scrollToBottom } from '../../../../utils/scroll';
|
|
22
21
|
import { takeCamera } from '../../../../utils/take-camera';
|
|
23
22
|
import FontAwesome from "react-native-vector-icons/FontAwesome";
|
|
23
|
+
import { useAnimatedValue } from "../../../../hooks/useAnimatedValue";
|
|
24
24
|
const OPTION_SIZE = (Dimensions.get('screen').width - 20) / 4;
|
|
25
25
|
const keyExtractor = (item) => item.id;
|
|
26
26
|
const OptionPanel = forwardRef((props, ref) => {
|
|
27
27
|
const navigation = useNavigation();
|
|
28
28
|
const [visible, setVisible] = useState(false);
|
|
29
29
|
const { currentConversation } = useConversation();
|
|
30
|
-
const
|
|
30
|
+
const heightAnim = useAnimatedValue(0);
|
|
31
31
|
const { t } = useTranslation();
|
|
32
32
|
const imManager = getImManager();
|
|
33
33
|
useImperativeHandle(ref, () => ({
|
|
@@ -43,17 +43,18 @@ const OptionPanel = forwardRef((props, ref) => {
|
|
|
43
43
|
}), [visible]);
|
|
44
44
|
const sendPicture = (data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
45
|
scrollToBottom();
|
|
46
|
-
yield
|
|
47
|
-
});
|
|
48
|
-
const sendVideo = (data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
-
scrollToBottom();
|
|
50
|
-
yield getImManager().sendVideoMessage(Object.assign({}, currentConversation), data);
|
|
51
|
-
});
|
|
52
|
-
const sendFile = (data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
-
scrollToBottom();
|
|
54
|
-
const destPath = yield copyFile(data.localPath, data.filename, 'file');
|
|
55
|
-
yield getImManager().sendFileMessage(Object.assign({}, currentConversation), Object.assign(Object.assign({}, data), { localPath: destPath }));
|
|
46
|
+
yield imManager.sendPictureMessage(Object.assign({}, currentConversation), data);
|
|
56
47
|
});
|
|
48
|
+
// const sendVideo = async (data: IPayload) => {
|
|
49
|
+
// scrollToBottom();
|
|
50
|
+
// await imManager.sendVideoMessage({...currentConversation!}, data);
|
|
51
|
+
// };
|
|
52
|
+
//
|
|
53
|
+
// const sendFile = async (data: IPayload) => {
|
|
54
|
+
// scrollToBottom();
|
|
55
|
+
// const destPath = await copyFile(data.localPath as string, data.filename!, 'file');
|
|
56
|
+
// await imManager.sendFileMessage({...currentConversation!}, {...data, localPath: destPath});
|
|
57
|
+
// };
|
|
57
58
|
const openAlbum = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
58
59
|
try {
|
|
59
60
|
const images = yield ImagePicker.openPicker({
|
|
@@ -63,17 +64,18 @@ const OptionPanel = forwardRef((props, ref) => {
|
|
|
63
64
|
compressImageMaxWidth: 1080,
|
|
64
65
|
});
|
|
65
66
|
console.log(images);
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
for (const item of images) {
|
|
68
|
+
const localPath = yield copyFile(item.path, item.filename, 'picture');
|
|
69
|
+
yield sendPicture({
|
|
68
70
|
filename: item.filename,
|
|
69
71
|
size: item.size,
|
|
70
|
-
localPath:
|
|
72
|
+
localPath: localPath,
|
|
71
73
|
type: item.mime,
|
|
72
74
|
lastModified: Number(item.modificationDate),
|
|
73
75
|
width: item.width,
|
|
74
76
|
height: item.height,
|
|
75
77
|
});
|
|
76
|
-
}
|
|
78
|
+
}
|
|
77
79
|
}
|
|
78
80
|
catch (e) {
|
|
79
81
|
console.log(e);
|
|
@@ -89,31 +91,7 @@ const OptionPanel = forwardRef((props, ref) => {
|
|
|
89
91
|
});
|
|
90
92
|
const pickFile = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
93
|
try {
|
|
92
|
-
|
|
93
|
-
type: [DocumentPicker.types.allFiles],
|
|
94
|
-
});
|
|
95
|
-
const fileInfo = files[0];
|
|
96
|
-
console.log('chooseFile: ', fileInfo);
|
|
97
|
-
if (!fileInfo.uri) {
|
|
98
|
-
Toast.error(t("failedToObtainFilePath"));
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const ext = getExtensionFromMime(fileInfo.type);
|
|
102
|
-
if (!ext) {
|
|
103
|
-
Toast.error(t("unsupportedFileTypes"));
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (!fileInfo.name.endsWith(ext)) {
|
|
107
|
-
fileInfo.name = fileInfo.name + ext;
|
|
108
|
-
}
|
|
109
|
-
yield sendFile({
|
|
110
|
-
// ios 中文名称编码了,需要解码
|
|
111
|
-
localPath: decodeURI(fileInfo.uri),
|
|
112
|
-
filename: fileInfo.name,
|
|
113
|
-
size: fileInfo.size,
|
|
114
|
-
type: fileInfo.type,
|
|
115
|
-
lastModified: Date.now(),
|
|
116
|
-
});
|
|
94
|
+
navigation.navigate('FileSelector');
|
|
117
95
|
}
|
|
118
96
|
catch (e) {
|
|
119
97
|
console.log(e);
|
|
@@ -155,7 +133,7 @@ const OptionPanel = forwardRef((props, ref) => {
|
|
|
155
133
|
{
|
|
156
134
|
id: 'star',
|
|
157
135
|
icon: <FontAwesome name="star" color="#000" size={32}/>,
|
|
158
|
-
text: t('
|
|
136
|
+
text: t('collected'),
|
|
159
137
|
onPress: () => {
|
|
160
138
|
navigation.navigate('Collection');
|
|
161
139
|
},
|
|
@@ -2,6 +2,7 @@ import MessageList from './MessageList';
|
|
|
2
2
|
import ChooseMember from './ChooseMember';
|
|
3
3
|
import MessageRecord from './MessageRecord';
|
|
4
4
|
import MessageBackup, { StartBackupPage, RestoreBackupPage } from './MessageBackup';
|
|
5
|
+
import FileSelector from "./FileSelector";
|
|
5
6
|
export const messageRoutes = [
|
|
6
7
|
{
|
|
7
8
|
name: 'MessageList',
|
|
@@ -26,6 +27,15 @@ export const messageRoutes = [
|
|
|
26
27
|
title: '',
|
|
27
28
|
},
|
|
28
29
|
},
|
|
30
|
+
{
|
|
31
|
+
name: 'FileSelector',
|
|
32
|
+
component: FileSelector,
|
|
33
|
+
options: {
|
|
34
|
+
title: '',
|
|
35
|
+
animation: 'fade_from_bottom',
|
|
36
|
+
animationDuration: 150,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
29
39
|
{
|
|
30
40
|
name: 'MessageBackup',
|
|
31
41
|
component: MessageBackup,
|