rn-vs-lb 1.0.69 → 1.0.71
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.
|
@@ -28,6 +28,8 @@ const meta: Meta<ChatItemProps> = {
|
|
|
28
28
|
export default meta;
|
|
29
29
|
|
|
30
30
|
const Template: StoryFn<ChatItemProps> = (args) => <ChatItem {...args} />;
|
|
31
|
+
const getItemLabel = (item: ChatItemProps, idx: number) =>
|
|
32
|
+
item.variant === 'person' ? item.chatName ?? item.senderFullName : item.chatName ?? `chat-${idx}`;
|
|
31
33
|
|
|
32
34
|
export const PersonOnline = Template.bind({});
|
|
33
35
|
PersonOnline.args = {
|
|
@@ -38,7 +40,7 @@ PersonOnline.args = {
|
|
|
38
40
|
isUserOnline: true,
|
|
39
41
|
lastMessage: 'See you at 6pm near the station',
|
|
40
42
|
createdAt: '10:42',
|
|
41
|
-
unread: '
|
|
43
|
+
unread: 'new',
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
export const PersonOffline = Template.bind({});
|
|
@@ -61,7 +63,7 @@ GroupWithSender.args = {
|
|
|
61
63
|
'https://images.unsplash.com/photo-1519340241574-2cec6aef0c01?auto=format&fit=crop&w=200&q=60',
|
|
62
64
|
lastMessage: 'Slides are uploaded to Drive, check the link above.',
|
|
63
65
|
createdAt: 'Yesterday',
|
|
64
|
-
unread: '
|
|
66
|
+
unread: 'new',
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
export const Bot = Template.bind({});
|
|
@@ -94,7 +96,7 @@ export const ListOfItems: StoryFn = () => {
|
|
|
94
96
|
isUserOnline: true,
|
|
95
97
|
lastMessage: 'See you soon!',
|
|
96
98
|
createdAt: '10:42',
|
|
97
|
-
unread: '
|
|
99
|
+
unread: 'new',
|
|
98
100
|
},
|
|
99
101
|
{
|
|
100
102
|
variant: 'person',
|
|
@@ -113,7 +115,7 @@ export const ListOfItems: StoryFn = () => {
|
|
|
113
115
|
'https://images.unsplash.com/photo-1519340241574-2cec6aef0c01?auto=format&fit=crop&w=200&q=60',
|
|
114
116
|
lastMessage: 'Standup in 5 minutes.',
|
|
115
117
|
createdAt: '08:55',
|
|
116
|
-
unread: '
|
|
118
|
+
unread: 'new',
|
|
117
119
|
},
|
|
118
120
|
{
|
|
119
121
|
variant: 'group',
|
|
@@ -166,7 +168,7 @@ export const ListOfItems: StoryFn = () => {
|
|
|
166
168
|
<ChatItem
|
|
167
169
|
key={idx}
|
|
168
170
|
{...item}
|
|
169
|
-
onPress={createPressHandler(item
|
|
171
|
+
onPress={createPressHandler(getItemLabel(item, idx))}
|
|
170
172
|
/>
|
|
171
173
|
))}
|
|
172
174
|
</ScrollView>
|
|
@@ -3,65 +3,98 @@ import { ThemeType } from '../../theme';
|
|
|
3
3
|
|
|
4
4
|
export const getStyles = (theme: ThemeType) => StyleSheet.create({
|
|
5
5
|
userContainer: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
paddingVertical: 8,
|
|
7
|
+
paddingHorizontal: 12,
|
|
8
|
+
borderRadius: 16,
|
|
9
|
+
backgroundColor: theme.white,
|
|
10
10
|
},
|
|
11
11
|
profileSection: {
|
|
12
12
|
flexDirection: 'row',
|
|
13
|
-
alignItems: '
|
|
13
|
+
alignItems: 'flex-start',
|
|
14
14
|
flex: 1,
|
|
15
15
|
},
|
|
16
16
|
avatar: {
|
|
17
|
-
width:
|
|
18
|
-
height:
|
|
19
|
-
borderRadius:
|
|
17
|
+
width: 56,
|
|
18
|
+
height: 56,
|
|
19
|
+
borderRadius: 28,
|
|
20
20
|
},
|
|
21
21
|
userInfo: {
|
|
22
22
|
flex: 1,
|
|
23
23
|
marginLeft: 12,
|
|
24
|
+
minHeight: 56,
|
|
25
|
+
justifyContent: 'center',
|
|
26
|
+
},
|
|
27
|
+
headerRow: {
|
|
28
|
+
flexDirection: 'row',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
justifyContent: 'space-between',
|
|
31
|
+
gap: 8,
|
|
24
32
|
},
|
|
25
|
-
|
|
33
|
+
headerRight: {
|
|
26
34
|
flexDirection: 'row',
|
|
27
|
-
alignItems:
|
|
28
|
-
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
gap: 8,
|
|
37
|
+
},
|
|
38
|
+
chatTitle: {
|
|
39
|
+
flex: 1,
|
|
40
|
+
},
|
|
41
|
+
unreadBadge: {
|
|
42
|
+
minHeight: 20,
|
|
43
|
+
paddingHorizontal: 8,
|
|
44
|
+
borderRadius: 999,
|
|
36
45
|
backgroundColor: theme.success,
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
gap: 5,
|
|
49
|
+
},
|
|
50
|
+
unreadDot: {
|
|
51
|
+
width: 6,
|
|
52
|
+
height: 6,
|
|
53
|
+
borderRadius: 3,
|
|
54
|
+
backgroundColor: theme.white,
|
|
55
|
+
},
|
|
56
|
+
unreadText: {
|
|
37
57
|
color: theme.white,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
58
|
+
fontSize: 10,
|
|
59
|
+
lineHeight: 12,
|
|
60
|
+
fontWeight: '700',
|
|
61
|
+
letterSpacing: 0.3,
|
|
62
|
+
textTransform: 'uppercase',
|
|
63
|
+
},
|
|
64
|
+
messageRow: {
|
|
65
|
+
marginTop: 4,
|
|
41
66
|
},
|
|
42
67
|
senderName: {
|
|
43
68
|
fontSize: 14,
|
|
44
|
-
|
|
69
|
+
color: theme.placeholder,
|
|
70
|
+
},
|
|
71
|
+
senderNameUnread: {
|
|
45
72
|
color: theme.text,
|
|
73
|
+
fontWeight: '500',
|
|
46
74
|
},
|
|
47
75
|
senderMessage: {
|
|
48
76
|
fontSize: 13,
|
|
49
77
|
fontStyle: 'italic',
|
|
78
|
+
color: theme.placeholder,
|
|
50
79
|
},
|
|
51
80
|
timeAgo: {
|
|
52
|
-
fontSize:
|
|
81
|
+
fontSize: 12,
|
|
53
82
|
color: theme.placeholder,
|
|
83
|
+
fontWeight: '500',
|
|
84
|
+
},
|
|
85
|
+
timeAgoUnread: {
|
|
86
|
+
color: theme.text,
|
|
54
87
|
},
|
|
55
88
|
|
|
56
89
|
status: {
|
|
57
|
-
position:
|
|
58
|
-
width:12,
|
|
90
|
+
position: 'absolute',
|
|
91
|
+
width: 12,
|
|
59
92
|
height: 12,
|
|
60
93
|
borderWidth: 2,
|
|
61
94
|
borderColor: theme.white,
|
|
62
95
|
borderRadius: 10,
|
|
63
96
|
bottom: 0,
|
|
64
|
-
right:1,
|
|
97
|
+
right: 1,
|
|
65
98
|
},
|
|
66
99
|
online: {
|
|
67
100
|
backgroundColor: theme.success,
|
|
@@ -6,7 +6,7 @@ import { useTheme } from '../../theme';
|
|
|
6
6
|
import { getStyles } from './ChatItem.styles';
|
|
7
7
|
|
|
8
8
|
type BaseProps = {
|
|
9
|
-
unread?: string;
|
|
9
|
+
unread?: string | boolean;
|
|
10
10
|
onPress?: () => void;
|
|
11
11
|
createdAt?: string;
|
|
12
12
|
lastMessage?: string;
|
|
@@ -40,26 +40,42 @@ export type ChatItemProps = PersonProps | GroupProps | BotProps;
|
|
|
40
40
|
export const ChatItem: FC<ChatItemProps> = (props) => {
|
|
41
41
|
const { theme, typography } = useTheme();
|
|
42
42
|
const styles = getStyles(theme);
|
|
43
|
+
const hasUnread = Boolean(props.unread);
|
|
44
|
+
const unreadLabel =
|
|
45
|
+
typeof props.unread === 'string' && props.unread.trim().length > 0 && props.unread.trim() !== '+'
|
|
46
|
+
? props.unread.trim().toUpperCase()
|
|
47
|
+
: 'NEW';
|
|
43
48
|
|
|
44
49
|
const handlePress = () => props.onPress?.();
|
|
45
50
|
|
|
46
|
-
// Общий заголовок (верхняя строка) + бейдж непрочитанного
|
|
51
|
+
// Общий заголовок (верхняя строка) + время + бейдж непрочитанного
|
|
47
52
|
const renderHeader = (title: string) => (
|
|
48
|
-
<View style={styles.
|
|
49
|
-
<Text numberOfLines={1} ellipsizeMode="tail" style={typography.titleH6}>
|
|
53
|
+
<View style={styles.headerRow}>
|
|
54
|
+
<Text numberOfLines={1} ellipsizeMode="tail" style={[typography.titleH6, styles.chatTitle]}>
|
|
50
55
|
{title}
|
|
51
56
|
</Text>
|
|
52
|
-
|
|
57
|
+
<View style={styles.headerRight}>
|
|
58
|
+
{props.createdAt ? (
|
|
59
|
+
<Text style={[styles.timeAgo, hasUnread && styles.timeAgoUnread]}>{props.createdAt}</Text>
|
|
60
|
+
) : null}
|
|
61
|
+
{hasUnread ? (
|
|
62
|
+
<View style={styles.unreadBadge}>
|
|
63
|
+
<View style={styles.unreadDot} />
|
|
64
|
+
<Text style={styles.unreadText}>{unreadLabel}</Text>
|
|
65
|
+
</View>
|
|
66
|
+
) : null}
|
|
67
|
+
</View>
|
|
53
68
|
</View>
|
|
54
69
|
);
|
|
55
70
|
|
|
56
71
|
// Превью последнего сообщения (нижний блок)
|
|
57
72
|
const renderLastMessage = () => {
|
|
58
73
|
if (!props.lastMessage) return null;
|
|
74
|
+
const previewStyle = [styles.senderName, hasUnread && styles.senderNameUnread];
|
|
59
75
|
|
|
60
76
|
if (props.variant === 'person') {
|
|
61
77
|
return (
|
|
62
|
-
<Text numberOfLines={2} ellipsizeMode="tail" style={
|
|
78
|
+
<Text numberOfLines={2} ellipsizeMode="tail" style={previewStyle}>
|
|
63
79
|
{props.senderFullName}: <Text style={styles.senderMessage}>{props.lastMessage}</Text>
|
|
64
80
|
</Text>
|
|
65
81
|
);
|
|
@@ -67,7 +83,7 @@ export const ChatItem: FC<ChatItemProps> = (props) => {
|
|
|
67
83
|
|
|
68
84
|
if (props.variant === 'group' && props.senderFullName) {
|
|
69
85
|
return (
|
|
70
|
-
<Text numberOfLines={2} ellipsizeMode="tail" style={
|
|
86
|
+
<Text numberOfLines={2} ellipsizeMode="tail" style={previewStyle}>
|
|
71
87
|
{props.senderFullName}: <Text style={styles.senderMessage}>{props.lastMessage}</Text>
|
|
72
88
|
</Text>
|
|
73
89
|
);
|
|
@@ -75,7 +91,7 @@ export const ChatItem: FC<ChatItemProps> = (props) => {
|
|
|
75
91
|
|
|
76
92
|
// bot или group без senderFullName
|
|
77
93
|
return (
|
|
78
|
-
<Text numberOfLines={2} ellipsizeMode="tail" style={
|
|
94
|
+
<Text numberOfLines={2} ellipsizeMode="tail" style={previewStyle}>
|
|
79
95
|
{props.lastMessage}
|
|
80
96
|
</Text>
|
|
81
97
|
);
|
|
@@ -118,12 +134,7 @@ export const ChatItem: FC<ChatItemProps> = (props) => {
|
|
|
118
134
|
{renderAvatar()}
|
|
119
135
|
<View style={styles.userInfo}>
|
|
120
136
|
{renderHeader(titleText)}
|
|
121
|
-
|
|
122
|
-
<View style={styles.senderContainer}>{renderLastMessage()}</View>
|
|
123
|
-
|
|
124
|
-
{props.lastMessage ? (
|
|
125
|
-
<Text style={styles.timeAgo}>{props.createdAt}</Text>
|
|
126
|
-
) : null}
|
|
137
|
+
<View style={styles.messageRow}>{renderLastMessage()}</View>
|
|
127
138
|
</View>
|
|
128
139
|
</TouchableOpacity>
|
|
129
140
|
</View>
|
|
@@ -13,6 +13,8 @@ const meta: Meta = {
|
|
|
13
13
|
onBackPress: { action: 'back' },
|
|
14
14
|
children: { control: false },
|
|
15
15
|
infoTooltip: { control: false },
|
|
16
|
+
customBackButton: { control: false },
|
|
17
|
+
background: { control: 'color' },
|
|
16
18
|
},
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -77,3 +79,23 @@ WithInfoTooltip.render = (args) => (
|
|
|
77
79
|
/>
|
|
78
80
|
</View>
|
|
79
81
|
);
|
|
82
|
+
|
|
83
|
+
export const WithCustomBackButton = Template.bind({});
|
|
84
|
+
WithCustomBackButton.args = {
|
|
85
|
+
background: '#EAF5FF',
|
|
86
|
+
};
|
|
87
|
+
WithCustomBackButton.render = (args) => (
|
|
88
|
+
<View style={{ padding: 16, backgroundColor: '#f8f8f8' }}>
|
|
89
|
+
<HeaderDefault
|
|
90
|
+
{...args}
|
|
91
|
+
customBackButton={(
|
|
92
|
+
<TouchableOpacity
|
|
93
|
+
onPress={args.onBackPress}
|
|
94
|
+
style={{ backgroundColor: '#D7E9FF', borderRadius: 12, paddingHorizontal: 10, paddingVertical: 6 }}
|
|
95
|
+
>
|
|
96
|
+
<Text style={{ fontSize: 12, fontWeight: '600' }}>{'< Back'}</Text>
|
|
97
|
+
</TouchableOpacity>
|
|
98
|
+
)}
|
|
99
|
+
/>
|
|
100
|
+
</View>
|
|
101
|
+
);
|
|
@@ -12,21 +12,35 @@ export enum AccessType {
|
|
|
12
12
|
|
|
13
13
|
interface HeaderProps {
|
|
14
14
|
title: string;
|
|
15
|
-
onBackPress
|
|
15
|
+
onBackPress?: () => void;
|
|
16
16
|
acceessType?: AccessType;
|
|
17
17
|
children?: React.ReactNode;
|
|
18
18
|
infoTooltip?: React.ReactNode;
|
|
19
|
+
background?: string;
|
|
20
|
+
customBackButton?: React.ReactNode;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
const HeaderDefault: React.FC<HeaderProps> = ({
|
|
23
|
+
const HeaderDefault: React.FC<HeaderProps> = ({
|
|
24
|
+
title,
|
|
25
|
+
onBackPress,
|
|
26
|
+
children,
|
|
27
|
+
acceessType,
|
|
28
|
+
infoTooltip,
|
|
29
|
+
background,
|
|
30
|
+
customBackButton,
|
|
31
|
+
}) => {
|
|
22
32
|
const { globalStyleSheet, theme, sizes, commonStyles, typography } = useTheme();
|
|
23
33
|
const styles = getStyles({ globalStyleSheet, theme, sizes, commonStyles });
|
|
24
34
|
|
|
25
35
|
return (
|
|
26
|
-
<View style={styles.container}>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
<View style={[styles.container, background ? { backgroundColor: background } : undefined]}>
|
|
37
|
+
{customBackButton ? (
|
|
38
|
+
customBackButton
|
|
39
|
+
) : (
|
|
40
|
+
<TouchableOpacity onPress={onBackPress}>
|
|
41
|
+
<Ionicons name="arrow-back" size={25} color={theme.text} />
|
|
42
|
+
</TouchableOpacity>
|
|
43
|
+
)}
|
|
30
44
|
<View style={[styles.titleContainer, !children && { paddingRight: sizes.xl }]}>
|
|
31
45
|
<Text style={typography.titleH5}>{title}</Text>
|
|
32
46
|
{acceessType && acceessType !== AccessType.COMMON && <Text style={styles.access}>{'\u2014'} {acceessType}</Text>}
|