rn-vs-lb 1.0.68 → 1.0.69
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/components/Chat/InputMessage.tsx +2 -2
- package/components/Profile/ProfileCard/ProfileCard.stories.tsx +98 -20
- package/components/Profile/ProfileCard/ProfileCard.tsx +49 -115
- package/components/Profile/ProfileCard/useProfileCardStyles.ts +12 -1
- package/components/Profile/index.ts +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { View, StyleSheet, TextInput, TouchableOpacity, Text, Image, ScrollView, ActivityIndicator } from 'react-native';
|
|
2
|
+
import { View, StyleSheet, TextInput, TouchableOpacity, Text, Image, ScrollView, ActivityIndicator, Platform } from 'react-native';
|
|
3
3
|
import { Ionicons } from '@expo/vector-icons';
|
|
4
4
|
import { ThemeType, useTheme } from '../../theme';
|
|
5
5
|
|
|
@@ -265,7 +265,7 @@ export const getStyles = (theme: ThemeType) =>
|
|
|
265
265
|
paddingHorizontal: 10,
|
|
266
266
|
paddingVertical: 6,
|
|
267
267
|
borderRadius: 4,
|
|
268
|
-
top:4,
|
|
268
|
+
top: Platform.OS === 'ios' ? 4 : undefined,
|
|
269
269
|
borderColor: theme.white,
|
|
270
270
|
},
|
|
271
271
|
sendButton: {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { View } from 'react-native';
|
|
5
|
+
import { FontAwesome, FontAwesome5, FontAwesome6, Ionicons, MaterialIcons, Octicons } from '@expo/vector-icons';
|
|
5
6
|
import PureProfileCard from "./ProfileCard";
|
|
6
7
|
|
|
7
8
|
const meta = {
|
|
@@ -9,17 +10,7 @@ const meta = {
|
|
|
9
10
|
component: PureProfileCard,
|
|
10
11
|
argTypes: {
|
|
11
12
|
onBack: { action: 'onBack' },
|
|
12
|
-
onOpenSettings: { action: 'onOpenSettings' },
|
|
13
|
-
onOpenActivity: { action: 'onOpenActivity' },
|
|
14
|
-
onOpenSpecialist: { action: 'onOpenSpecialist' },
|
|
15
|
-
onOpenCreatePoll: { action: 'onOpenCreatePoll' },
|
|
16
|
-
onOpenCreateEvent: { action: 'onOpenCreateEvent' },
|
|
17
|
-
onOpenAiBots: { action: 'onOpenAiBots' },
|
|
18
|
-
onOpenBots: { action: 'onOpenBots' },
|
|
19
|
-
onOpenUserSheet: { action: 'onOpenUserSheet' },
|
|
20
13
|
onLearnMorePress: { action: 'onLearnMorePress' },
|
|
21
|
-
onMessage: { action: 'onMessage' },
|
|
22
|
-
onFollowToggle: { action: 'onFollowToggle' },
|
|
23
14
|
},
|
|
24
15
|
decorators: [
|
|
25
16
|
// если у тебя есть ThemeProvider — оберни им здесь
|
|
@@ -32,10 +23,18 @@ const meta = {
|
|
|
32
23
|
isMe: false,
|
|
33
24
|
isOnline: false,
|
|
34
25
|
lastSeenText: 'Last seen: 2 hours ago',
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
headerActions: [
|
|
27
|
+
{
|
|
28
|
+
id: 'specialist',
|
|
29
|
+
icon: <FontAwesome5 size={20} name="id-card" color="#111" />,
|
|
30
|
+
onPress: () => {},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'menu',
|
|
34
|
+
icon: <Ionicons name="ellipsis-horizontal-sharp" size={20} color="#2f7fff" />,
|
|
35
|
+
onPress: () => {},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
39
38
|
},
|
|
40
39
|
} satisfies Meta<typeof PureProfileCard>;
|
|
41
40
|
|
|
@@ -45,16 +44,95 @@ type S = StoryObj<typeof PureProfileCard>;
|
|
|
45
44
|
export const Default: S = {};
|
|
46
45
|
export const MeOwner: S = { args: { isMe: true, isOnline: undefined, lastSeenText: undefined } };
|
|
47
46
|
export const OnlineUser: S = { args: { isMe: false, isOnline: true, lastSeenText: undefined } };
|
|
48
|
-
export const
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
export const TwoActions: S = {
|
|
48
|
+
args: {
|
|
49
|
+
ctaActions: [
|
|
50
|
+
{
|
|
51
|
+
label: 'Message',
|
|
52
|
+
variant: 'primary',
|
|
53
|
+
icon: <FontAwesome name="send" size={14} color="#fff" />,
|
|
54
|
+
onPress: () => {},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: 'Follow',
|
|
58
|
+
variant: 'outline',
|
|
59
|
+
icon: <FontAwesome5 name="user-plus" size={14} color="#2f7fff" />,
|
|
60
|
+
onPress: () => {},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const OneActionFullWidth: S = {
|
|
67
|
+
args: {
|
|
68
|
+
ctaActions: [
|
|
69
|
+
{
|
|
70
|
+
label: 'Create Poll',
|
|
71
|
+
icon: <Ionicons name="stats-chart-outline" size={20} color="#2f7fff" />,
|
|
72
|
+
onPress: () => {},
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const OwnerTwoActions: S = {
|
|
51
79
|
args: {
|
|
52
80
|
isMe: true,
|
|
53
81
|
isAuth: true,
|
|
54
82
|
isOnline: undefined,
|
|
55
83
|
lastSeenText: undefined,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
84
|
+
headerActions: [
|
|
85
|
+
{
|
|
86
|
+
id: 'activity',
|
|
87
|
+
icon: <FontAwesome name="bell-o" size={21} color="#111" />,
|
|
88
|
+
onPress: () => {},
|
|
89
|
+
showDot: true,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'specialist',
|
|
93
|
+
icon: <FontAwesome5 size={20} name="id-card" color="#111" />,
|
|
94
|
+
onPress: () => {},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'ai-bots',
|
|
98
|
+
icon: <MaterialIcons name="smart-toy" size={25} color="#111" />,
|
|
99
|
+
onPress: () => {},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'bots',
|
|
103
|
+
icon: <FontAwesome6 style={{ top: -1 }} size={20} name="robot" color="#111" />,
|
|
104
|
+
onPress: () => {},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'settings',
|
|
108
|
+
icon: <Ionicons size={24} name="settings-outline" color="#111" />,
|
|
109
|
+
onPress: () => {},
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
ctaActions: [
|
|
113
|
+
{
|
|
114
|
+
label: 'Create Poll',
|
|
115
|
+
icon: <Ionicons name="stats-chart-outline" size={20} color="#2f7fff" />,
|
|
116
|
+
onPress: () => {},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: 'Create Event',
|
|
120
|
+
icon: <Octicons name="diff-added" size={20} color="#2f7fff" />,
|
|
121
|
+
onPress: () => {},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const SixHeaderActions: S = {
|
|
128
|
+
args: {
|
|
129
|
+
headerActions: [
|
|
130
|
+
{ id: 'a1', icon: <FontAwesome name="bell-o" size={21} color="#111" />, onPress: () => {}, showDot: true },
|
|
131
|
+
{ id: 'a2', icon: <FontAwesome5 size={20} name="id-card" color="#111" />, onPress: () => {} },
|
|
132
|
+
{ id: 'a3', icon: <MaterialIcons name="smart-toy" size={25} color="#111" />, onPress: () => {} },
|
|
133
|
+
{ id: 'a4', icon: <FontAwesome6 style={{ top: -1 }} size={20} name="robot" color="#111" />, onPress: () => {} },
|
|
134
|
+
{ id: 'a5', icon: <Ionicons size={24} name="settings-outline" color="#111" />, onPress: () => {} },
|
|
135
|
+
{ id: 'a6', icon: <Ionicons name="ellipsis-horizontal-sharp" size={20} color="#2f7fff" />, onPress: () => {} },
|
|
136
|
+
],
|
|
59
137
|
},
|
|
60
138
|
};
|
|
@@ -7,9 +7,23 @@ import {
|
|
|
7
7
|
TouchableOpacity,
|
|
8
8
|
View,
|
|
9
9
|
} from 'react-native';
|
|
10
|
-
import { Ionicons,
|
|
10
|
+
import { Ionicons, MaterialIcons } from '@expo/vector-icons';
|
|
11
11
|
import { useProfileCardStyles } from './useProfileCardStyles';
|
|
12
12
|
|
|
13
|
+
export type PureProfileCardHeaderAction = {
|
|
14
|
+
id?: string;
|
|
15
|
+
onPress: () => Promise<void> | void;
|
|
16
|
+
icon: React.ReactNode;
|
|
17
|
+
showDot?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type PureProfileCardCtaAction = {
|
|
21
|
+
label: string;
|
|
22
|
+
onPress: () => Promise<void> | void;
|
|
23
|
+
icon?: React.ReactNode;
|
|
24
|
+
variant?: 'primary' | 'outline';
|
|
25
|
+
};
|
|
26
|
+
|
|
13
27
|
export type PureProfileCardProps = {
|
|
14
28
|
name: string;
|
|
15
29
|
imageUri: string;
|
|
@@ -22,39 +36,25 @@ export type PureProfileCardProps = {
|
|
|
22
36
|
|
|
23
37
|
/** действия/навигация */
|
|
24
38
|
onBack?: () => void;
|
|
25
|
-
|
|
26
|
-
onOpenActivity?: () => void;
|
|
27
|
-
onOpenSpecialist?: () => void;
|
|
28
|
-
onOpenCreatePoll?: () => void;
|
|
29
|
-
onOpenCreateEvent?: () => void;
|
|
30
|
-
onOpenAiBots?: () => void;
|
|
31
|
-
onOpenBots?: () => void;
|
|
32
|
-
onOpenUserSheet?: () => void;
|
|
39
|
+
headerActions?: PureProfileCardHeaderAction[];
|
|
33
40
|
|
|
34
41
|
/** CTA */
|
|
35
42
|
onLearnMorePress?: () => void;
|
|
36
|
-
|
|
37
|
-
onFollowToggle?: () => void;
|
|
38
|
-
|
|
39
|
-
/** UI-флаги */
|
|
40
|
-
specialistEnabled?: boolean;
|
|
41
|
-
hideFollowBtn?: boolean;
|
|
42
|
-
isFollowing?: boolean;
|
|
43
|
-
hasNotifications?: boolean;
|
|
43
|
+
ctaActions?: PureProfileCardCtaAction[];
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
export default function PureProfileCard(props: PureProfileCardProps) {
|
|
47
47
|
const {
|
|
48
48
|
name, imageUri,
|
|
49
49
|
isAuth, isMe, isOnline, lastSeenText,
|
|
50
|
-
onBack,
|
|
51
|
-
|
|
52
|
-
onLearnMorePress, onMessage, onFollowToggle,
|
|
53
|
-
specialistEnabled, hideFollowBtn, isFollowing, hasNotifications,
|
|
50
|
+
onBack, headerActions,
|
|
51
|
+
onLearnMorePress, ctaActions,
|
|
54
52
|
} = props;
|
|
55
53
|
|
|
56
54
|
const { theme, globalStyleSheet, typography, styles } = useProfileCardStyles();
|
|
57
55
|
const [previewVisible, setPreviewVisible] = useState(false);
|
|
56
|
+
const visibleHeaderActions = (headerActions ?? []).slice(0, 6);
|
|
57
|
+
const visibleCtaActions = (ctaActions ?? []).slice(0, 2);
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
60
|
<View style={styles.cardContainer}>
|
|
@@ -64,74 +64,20 @@ export default function PureProfileCard(props: PureProfileCardProps) {
|
|
|
64
64
|
<Ionicons size={24} name="arrow-back" color={theme.text} />
|
|
65
65
|
</TouchableOpacity>
|
|
66
66
|
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
{
|
|
70
|
-
<TouchableOpacity onPress={
|
|
71
|
-
<View style={styles.iconBackground}>
|
|
72
|
-
<FontAwesome5 size={20} name="id-card" color={theme.text} />
|
|
73
|
-
</View>
|
|
74
|
-
</TouchableOpacity>
|
|
75
|
-
)}
|
|
76
|
-
{onOpenUserSheet && (
|
|
77
|
-
<TouchableOpacity onPress={() => onOpenUserSheet()}>
|
|
78
|
-
<View style={styles.iconBackground}>
|
|
79
|
-
<Ionicons name="ellipsis-horizontal-sharp" size={20} color={theme.primary} />
|
|
80
|
-
</View>
|
|
81
|
-
</TouchableOpacity>
|
|
82
|
-
)}
|
|
83
|
-
</View>
|
|
84
|
-
) : (
|
|
85
|
-
<View style={styles.settingSection}>
|
|
86
|
-
{onOpenActivity && (
|
|
87
|
-
<View>
|
|
88
|
-
<TouchableOpacity onPress={onOpenActivity}>
|
|
89
|
-
<View style={styles.iconBackground}>
|
|
90
|
-
<FontAwesome size={21} name="bell-o" color={theme.text} />
|
|
91
|
-
</View>
|
|
92
|
-
</TouchableOpacity>
|
|
93
|
-
{hasNotifications && (
|
|
94
|
-
<View style={{
|
|
95
|
-
position: 'absolute', right: 5, top: 2,
|
|
96
|
-
width: 8, height: 8, borderRadius: 4, backgroundColor: theme.danger,
|
|
97
|
-
}} />
|
|
98
|
-
)}
|
|
99
|
-
</View>
|
|
100
|
-
)}
|
|
101
|
-
|
|
102
|
-
{onOpenSpecialist && (
|
|
103
|
-
<TouchableOpacity onPress={onOpenSpecialist}>
|
|
104
|
-
<View style={styles.iconBackground}>
|
|
105
|
-
<FontAwesome5 size={20} name="id-card" color={theme.text} />
|
|
106
|
-
</View>
|
|
107
|
-
</TouchableOpacity>
|
|
108
|
-
)}
|
|
109
|
-
|
|
110
|
-
{onOpenAiBots && (
|
|
111
|
-
<TouchableOpacity onPress={onOpenAiBots}>
|
|
67
|
+
<View style={styles.settingSection}>
|
|
68
|
+
{visibleHeaderActions.map((action, index) => (
|
|
69
|
+
<View key={action.id ?? `header-action-${index}`} style={styles.headerActionItem}>
|
|
70
|
+
<TouchableOpacity onPress={action.onPress}>
|
|
112
71
|
<View style={styles.iconBackground}>
|
|
113
|
-
|
|
72
|
+
{action.icon}
|
|
114
73
|
</View>
|
|
115
74
|
</TouchableOpacity>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
</View>
|
|
123
|
-
</TouchableOpacity>
|
|
124
|
-
)}
|
|
125
|
-
|
|
126
|
-
{onOpenSettings && (
|
|
127
|
-
<TouchableOpacity onPress={onOpenSettings}>
|
|
128
|
-
<View style={styles.iconBackground}>
|
|
129
|
-
<Ionicons size={24} name="settings-outline" color={theme.text} />
|
|
130
|
-
</View>
|
|
131
|
-
</TouchableOpacity>
|
|
132
|
-
)}
|
|
133
|
-
</View>
|
|
134
|
-
)}
|
|
75
|
+
{action.showDot && (
|
|
76
|
+
<View style={styles.headerActionDot} />
|
|
77
|
+
)}
|
|
78
|
+
</View>
|
|
79
|
+
))}
|
|
80
|
+
</View>
|
|
135
81
|
</View>
|
|
136
82
|
|
|
137
83
|
{/* аватар */}
|
|
@@ -161,36 +107,24 @@ export default function PureProfileCard(props: PureProfileCardProps) {
|
|
|
161
107
|
</TouchableOpacity>}
|
|
162
108
|
|
|
163
109
|
{/* CTA */}
|
|
164
|
-
{
|
|
110
|
+
{visibleCtaActions.length > 0 && (
|
|
165
111
|
<View style={styles.buttonsRow}>
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{onOpenCreatePoll && (
|
|
183
|
-
<TouchableOpacity style={styles.btnOutline} onPress={onOpenCreatePoll}>
|
|
184
|
-
<Ionicons name="stats-chart-outline" size={20} color={theme.primary} />
|
|
185
|
-
<Text style={styles.btnOutlineText}>Create Poll</Text>
|
|
186
|
-
</TouchableOpacity>
|
|
187
|
-
)}
|
|
188
|
-
{onOpenCreateEvent && (
|
|
189
|
-
<TouchableOpacity style={styles.btnOutline} onPress={onOpenCreateEvent}>
|
|
190
|
-
<Octicons name="diff-added" size={20} color={theme.primary} />
|
|
191
|
-
<Text style={styles.btnOutlineText}>Create Event</Text>
|
|
192
|
-
</TouchableOpacity>
|
|
193
|
-
)}
|
|
112
|
+
{visibleCtaActions.map((action, index) => {
|
|
113
|
+
const isPrimary = (action.variant ?? 'outline') === 'primary';
|
|
114
|
+
return (
|
|
115
|
+
<TouchableOpacity
|
|
116
|
+
key={`${action.label}-${index}`}
|
|
117
|
+
style={[
|
|
118
|
+
isPrimary ? styles.btnPrimary : styles.btnOutline,
|
|
119
|
+
visibleCtaActions.length === 1 && styles.btnWide,
|
|
120
|
+
]}
|
|
121
|
+
onPress={action.onPress}
|
|
122
|
+
>
|
|
123
|
+
{action.icon}
|
|
124
|
+
<Text style={isPrimary ? styles.btnPrimaryText : styles.btnOutlineText}>{action.label}</Text>
|
|
125
|
+
</TouchableOpacity>
|
|
126
|
+
);
|
|
127
|
+
})}
|
|
194
128
|
</View>
|
|
195
129
|
)}
|
|
196
130
|
|
|
@@ -19,12 +19,23 @@ export const useProfileCardStyles = () => {
|
|
|
19
19
|
},
|
|
20
20
|
navigation: { width: '100%' },
|
|
21
21
|
settingSection: { flexDirection: 'row' },
|
|
22
|
+
headerActionItem: {
|
|
23
|
+
position: 'relative',
|
|
24
|
+
},
|
|
22
25
|
iconBackground: {
|
|
23
26
|
backgroundColor: theme.backgroundLight,
|
|
24
27
|
height: 40, width: 40, borderRadius: 50,
|
|
25
28
|
alignItems: 'center', justifyContent: 'center',
|
|
26
29
|
},
|
|
27
|
-
|
|
30
|
+
headerActionDot: {
|
|
31
|
+
position: 'absolute',
|
|
32
|
+
right: 5,
|
|
33
|
+
top: 2,
|
|
34
|
+
width: 8,
|
|
35
|
+
height: 8,
|
|
36
|
+
borderRadius: 4,
|
|
37
|
+
backgroundColor: theme.danger,
|
|
38
|
+
},
|
|
28
39
|
learnMoreText: { color: theme.placeholder, marginLeft: 5 },
|
|
29
40
|
|
|
30
41
|
buttonsRow: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ModalProfilePhoto } from './ModalProfilePhoto';
|
|
2
2
|
export { default as ProfileCard } from './ProfileCard/ProfileCard';
|
|
3
|
-
export type { PureProfileCardProps } from './ProfileCard/ProfileCard';
|
|
3
|
+
export type { PureProfileCardProps, PureProfileCardCtaAction, PureProfileCardHeaderAction } from './ProfileCard/ProfileCard';
|
|
4
4
|
export { default as ProfilePhotoBanner } from './ProfilePhotoBanner';
|
|
5
5
|
export { default as ProfilePhotoUpload } from './ProfilePhotoUpload/ProfilePhotoUpload';
|
|
6
6
|
export type { ProfilePhotoUploadProps } from './ProfilePhotoUpload/ProfilePhotoUpload';
|