@unif/react-native-chat 3.2.4 → 3.3.0
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/lib/module/components/Attachments/AttachmentItem.js +3 -2
- package/lib/module/components/Attachments/AttachmentItem.js.map +1 -1
- package/lib/module/components/Attachments/Attachments.js +1 -0
- package/lib/module/components/Attachments/Attachments.js.map +1 -1
- package/lib/module/components/Attachments/styles.js +4 -0
- package/lib/module/components/Attachments/styles.js.map +1 -1
- package/lib/module/components/Composer/Composer.js +13 -9
- package/lib/module/components/Composer/Composer.js.map +1 -1
- package/lib/module/components/Confirmation/Confirmation.js +29 -13
- package/lib/module/components/Confirmation/Confirmation.js.map +1 -1
- package/lib/module/components/Confirmation/styles.js +17 -6
- package/lib/module/components/Confirmation/styles.js.map +1 -1
- package/lib/module/components/Message/MarkdownContent/styles.js +29 -33
- package/lib/module/components/Message/MarkdownContent/styles.js.map +1 -1
- package/lib/module/components/Message/Message.js +37 -24
- package/lib/module/components/Message/Message.js.map +1 -1
- package/lib/module/components/Message/MessageWaiting.js +53 -0
- package/lib/module/components/Message/MessageWaiting.js.map +1 -0
- package/lib/module/components/Message/constants.js +3 -0
- package/lib/module/components/Message/constants.js.map +1 -1
- package/lib/module/components/Message/styles.js +41 -3
- package/lib/module/components/Message/styles.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +60 -31
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/components/MessageList/MessageListEndButton.js +60 -0
- package/lib/module/components/MessageList/MessageListEndButton.js.map +1 -0
- package/lib/module/components/MessageList/MessageListRow.js +9 -2
- package/lib/module/components/MessageList/MessageListRow.js.map +1 -1
- package/lib/module/components/MessageList/styles.js +6 -0
- package/lib/module/components/MessageList/styles.js.map +1 -1
- package/lib/module/components/MessageList/useMessageListScroll.js +218 -13
- package/lib/module/components/MessageList/useMessageListScroll.js.map +1 -1
- package/lib/module/components/Process/Process.js +15 -5
- package/lib/module/components/Process/Process.js.map +1 -1
- package/lib/module/components/Process/ProcessCompactStep.js +21 -12
- package/lib/module/components/Process/ProcessCompactStep.js.map +1 -1
- package/lib/module/components/Process/ProcessTimelineStep.js +79 -0
- package/lib/module/components/Process/ProcessTimelineStep.js.map +1 -0
- package/lib/module/components/Process/styles.js +63 -3
- package/lib/module/components/Process/styles.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/components/Attachments/AttachmentItem.d.ts +1 -1
- package/lib/typescript/src/components/Attachments/AttachmentItem.d.ts.map +1 -1
- package/lib/typescript/src/components/Attachments/Attachments.d.ts.map +1 -1
- package/lib/typescript/src/components/Attachments/styles.d.ts +4 -0
- package/lib/typescript/src/components/Attachments/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/Attachments/types.d.ts +2 -1
- package/lib/typescript/src/components/Attachments/types.d.ts.map +1 -1
- package/lib/typescript/src/components/Composer/Composer.d.ts.map +1 -1
- package/lib/typescript/src/components/Confirmation/Confirmation.d.ts.map +1 -1
- package/lib/typescript/src/components/Confirmation/styles.d.ts +13 -2
- package/lib/typescript/src/components/Confirmation/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/Message/MarkdownContent/styles.d.ts +32 -66
- package/lib/typescript/src/components/Message/MarkdownContent/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/Message/Message.d.ts.map +1 -1
- package/lib/typescript/src/components/Message/MessageWaiting.d.ts +3 -0
- package/lib/typescript/src/components/Message/MessageWaiting.d.ts.map +1 -0
- package/lib/typescript/src/components/Message/constants.d.ts +3 -0
- package/lib/typescript/src/components/Message/constants.d.ts.map +1 -1
- package/lib/typescript/src/components/Message/styles.d.ts +40 -2
- package/lib/typescript/src/components/Message/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/Message/types.d.ts +5 -0
- package/lib/typescript/src/components/Message/types.d.ts.map +1 -1
- package/lib/typescript/src/components/MessageList/MessageList.d.ts.map +1 -1
- package/lib/typescript/src/components/MessageList/MessageListEndButton.d.ts +3 -0
- package/lib/typescript/src/components/MessageList/MessageListEndButton.d.ts.map +1 -0
- package/lib/typescript/src/components/MessageList/MessageListRow.d.ts +2 -8
- package/lib/typescript/src/components/MessageList/MessageListRow.d.ts.map +1 -1
- package/lib/typescript/src/components/MessageList/index.d.ts +1 -1
- package/lib/typescript/src/components/MessageList/index.d.ts.map +1 -1
- package/lib/typescript/src/components/MessageList/styles.d.ts +6 -0
- package/lib/typescript/src/components/MessageList/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/MessageList/types.d.ts +41 -2
- package/lib/typescript/src/components/MessageList/types.d.ts.map +1 -1
- package/lib/typescript/src/components/MessageList/useMessageListScroll.d.ts +14 -3
- package/lib/typescript/src/components/MessageList/useMessageListScroll.d.ts.map +1 -1
- package/lib/typescript/src/components/Process/Process.d.ts.map +1 -1
- package/lib/typescript/src/components/Process/ProcessCompactStep.d.ts +1 -1
- package/lib/typescript/src/components/Process/ProcessCompactStep.d.ts.map +1 -1
- package/lib/typescript/src/components/Process/ProcessTimelineStep.d.ts +5 -0
- package/lib/typescript/src/components/Process/ProcessTimelineStep.d.ts.map +1 -0
- package/lib/typescript/src/components/Process/styles.d.ts +62 -2
- package/lib/typescript/src/components/Process/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/Process/types.d.ts +7 -2
- package/lib/typescript/src/components/Process/types.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Attachments/AttachmentItem.tsx +11 -3
- package/src/components/Attachments/Attachments.tsx +1 -0
- package/src/components/Attachments/styles.ts +1 -0
- package/src/components/Attachments/types.ts +2 -1
- package/src/components/Composer/Composer.tsx +34 -8
- package/src/components/Confirmation/Confirmation.tsx +50 -14
- package/src/components/Confirmation/styles.ts +13 -6
- package/src/components/Message/MarkdownContent/styles.ts +18 -44
- package/src/components/Message/Message.tsx +53 -33
- package/src/components/Message/MessageWaiting.tsx +68 -0
- package/src/components/Message/constants.ts +4 -0
- package/src/components/Message/styles.ts +25 -2
- package/src/components/Message/types.ts +6 -0
- package/src/components/MessageList/MessageList.tsx +107 -35
- package/src/components/MessageList/MessageListEndButton.tsx +61 -0
- package/src/components/MessageList/MessageListRow.tsx +12 -12
- package/src/components/MessageList/index.ts +1 -0
- package/src/components/MessageList/styles.ts +6 -0
- package/src/components/MessageList/types.ts +52 -2
- package/src/components/MessageList/useMessageListScroll.ts +296 -21
- package/src/components/Process/Process.tsx +38 -5
- package/src/components/Process/ProcessCompactStep.tsx +32 -16
- package/src/components/Process/ProcessTimelineStep.tsx +109 -0
- package/src/components/Process/styles.ts +48 -3
- package/src/components/Process/types.ts +7 -2
- package/src/index.tsx +1 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Text, View } from 'react-native';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
Icon,
|
|
5
|
+
Spinner,
|
|
6
|
+
useColors,
|
|
7
|
+
useThemedStyles,
|
|
8
|
+
} from '@unif/react-native-design';
|
|
3
9
|
import {
|
|
4
10
|
CONFIRMATION_STATUS_TEXT,
|
|
5
11
|
DEFAULT_CANCEL_LABEL,
|
|
@@ -24,35 +30,65 @@ export function Confirmation({
|
|
|
24
30
|
testID,
|
|
25
31
|
}: ConfirmationProps): React.JSX.Element {
|
|
26
32
|
const styles = useThemedStyles(createStyles);
|
|
33
|
+
const colors = useColors();
|
|
34
|
+
const tint =
|
|
35
|
+
status === 'confirmed'
|
|
36
|
+
? colors.success
|
|
37
|
+
: status === 'cancelled'
|
|
38
|
+
? colors.foregroundSubtle
|
|
39
|
+
: colors.primary;
|
|
40
|
+
const borderColor =
|
|
41
|
+
status === 'cancelled' ? colors.surfaceContainerHighest : tint;
|
|
27
42
|
const resolvedStatusText =
|
|
28
43
|
status === 'pending'
|
|
29
44
|
? statusText
|
|
30
45
|
: (statusText ?? CONFIRMATION_STATUS_TEXT[status]);
|
|
31
46
|
|
|
32
47
|
return (
|
|
33
|
-
<View style={[styles.root, style]} testID={testID}>
|
|
34
|
-
<
|
|
35
|
-
|
|
48
|
+
<View style={[styles.root, { borderColor }, style]} testID={testID}>
|
|
49
|
+
<View style={styles.heading}>
|
|
50
|
+
{status === 'processing' ? (
|
|
51
|
+
<Spinner size={14} color={tint} />
|
|
52
|
+
) : (
|
|
53
|
+
<Icon
|
|
54
|
+
name={
|
|
55
|
+
status === 'confirmed'
|
|
56
|
+
? 'check'
|
|
57
|
+
: status === 'cancelled'
|
|
58
|
+
? 'close'
|
|
59
|
+
: 'warning'
|
|
60
|
+
}
|
|
61
|
+
size={14}
|
|
62
|
+
color={tint}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
<Text
|
|
66
|
+
style={[styles.title, status === 'cancelled' && styles.cancelled]}
|
|
67
|
+
>
|
|
68
|
+
{title}
|
|
69
|
+
</Text>
|
|
70
|
+
</View>
|
|
71
|
+
<View style={styles.body}>{children}</View>
|
|
36
72
|
{resolvedStatusText ? (
|
|
37
73
|
<Text style={styles.status}>{resolvedStatusText}</Text>
|
|
38
74
|
) : null}
|
|
39
75
|
{status === 'pending' && (onConfirm || onCancel) ? (
|
|
40
76
|
<View style={styles.actions}>
|
|
41
|
-
{onConfirm ? (
|
|
42
|
-
<Button
|
|
43
|
-
label={confirmLabel}
|
|
44
|
-
onPress={onConfirm}
|
|
45
|
-
disabled={confirmDisabled}
|
|
46
|
-
size="sm"
|
|
47
|
-
/>
|
|
48
|
-
) : null}
|
|
49
77
|
{onCancel ? (
|
|
50
78
|
<Button
|
|
51
79
|
label={cancelLabel}
|
|
52
80
|
onPress={onCancel}
|
|
53
81
|
disabled={cancelDisabled}
|
|
54
|
-
|
|
55
|
-
variant="
|
|
82
|
+
block
|
|
83
|
+
variant="secondary"
|
|
84
|
+
/>
|
|
85
|
+
) : null}
|
|
86
|
+
{onConfirm ? (
|
|
87
|
+
<Button
|
|
88
|
+
label={confirmLabel}
|
|
89
|
+
onPress={onConfirm}
|
|
90
|
+
disabled={confirmDisabled}
|
|
91
|
+
block
|
|
56
92
|
/>
|
|
57
93
|
) : null}
|
|
58
94
|
</View>
|
|
@@ -10,25 +10,32 @@ import {
|
|
|
10
10
|
export const createStyles = (colors: ColorTokens) =>
|
|
11
11
|
StyleSheet.create({
|
|
12
12
|
root: {
|
|
13
|
-
gap: space[4],
|
|
14
13
|
padding: space[5],
|
|
15
|
-
borderWidth:
|
|
14
|
+
borderWidth: 2,
|
|
16
15
|
borderColor: colors.outline,
|
|
17
|
-
borderRadius: radius.
|
|
16
|
+
borderRadius: radius.lg,
|
|
18
17
|
backgroundColor: colors.surface,
|
|
19
18
|
},
|
|
19
|
+
heading: {
|
|
20
|
+
flexDirection: 'row',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
gap: space[2],
|
|
23
|
+
marginBottom: space[3],
|
|
24
|
+
},
|
|
25
|
+
body: { marginBottom: space[4] },
|
|
26
|
+
cancelled: { color: colors.foregroundSubtle },
|
|
20
27
|
title: {
|
|
28
|
+
flex: 1,
|
|
21
29
|
color: colors.foreground,
|
|
22
|
-
fontSize: type.
|
|
30
|
+
fontSize: type.xs,
|
|
23
31
|
fontWeight: fw.semi,
|
|
24
32
|
},
|
|
25
33
|
status: {
|
|
26
34
|
color: colors.foregroundMuted,
|
|
27
|
-
fontSize: type.
|
|
35
|
+
fontSize: type.xxs,
|
|
28
36
|
},
|
|
29
37
|
actions: {
|
|
30
38
|
flexDirection: 'row',
|
|
31
|
-
flexWrap: 'wrap',
|
|
32
39
|
gap: space[3],
|
|
33
40
|
},
|
|
34
41
|
footer: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
|
-
import { type,
|
|
2
|
+
import { type, fw } from '@unif/react-native-design';
|
|
3
3
|
import type { ColorTokens } from '@unif/react-native-design';
|
|
4
4
|
|
|
5
5
|
const makeMarkdownStyles = (colors: ColorTokens, outgoing: boolean) => {
|
|
@@ -11,58 +11,32 @@ const makeMarkdownStyles = (colors: ColorTokens, outgoing: boolean) => {
|
|
|
11
11
|
};
|
|
12
12
|
return StyleSheet.create({
|
|
13
13
|
text: body,
|
|
14
|
-
em: {
|
|
15
|
-
strong: {
|
|
16
|
-
strikethrough: {
|
|
14
|
+
em: { color: textColor, fontStyle: 'italic' },
|
|
15
|
+
strong: { color: textColor, fontWeight: 'bold' },
|
|
16
|
+
strikethrough: { color: textColor, textDecorationLine: 'line-through' },
|
|
17
17
|
link: {
|
|
18
|
-
...body,
|
|
19
18
|
color: outgoing ? colors.onPrimary : colors.primary,
|
|
20
19
|
textDecorationLine: 'underline',
|
|
21
20
|
},
|
|
22
|
-
li:
|
|
21
|
+
li: { color: textColor, fontSize: type.sm },
|
|
23
22
|
codespan: {
|
|
24
|
-
...body,
|
|
25
|
-
fontFamily: fontMono,
|
|
26
23
|
color: colors.foreground,
|
|
27
24
|
backgroundColor: colors.surfaceContainer,
|
|
28
25
|
},
|
|
29
|
-
h1: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
borderBottomColor: colors.outline,
|
|
40
|
-
},
|
|
41
|
-
h3: {
|
|
42
|
-
fontSize: type.h3,
|
|
43
|
-
lineHeight: type.h3 * 1.4,
|
|
44
|
-
color: textColor,
|
|
45
|
-
},
|
|
46
|
-
h4: {
|
|
47
|
-
fontSize: type.body,
|
|
48
|
-
lineHeight: type.body * 1.4,
|
|
49
|
-
color: textColor,
|
|
50
|
-
},
|
|
51
|
-
h5: {
|
|
52
|
-
fontSize: type.sm,
|
|
53
|
-
lineHeight: type.sm * 1.4,
|
|
54
|
-
color: textColor,
|
|
55
|
-
},
|
|
56
|
-
h6: {
|
|
57
|
-
fontSize: type.xs,
|
|
58
|
-
lineHeight: type.xs * 1.4,
|
|
59
|
-
color: textColor,
|
|
26
|
+
h1: { fontSize: type.h2, fontWeight: fw.bold, color: textColor },
|
|
27
|
+
h2: { fontSize: type.h3, fontWeight: fw.bold, color: textColor },
|
|
28
|
+
h3: { fontSize: type.body, fontWeight: fw.semi, color: textColor },
|
|
29
|
+
h4: { fontSize: type.sm, fontWeight: fw.semi, color: textColor },
|
|
30
|
+
h5: { fontSize: type.sm, fontWeight: fw.semi, color: textColor },
|
|
31
|
+
h6: { fontSize: type.sm, fontWeight: fw.semi, color: textColor },
|
|
32
|
+
code: {
|
|
33
|
+
backgroundColor: colors.surfaceContainer,
|
|
34
|
+
borderColor: colors.outline,
|
|
35
|
+
borderWidth: 1,
|
|
60
36
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
blockquote: { borderLeftColor: colors.outline },
|
|
65
|
-
hr: { borderBottomColor: colors.outline },
|
|
37
|
+
codeText: { color: colors.foreground },
|
|
38
|
+
blockquote: { borderColor: colors.outline, borderLeftWidth: 3 },
|
|
39
|
+
hr: { backgroundColor: colors.outline, height: 1 },
|
|
66
40
|
table: { borderColor: colors.outline },
|
|
67
41
|
});
|
|
68
42
|
};
|
|
@@ -2,13 +2,13 @@ import { Pressable, Text, View } from 'react-native';
|
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
IconButton,
|
|
5
|
-
Spinner,
|
|
6
5
|
useThemedStyles,
|
|
7
6
|
useColors,
|
|
8
7
|
} from '@unif/react-native-design';
|
|
9
8
|
import { MarkdownContent } from './MarkdownContent/MarkdownContent';
|
|
10
9
|
import { createStyles } from './styles';
|
|
11
10
|
import { isNestedAction } from './isNestedAction';
|
|
11
|
+
import { MessageWaiting } from './MessageWaiting';
|
|
12
12
|
import type { MessageProps } from './types';
|
|
13
13
|
|
|
14
14
|
export function Message(props: MessageProps) {
|
|
@@ -23,6 +23,7 @@ export function Message(props: MessageProps) {
|
|
|
23
23
|
header,
|
|
24
24
|
footer,
|
|
25
25
|
actions = [],
|
|
26
|
+
failureAction,
|
|
26
27
|
onPress,
|
|
27
28
|
onLongPress,
|
|
28
29
|
style,
|
|
@@ -39,6 +40,7 @@ export function Message(props: MessageProps) {
|
|
|
39
40
|
(footer !== undefined && footer !== null && footer !== false);
|
|
40
41
|
const waiting =
|
|
41
42
|
(status === 'pending' || status === 'streaming') && !hasContent;
|
|
43
|
+
const failedAction = status === 'failed' ? failureAction : undefined;
|
|
42
44
|
const contentStyle = [
|
|
43
45
|
styles.content,
|
|
44
46
|
fullWidth && styles.fullWidth,
|
|
@@ -48,6 +50,12 @@ export function Message(props: MessageProps) {
|
|
|
48
50
|
],
|
|
49
51
|
surface === 'outlined' && styles.outlined,
|
|
50
52
|
status === 'failed' && styles.failed,
|
|
53
|
+
waiting &&
|
|
54
|
+
surface === 'bubble' &&
|
|
55
|
+
placement === 'start' &&
|
|
56
|
+
styles.waitingBubble,
|
|
57
|
+
failedAction && styles.failureContent,
|
|
58
|
+
failedAction && fullWidth && styles.failureFullWidth,
|
|
51
59
|
];
|
|
52
60
|
const content = (
|
|
53
61
|
<>
|
|
@@ -76,16 +84,8 @@ export function Message(props: MessageProps) {
|
|
|
76
84
|
) : (
|
|
77
85
|
props.children
|
|
78
86
|
)}
|
|
79
|
-
{waiting ?
|
|
80
|
-
|
|
81
|
-
accessible
|
|
82
|
-
accessibilityLabel="正在等待回复"
|
|
83
|
-
accessibilityState={{ busy: true }}
|
|
84
|
-
>
|
|
85
|
-
<Spinner />
|
|
86
|
-
</View>
|
|
87
|
-
) : null}
|
|
88
|
-
{statusText ? (
|
|
87
|
+
{waiting ? <MessageWaiting label={statusText} /> : null}
|
|
88
|
+
{statusText && !waiting ? (
|
|
89
89
|
<Text style={[styles.status, status === 'failed' && styles.errorText]}>
|
|
90
90
|
{statusText}
|
|
91
91
|
</Text>
|
|
@@ -93,6 +93,31 @@ export function Message(props: MessageProps) {
|
|
|
93
93
|
{footer}
|
|
94
94
|
</>
|
|
95
95
|
);
|
|
96
|
+
const bubble =
|
|
97
|
+
onPress || onLongPress ? (
|
|
98
|
+
<Pressable
|
|
99
|
+
style={contentStyle}
|
|
100
|
+
onPress={
|
|
101
|
+
onPress
|
|
102
|
+
? (event) => {
|
|
103
|
+
if (!isNestedAction(event)) onPress();
|
|
104
|
+
}
|
|
105
|
+
: undefined
|
|
106
|
+
}
|
|
107
|
+
onLongPress={
|
|
108
|
+
onLongPress
|
|
109
|
+
? (event) => {
|
|
110
|
+
if (!isNestedAction(event)) onLongPress();
|
|
111
|
+
}
|
|
112
|
+
: undefined
|
|
113
|
+
}
|
|
114
|
+
accessible={false}
|
|
115
|
+
>
|
|
116
|
+
{content}
|
|
117
|
+
</Pressable>
|
|
118
|
+
) : (
|
|
119
|
+
<View style={contentStyle}>{content}</View>
|
|
120
|
+
);
|
|
96
121
|
return (
|
|
97
122
|
<View
|
|
98
123
|
style={[styles.root, placement === 'end' && styles.end, style]}
|
|
@@ -107,29 +132,24 @@ export function Message(props: MessageProps) {
|
|
|
107
132
|
]}
|
|
108
133
|
>
|
|
109
134
|
{name ? <Text style={styles.name}>{name}</Text> : null}
|
|
110
|
-
{
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
accessible={false}
|
|
128
|
-
>
|
|
129
|
-
{content}
|
|
130
|
-
</Pressable>
|
|
135
|
+
{failedAction ? (
|
|
136
|
+
<View style={[styles.failureRow, placement === 'end' && styles.end]}>
|
|
137
|
+
{bubble}
|
|
138
|
+
<IconButton
|
|
139
|
+
icon="alert"
|
|
140
|
+
size="sm"
|
|
141
|
+
variant="ghost"
|
|
142
|
+
color={colors.error}
|
|
143
|
+
style={styles.failureAction}
|
|
144
|
+
accessibilityLabel={failedAction.label}
|
|
145
|
+
accessibilityHint={failedAction.accessibilityHint}
|
|
146
|
+
disabled={failedAction.disabled}
|
|
147
|
+
loading={failedAction.loading}
|
|
148
|
+
onPress={failedAction.onPress}
|
|
149
|
+
/>
|
|
150
|
+
</View>
|
|
131
151
|
) : (
|
|
132
|
-
|
|
152
|
+
bubble
|
|
133
153
|
)}
|
|
134
154
|
{actions.length ? (
|
|
135
155
|
<View style={styles.actions}>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { Animated, Easing, Platform, Text, View } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
usePrefersReducedMotion,
|
|
5
|
+
useThemedStyles,
|
|
6
|
+
} from '@unif/react-native-design';
|
|
7
|
+
import {
|
|
8
|
+
MESSAGE_WAITING_DOT_DELAYS,
|
|
9
|
+
MESSAGE_WAITING_DOT_DURATION,
|
|
10
|
+
MESSAGE_WAITING_DOT_MIN_OPACITY,
|
|
11
|
+
} from './constants';
|
|
12
|
+
import { createStyles } from './styles';
|
|
13
|
+
import type { MessageWaitingProps } from './types';
|
|
14
|
+
|
|
15
|
+
export function MessageWaiting({ label }: MessageWaitingProps) {
|
|
16
|
+
const styles = useThemedStyles(createStyles);
|
|
17
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
18
|
+
const [values] = useState(() =>
|
|
19
|
+
MESSAGE_WAITING_DOT_DELAYS.map(() => new Animated.Value(1))
|
|
20
|
+
);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const animations = MESSAGE_WAITING_DOT_DELAYS.map((delay, index) => {
|
|
23
|
+
const value = values[index]!;
|
|
24
|
+
value.setValue(reducedMotion ? 1 : MESSAGE_WAITING_DOT_MIN_OPACITY);
|
|
25
|
+
return Animated.sequence([
|
|
26
|
+
Animated.delay(delay),
|
|
27
|
+
Animated.loop(
|
|
28
|
+
Animated.sequence(
|
|
29
|
+
[1, MESSAGE_WAITING_DOT_MIN_OPACITY].map((toValue) =>
|
|
30
|
+
Animated.timing(value, {
|
|
31
|
+
toValue,
|
|
32
|
+
duration: MESSAGE_WAITING_DOT_DURATION,
|
|
33
|
+
easing: Easing.inOut(Easing.ease),
|
|
34
|
+
useNativeDriver: Platform.OS !== 'web',
|
|
35
|
+
})
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
),
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
if (!reducedMotion) animations.forEach((animation) => animation.start());
|
|
42
|
+
return () => animations.forEach((animation) => animation.stop());
|
|
43
|
+
}, [reducedMotion, values]);
|
|
44
|
+
return (
|
|
45
|
+
<View
|
|
46
|
+
style={styles.waitingRow}
|
|
47
|
+
accessible
|
|
48
|
+
accessibilityLabel={label || '正在等待回复'}
|
|
49
|
+
accessibilityState={{ busy: true }}
|
|
50
|
+
>
|
|
51
|
+
<View
|
|
52
|
+
style={styles.waitingDots}
|
|
53
|
+
pointerEvents="none"
|
|
54
|
+
accessibilityElementsHidden
|
|
55
|
+
importantForAccessibility="no-hide-descendants"
|
|
56
|
+
>
|
|
57
|
+
{values.map((opacity, index) => (
|
|
58
|
+
<Animated.View
|
|
59
|
+
key={index}
|
|
60
|
+
testID={`message-waiting-dot-${index}`}
|
|
61
|
+
style={[styles.waitingDot, { opacity }]}
|
|
62
|
+
/>
|
|
63
|
+
))}
|
|
64
|
+
</View>
|
|
65
|
+
{label ? <Text style={styles.waitingLabel}>{label}</Text> : null}
|
|
66
|
+
</View>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export const INTERACTIVE_CONTENT_SELECTOR =
|
|
2
2
|
'button,a,input,textarea,select,[role="button"],[role="link"],[role="checkbox"],[role="switch"],[role="menuitem"],[role="tab"]';
|
|
3
|
+
|
|
4
|
+
export const MESSAGE_WAITING_DOT_DELAYS = [0, 150, 300] as const;
|
|
5
|
+
export const MESSAGE_WAITING_DOT_DURATION = 600;
|
|
6
|
+
export const MESSAGE_WAITING_DOT_MIN_OPACITY = 0.3;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
|
-
import { radius, space, type } from '@unif/react-native-design';
|
|
2
|
+
import { r, radius, space, type } from '@unif/react-native-design';
|
|
3
3
|
import type { ColorTokens } from '@unif/react-native-design';
|
|
4
4
|
|
|
5
5
|
export const createStyles = (colors: ColorTokens) =>
|
|
@@ -35,7 +35,30 @@ export const createStyles = (colors: ColorTokens) =>
|
|
|
35
35
|
borderWidth: 1,
|
|
36
36
|
borderColor: colors.outline,
|
|
37
37
|
},
|
|
38
|
-
failed: {
|
|
38
|
+
failed: { opacity: 0.7 },
|
|
39
|
+
failureRow: {
|
|
40
|
+
flexDirection: 'row',
|
|
41
|
+
alignItems: 'center',
|
|
42
|
+
alignSelf: 'stretch',
|
|
43
|
+
gap: space[2],
|
|
44
|
+
},
|
|
45
|
+
failureContent: { flexShrink: 1 },
|
|
46
|
+
failureAction: { alignSelf: 'center' },
|
|
47
|
+
failureFullWidth: { flex: 1 },
|
|
48
|
+
waitingBubble: { width: r(240), borderTopLeftRadius: 0 },
|
|
49
|
+
waitingRow: { flexDirection: 'row', alignItems: 'center', gap: space[2] },
|
|
50
|
+
waitingDots: { flexDirection: 'row', gap: r(3) },
|
|
51
|
+
waitingDot: {
|
|
52
|
+
width: r(6),
|
|
53
|
+
height: r(6),
|
|
54
|
+
borderRadius: r(3),
|
|
55
|
+
backgroundColor: colors.foregroundSubtle,
|
|
56
|
+
},
|
|
57
|
+
waitingLabel: {
|
|
58
|
+
fontSize: type.xxs,
|
|
59
|
+
color: colors.foregroundSubtle,
|
|
60
|
+
flexShrink: 1,
|
|
61
|
+
},
|
|
39
62
|
text: {
|
|
40
63
|
fontSize: type.sm,
|
|
41
64
|
lineHeight: type.sm * 1.5,
|
|
@@ -15,6 +15,8 @@ export interface MessageBaseProps {
|
|
|
15
15
|
header?: ReactNode;
|
|
16
16
|
footer?: ReactNode;
|
|
17
17
|
actions?: readonly ChatAction[];
|
|
18
|
+
/** 仅 failed 时在气泡旁显示的错误操作;不自动生成重试。 */
|
|
19
|
+
failureAction?: ChatAction;
|
|
18
20
|
onPress?(): void;
|
|
19
21
|
onLongPress?(): void;
|
|
20
22
|
style?: StyleProp<ViewStyle>;
|
|
@@ -43,3 +45,7 @@ export interface MessageCustomContent {
|
|
|
43
45
|
}
|
|
44
46
|
export type MessageProps = MessageBaseProps &
|
|
45
47
|
(MessagePlainContent | MessageMarkdownContent | MessageCustomContent);
|
|
48
|
+
|
|
49
|
+
export interface MessageWaitingProps {
|
|
50
|
+
label?: string;
|
|
51
|
+
}
|