rn-vs-lb 1.0.64 → 1.0.66

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 (113) hide show
  1. package/components/Button/Button.stories.tsx +1 -1
  2. package/components/Button/DeleteAccountButton.stories.tsx +13 -1
  3. package/components/Button/DeleteAccountButton.tsx +28 -8
  4. package/components/Button/PostButton.stories.tsx +1 -1
  5. package/components/Button/TelegramFeedbackLink.stories.tsx +7 -1
  6. package/components/Button/TelegramFeedbackLink.tsx +12 -4
  7. package/components/Cards/BusinessIdeaCard.stories.tsx +73 -0
  8. package/components/Cards/BusinessIdeaCard.tsx +251 -0
  9. package/components/Cards/EventCard.stories.tsx +4 -1
  10. package/components/Cards/EventCard.tsx +9 -2
  11. package/components/Cards/PlaceCard.stories.tsx +1 -1
  12. package/components/Cards/index.tsx +1 -0
  13. package/components/Chat/ChatItem.stories.tsx +1 -1
  14. package/components/Chat/ChatTab/ChatTabs.stories.tsx +1 -1
  15. package/components/Chat/ChatTab/SubTabButton.stories.tsx +1 -1
  16. package/components/Chat/InputMessage.stories.tsx +12 -1
  17. package/components/Chat/InputMessage.tsx +5 -3
  18. package/components/Chat/MessageItem/MessageItem.stories.tsx +1 -1
  19. package/components/Chat/PinnedMessagesBar/PinnedMessagesBar.stories.tsx +1 -1
  20. package/components/Gallery/AiAgentGallery.stories.tsx +1 -1
  21. package/components/Gallery/ProfileMediaGallery.stories.tsx +66 -0
  22. package/components/Gallery/ProfileMediaGallery.tsx +239 -0
  23. package/components/Gallery/ProfileMediaNavigationItem.tsx +73 -0
  24. package/components/Gallery/ProfileSelfiesGallery.pure.tsx +97 -0
  25. package/components/Gallery/ProfileSelfiesGallery.stories.tsx +86 -0
  26. package/components/Gallery/index.ts +4 -1
  27. package/components/Header/HeaderDefault.stories.tsx +1 -1
  28. package/components/Header/HeaderEdit.stories.tsx +1 -1
  29. package/components/Header/HeaderHome.stories.tsx +1 -1
  30. package/components/Header/HeaderSwitcher.stories.tsx +1 -1
  31. package/components/Header/HeaderWithImg.stories.tsx +10 -1
  32. package/components/Header/HeaderWithImg.tsx +27 -7
  33. package/components/Modals/GalleryModal.stories.tsx +1 -1
  34. package/components/Modals/GuestAiChatModal.stories.tsx +12 -4
  35. package/components/Modals/GuestAiChatModal.tsx +9 -3
  36. package/components/Modals/ReportModal.stories.tsx +35 -2
  37. package/components/Modals/ReportModal.tsx +23 -24
  38. package/components/Poll/CommentItem.stories.tsx +1 -1
  39. package/components/Poll/PollCardList.stories.tsx +5 -1
  40. package/components/Poll/PollCardList.tsx +4 -2
  41. package/components/Posts/EventCardList.stories.tsx +1 -1
  42. package/components/Posts/PlaceCardList.stories.tsx +1 -1
  43. package/components/Prank/HeroPrankCard.stories.tsx +47 -0
  44. package/components/Prank/HeroPrankCard.tsx +114 -0
  45. package/components/Prank/HomelessPrankPage.stories.tsx +58 -0
  46. package/components/Prank/UploadPromptCard.stories.tsx +55 -0
  47. package/components/Prank/UploadPromptCard.tsx +130 -0
  48. package/components/Prank/index.ts +5 -0
  49. package/components/Profile/ModalProfilePhoto.stories.tsx +1 -1
  50. package/components/Profile/ProfileCard/ProfileCard.stories.tsx +1 -1
  51. package/components/Profile/ProfilePhotoBanner.stories.tsx +11 -2
  52. package/components/Profile/ProfilePhotoBanner.tsx +5 -3
  53. package/components/Profile/ProfilePhotoUpload/ProfilePhotoUpload.stories.tsx +1 -1
  54. package/components/Profile/ProfileSummary/ProfileSummary.stories.tsx +52 -0
  55. package/components/Profile/ProfileSummary/ProfileSummary.tsx +188 -0
  56. package/components/Profile/ProfileTabs/UserProfileTabs.stories.tsx +1 -1
  57. package/components/Profile/index.ts +2 -0
  58. package/components/Screens/AppScreens.stories.tsx +572 -0
  59. package/components/Screens/HomeScreen.stories.tsx +159 -0
  60. package/components/Screens/LibraryScreen.stories.tsx +97 -0
  61. package/components/Settings/AppSettingsScreen.stories.tsx +134 -0
  62. package/components/Settings/AppSettingsScreen.tsx +215 -0
  63. package/components/Settings/SettingsHeader.tsx +37 -0
  64. package/components/Settings/SettingsListItem.tsx +142 -0
  65. package/components/Settings/SettingsManageAccountButton.tsx +58 -0
  66. package/components/Settings/SettingsOptionList.tsx +82 -0
  67. package/components/Settings/SettingsProfileCard.tsx +58 -0
  68. package/components/Settings/SettingsScreen.stories.tsx +66 -0
  69. package/components/Settings/SettingsScreen.tsx +129 -0
  70. package/components/Settings/SettingsSection.tsx +56 -0
  71. package/components/Settings/SettingsToggleItem.tsx +70 -0
  72. package/components/Settings/index.ts +23 -0
  73. package/components/Specialist/Hero.stories.tsx +2 -1
  74. package/components/Specialist/Hero.tsx +3 -1
  75. package/components/Specialist/PortfolioCarousel.stories.tsx +1 -1
  76. package/components/Specialist/ServicesList.stories.tsx +2 -1
  77. package/components/Specialist/ServicesList.tsx +4 -3
  78. package/components/Tooltip/DangerTooltip.stories.tsx +1 -1
  79. package/components/Tooltip/InfoTooltip.stories.tsx +1 -1
  80. package/components/Tooltip/InfoTooltipBase.stories.tsx +1 -1
  81. package/components/Tooltip/SucceedTooltip.stories.tsx +1 -1
  82. package/components/Tooltip/WarningTooltip.stories.tsx +1 -1
  83. package/components/UI/DeletedState.stories.tsx +6 -1
  84. package/components/UI/DeletedState.tsx +12 -3
  85. package/components/UI/DescriptionMore.stories.tsx +2 -0
  86. package/components/UI/DescriptionMore.tsx +13 -3
  87. package/components/UI/EmptyState.stories.tsx +4 -1
  88. package/components/UI/EmptyState.tsx +3 -2
  89. package/components/UI/HorizontalCardSection.stories.tsx +85 -0
  90. package/components/UI/HorizontalCardSection.tsx +199 -0
  91. package/components/UI/NoAuth.stories.tsx +3 -0
  92. package/components/UI/NoAuth.tsx +7 -6
  93. package/components/UI/ParticipantItem.stories.tsx +21 -0
  94. package/components/UI/ParticipantItem.tsx +30 -6
  95. package/components/UI/TabBar/BottomTabBar.stories.tsx +87 -0
  96. package/components/UI/TabBar/BottomTabBar.tsx +128 -0
  97. package/components/UI/ThemeSwitcher.stories.tsx +5 -1
  98. package/components/UI/ThemeSwitcher.tsx +7 -2
  99. package/components/UI/UpdateRequiredView.stories.tsx +2 -0
  100. package/components/UI/UpdateRequiredView.tsx +3 -2
  101. package/components/UI/index.ts +4 -0
  102. package/components/UserCards/AiAgentHeroCard.stories.tsx +66 -0
  103. package/components/UserCards/AiAgentHeroCard.tsx +242 -0
  104. package/components/UserCards/Organazer.tsx +3 -2
  105. package/components/UserCards/Organizer.stories.tsx +5 -1
  106. package/components/UserCards/SpecialistCard.stories.tsx +3 -1
  107. package/components/UserCards/SpecialistCard.tsx +3 -2
  108. package/components/UserCards/StoryCard.stories.tsx +1 -1
  109. package/components/UserCards/UserProfileCard.stories.tsx +1 -1
  110. package/components/UserCards/UserRow.stories.tsx +1 -1
  111. package/components/UserCards/index.ts +2 -0
  112. package/components/index.ts +2 -0
  113. package/package.json +2 -1
@@ -6,7 +6,7 @@ import { MaterialIcons } from '@expo/vector-icons';
6
6
  import Button, { MyButtonProps } from './Button';
7
7
 
8
8
  const meta: Meta<MyButtonProps> = {
9
- title: 'Button/Base',
9
+ title: 'UI/Buttons/Base',
10
10
  component: Button,
11
11
  decorators: [
12
12
  (Story) => (
@@ -3,7 +3,7 @@ import { Meta, StoryFn } from '@storybook/react';
3
3
  import { DeleteAccountButton, DeleteAccountButtonProps } from './DeleteAccountButton';
4
4
 
5
5
  const meta: Meta<DeleteAccountButtonProps> = {
6
- title: 'Button/DeleteAccountButton',
6
+ title: 'UI/Buttons/DeleteAccountButton',
7
7
  component: DeleteAccountButton,
8
8
  };
9
9
 
@@ -18,9 +18,21 @@ const createMockDelete = (delay = 800) => async () => {
18
18
  export const Default = Template.bind({});
19
19
  Default.args = {
20
20
  deleteAccount: createMockDelete(),
21
+ triggerLabel: 'Delete account',
22
+ modalTitle: 'Confirm Deletion',
23
+ modalDescription:
24
+ 'All your data, including profile, events, and chat history, will be permanently deleted. This process is irreversible and will be completed within 24 hours. Are you sure you want to proceed?',
25
+ cancelButtonLabel: 'Cancel',
26
+ confirmButtonLabel: 'Delete',
21
27
  };
22
28
 
23
29
  export const SlowNetwork = Template.bind({});
24
30
  SlowNetwork.args = {
25
31
  deleteAccount: createMockDelete(2000),
32
+ triggerLabel: 'Delete account',
33
+ modalTitle: 'Confirm Deletion',
34
+ modalDescription:
35
+ 'All your data, including profile, events, and chat history, will be permanently deleted. This process is irreversible and will be completed within 24 hours. Are you sure you want to proceed?',
36
+ cancelButtonLabel: 'Cancel',
37
+ confirmButtonLabel: 'Delete',
26
38
  };
@@ -6,9 +6,21 @@ import { ThemeType, useTheme } from '../../theme';
6
6
 
7
7
  export type DeleteAccountButtonProps = {
8
8
  deleteAccount: () => Promise<void>;
9
+ triggerLabel: string;
10
+ modalTitle: string;
11
+ modalDescription: string;
12
+ cancelButtonLabel: string;
13
+ confirmButtonLabel: string;
9
14
  }
10
15
 
11
- export const DeleteAccountButton: FC<DeleteAccountButtonProps> = ({ deleteAccount }) => {
16
+ export const DeleteAccountButton: FC<DeleteAccountButtonProps> = ({
17
+ deleteAccount,
18
+ triggerLabel,
19
+ modalTitle,
20
+ modalDescription,
21
+ cancelButtonLabel,
22
+ confirmButtonLabel,
23
+ }) => {
12
24
  const [modalVisible, setModalVisible] = useState(false);
13
25
  const { theme, typography } = useTheme();
14
26
  const styles = getStyles({ theme });
@@ -21,7 +33,7 @@ export const DeleteAccountButton: FC<DeleteAccountButtonProps> = ({ deleteAccoun
21
33
  return (
22
34
  <View style={styles.container}>
23
35
  <TouchableOpacity style={styles.deleteButton} onPress={() => setModalVisible(true)}>
24
- <Text style={[typography.titleH6, { color: theme.red }]}>Delete account</Text>
36
+ <Text style={[typography.titleH6, { color: theme.red }]}>{triggerLabel}</Text>
25
37
  </TouchableOpacity>
26
38
 
27
39
  <Modal
@@ -32,17 +44,25 @@ export const DeleteAccountButton: FC<DeleteAccountButtonProps> = ({ deleteAccoun
32
44
  >
33
45
  <View style={styles.modalOverlay}>
34
46
  <View style={styles.modalContent}>
35
- <Text style={typography.titleH5}>Confirm Deletion</Text>
47
+ <Text style={typography.titleH5}>{modalTitle}</Text>
36
48
  <Spacer size="xxs" />
37
49
  <Text style={[typography.body, { textAlign: "center" }]}>
38
- All your data, including profile, events, and chat history, will be permanently deleted.
39
- This process is irreversible and will be completed within 24 hours.
40
- Are you sure you want to proceed?
50
+ {modalDescription}
41
51
  </Text>
42
52
  <Spacer size="lg" />
43
53
  <View style={styles.buttonRow}>
44
- <Button onPress={() => setModalVisible(false)} style={styles.cancelButton} type="gray-outline" title="Cancel" />
45
- <Button onPress={handleDeleteAccount} type="report-outline" style={styles.confirmButton} title="Delete" />
54
+ <Button
55
+ onPress={() => setModalVisible(false)}
56
+ style={styles.cancelButton}
57
+ type="gray-outline"
58
+ title={cancelButtonLabel}
59
+ />
60
+ <Button
61
+ onPress={handleDeleteAccount}
62
+ type="report-outline"
63
+ style={styles.confirmButton}
64
+ title={confirmButtonLabel}
65
+ />
46
66
  </View>
47
67
  </View>
48
68
  </View>
@@ -3,7 +3,7 @@ import { Meta, StoryFn } from '@storybook/react';
3
3
  import PostButton from './PostButton';
4
4
 
5
5
  const meta: Meta = {
6
- title: 'Button/PostButton',
6
+ title: 'UI/Buttons/PostButton',
7
7
  component: PostButton,
8
8
  argTypes: {
9
9
  title: {
@@ -3,7 +3,7 @@ import { Meta, StoryFn } from '@storybook/react';
3
3
  import { TelegramFeedbackLink, TelegramFeedbackLinkProps } from './TelegramFeedbackLink';
4
4
 
5
5
  const meta: Meta<TelegramFeedbackLinkProps> = {
6
- title: 'Button/TelegramFeedbackLink',
6
+ title: 'UI/Buttons/TelegramFeedbackLink',
7
7
  component: TelegramFeedbackLink,
8
8
  argTypes: {
9
9
  link: {
@@ -20,9 +20,15 @@ const Template: StoryFn<TelegramFeedbackLinkProps> = (args) => <TelegramFeedback
20
20
  export const Default = Template.bind({});
21
21
  Default.args = {
22
22
  link: 'https://t.me/volunteer_support_bot',
23
+ title: 'Feedback & Bugs',
24
+ subtitle: 'Tap to write us in Telegram',
25
+ unsupportedLinkMessage: "Can't open Telegram URL",
23
26
  };
24
27
 
25
28
  export const CustomRoom = Template.bind({});
26
29
  CustomRoom.args = {
27
30
  link: 'https://t.me/joinchat/ExampleRoom',
31
+ title: 'Report an Issue',
32
+ subtitle: 'Reach out to the team on Telegram',
33
+ unsupportedLinkMessage: "Can't open Telegram URL",
28
34
  };
@@ -6,9 +6,17 @@ import { useTheme } from '../../theme';
6
6
 
7
7
  export type TelegramFeedbackLinkProps = {
8
8
  link: string;
9
+ title: string;
10
+ subtitle: string;
11
+ unsupportedLinkMessage: string;
9
12
  }
10
13
 
11
- export const TelegramFeedbackLink: React.FC<TelegramFeedbackLinkProps> = ({ link }) => {
14
+ export const TelegramFeedbackLink: React.FC<TelegramFeedbackLinkProps> = ({
15
+ link,
16
+ title,
17
+ subtitle,
18
+ unsupportedLinkMessage,
19
+ }) => {
12
20
  const { theme, typography } = useTheme();
13
21
 
14
22
  const handlePress = async () => {
@@ -16,7 +24,7 @@ export const TelegramFeedbackLink: React.FC<TelegramFeedbackLinkProps> = ({ link
16
24
  if (supported) {
17
25
  await Linking.openURL(link);
18
26
  } else {
19
- console.warn("Can't open Telegram URL");
27
+ console.warn(unsupportedLinkMessage);
20
28
  }
21
29
  };
22
30
 
@@ -24,8 +32,8 @@ export const TelegramFeedbackLink: React.FC<TelegramFeedbackLinkProps> = ({ link
24
32
  <TouchableOpacity style={styles.container} onPress={handlePress}>
25
33
  <FontAwesome name={'telegram'} size={26} color={theme.primaryLight} />
26
34
  <View style={styles.textContainer}>
27
- <Text style={[typography.titleH6, { color: theme.text }]}>Feedback & Bugs</Text>
28
- <Text style={typography.bodySm}>Tap to write us in Telegram</Text>
35
+ <Text style={[typography.titleH6, { color: theme.text }]}>{title}</Text>
36
+ <Text style={typography.bodySm}>{subtitle}</Text>
29
37
  </View>
30
38
  </TouchableOpacity>
31
39
  );
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import BusinessIdeaCard from './BusinessIdeaCard';
4
+ import { ThemeProvider } from '../../theme/themeContext';
5
+
6
+ const meta: Meta<typeof BusinessIdeaCard> = {
7
+ title: 'UI/Cards/BusinessIdeaCard',
8
+ component: BusinessIdeaCard,
9
+ decorators: [
10
+ (Story) => (
11
+ <ThemeProvider>
12
+ <Story />
13
+ </ThemeProvider>
14
+ ),
15
+ ],
16
+ argTypes: {
17
+ avatarUri: { control: 'text' },
18
+ channelLabel: { control: 'text' },
19
+ imageUri: { control: 'text' },
20
+ quote: { control: 'text' },
21
+ likes: { control: 'number' },
22
+ comments: { control: 'number' },
23
+ shares: { control: 'number' },
24
+ timeAgo: { control: 'text' },
25
+ liked: { control: 'boolean' },
26
+ showMore: { control: 'boolean' },
27
+ showMoreLabel: { control: 'text' },
28
+ onPressLike: { action: 'like pressed' },
29
+ onPressComment: { action: 'comment pressed' },
30
+ onPressShare: { action: 'share pressed' },
31
+ onPressMore: { action: 'more pressed' },
32
+ onPressShowMore: { action: 'show more pressed' },
33
+ },
34
+ };
35
+
36
+ export default meta;
37
+
38
+ type Story = StoryObj<typeof BusinessIdeaCard>;
39
+
40
+ export const Default: Story = {
41
+ args: {
42
+ avatarUri: 'https://i.pravatar.cc/100?img=65',
43
+ channelLabel: 'Бизнес идеи',
44
+ imageUri:
45
+ 'https://images.wsj.net/im-861283?width=700&height=467',
46
+ quote:
47
+ '«Я думаю, что мы будем жить в мире, где появятся сотни миллионов и миллиарды разных ИИ-агентов, и, возможно, их будет больше, чем людей»',
48
+ likes: 13,
49
+ comments: 7,
50
+ shares: 5,
51
+ timeAgo: '2 d ago',
52
+ showMoreLabel: 'Show more',
53
+ showMore: true,
54
+ },
55
+ };
56
+
57
+ export const WithoutAvatar: Story = {
58
+ args: {
59
+ ...Default.args,
60
+ avatarUri: '',
61
+ channelLabel: 'AI news',
62
+ },
63
+ };
64
+
65
+ export const LongQuote: Story = {
66
+ args: {
67
+ ...Default.args,
68
+ quote:
69
+ '«Мы видим, как искусственный интеллект постепенно перестает быть инструментом и становится полноценным партнером в работе. В ближайшие годы появится огромное количество агентов, которые будут помогать людям решать бытовые и профессиональные задачи, освобождая время для творчества и стратегического мышления.»',
70
+ showMore: false,
71
+ liked: true,
72
+ },
73
+ };
@@ -0,0 +1,251 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ Image,
6
+ TouchableOpacity,
7
+ StyleSheet,
8
+ ImageSourcePropType,
9
+ TextStyle,
10
+ } from 'react-native';
11
+ import { Ionicons } from '@expo/vector-icons';
12
+ import { useTheme, ThemeType, SizesType } from '../../theme';
13
+ import Spacer from '../UI/Spacer';
14
+ import { GlobalStyleSheetType } from '../../theme/styles/styleSheet';
15
+
16
+ export interface BusinessIdeaCardProps {
17
+ avatarUri: string;
18
+ channelLabel: string;
19
+ imageUri: string;
20
+ quote: string;
21
+ likes: number;
22
+ comments: number;
23
+ shares: number;
24
+ timeAgo: string;
25
+ showMore?: boolean;
26
+ showMoreLabel?: string;
27
+ liked?: boolean;
28
+ onPressLike?: () => void;
29
+ onPressComment?: () => void;
30
+ onPressShare?: () => void;
31
+ onPressMore?: () => void;
32
+ onPressShowMore?: () => void;
33
+ avatarPlaceholder?: ImageSourcePropType;
34
+ }
35
+
36
+ const BusinessIdeaCard: React.FC<BusinessIdeaCardProps> = ({
37
+ avatarUri,
38
+ channelLabel,
39
+ imageUri,
40
+ quote,
41
+ likes,
42
+ comments,
43
+ shares,
44
+ timeAgo,
45
+ showMore = true,
46
+ showMoreLabel = 'Show more',
47
+ liked = false,
48
+ onPressLike,
49
+ onPressComment,
50
+ onPressShare,
51
+ onPressMore,
52
+ onPressShowMore,
53
+ avatarPlaceholder,
54
+ }) => {
55
+ const { theme, sizes, commonStyles, typography, globalStyleSheet } = useTheme();
56
+ const styles = getStyles({ theme, sizes, globalStyleSheet });
57
+
58
+ const renderAvatar = () => {
59
+ if (avatarUri) {
60
+ return <Image source={{ uri: avatarUri }} style={styles.avatar} />;
61
+ }
62
+
63
+ if (avatarPlaceholder) {
64
+ return <Image source={avatarPlaceholder} style={styles.avatar} />;
65
+ }
66
+
67
+ return (
68
+ <View style={[styles.avatar, styles.avatarFallback]}>
69
+ <Ionicons name="bulb" size={18} color={theme.primary} />
70
+ </View>
71
+ );
72
+ };
73
+
74
+ return (
75
+ <View style={[commonStyles.card, commonStyles.shadow, styles.container]}>
76
+ <View style={styles.header}>
77
+ <View style={styles.headerLeft}>
78
+ {renderAvatar()}
79
+ <Text style={styles.channelLabel}>{channelLabel}</Text>
80
+ </View>
81
+ {onPressMore ? (
82
+ <TouchableOpacity onPress={onPressMore} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
83
+ <Ionicons name="ellipsis-horizontal" size={20} color={theme.greyText} />
84
+ </TouchableOpacity>
85
+ ) : (
86
+ <Ionicons name="ellipsis-horizontal" size={20} color={theme.greyText} />
87
+ )}
88
+ </View>
89
+
90
+ <Spacer size="xs" />
91
+
92
+ <Image source={{ uri: imageUri }} style={styles.coverImage} resizeMode="cover" />
93
+
94
+ <Spacer size="sm" />
95
+
96
+ <Text style={[typography.body, styles.quote]} numberOfLines={4} ellipsizeMode="tail">
97
+ {quote}
98
+ </Text>
99
+
100
+ {showMore && (
101
+ <>
102
+ <Spacer size="xxs" />
103
+ {onPressShowMore ? (
104
+ <TouchableOpacity onPress={onPressShowMore} activeOpacity={0.7}>
105
+ <Text style={styles.showMore}>{showMoreLabel}</Text>
106
+ </TouchableOpacity>
107
+ ) : (
108
+ <Text style={styles.showMore}>{showMoreLabel}</Text>
109
+ )}
110
+ </>
111
+ )}
112
+
113
+ <Spacer size="sm" />
114
+
115
+ <View style={styles.footer}>
116
+ <View style={styles.actionsRow}>
117
+ <ActionButton
118
+ iconName={liked ? 'heart' : 'heart-outline'}
119
+ iconColor={liked ? theme.danger : theme.greyText}
120
+ label={likes}
121
+ onPress={onPressLike}
122
+ />
123
+ <ActionButton
124
+ iconName="chatbubble-ellipses-outline"
125
+ iconColor={theme.greyText}
126
+ label={comments}
127
+ onPress={onPressComment}
128
+ />
129
+ <ActionButton
130
+ iconName="arrow-redo-outline"
131
+ iconColor={theme.greyText}
132
+ label={shares}
133
+ onPress={onPressShare}
134
+ />
135
+ </View>
136
+ <Text style={styles.timeAgo}>{timeAgo}</Text>
137
+ </View>
138
+ </View>
139
+ );
140
+ };
141
+
142
+ interface StyleParams {
143
+ theme: ThemeType;
144
+ sizes: SizesType;
145
+ globalStyleSheet: GlobalStyleSheetType;
146
+ }
147
+
148
+ const getStyles = ({ theme, sizes, globalStyleSheet }: StyleParams) =>
149
+ StyleSheet.create({
150
+ container: {
151
+ padding: sizes.md,
152
+ borderRadius: sizes.radius_lg,
153
+ backgroundColor: theme.white,
154
+ },
155
+ header: {
156
+ ...globalStyleSheet.flexRowCenterBetween,
157
+ },
158
+ headerLeft: {
159
+ ...globalStyleSheet.flexRowCenterStart,
160
+ gap: sizes.xs,
161
+ },
162
+ avatar: {
163
+ width: 36,
164
+ height: 36,
165
+ borderRadius: 18,
166
+ backgroundColor: theme.backgroundSecond,
167
+ overflow: 'hidden',
168
+ },
169
+ avatarFallback: {
170
+ alignItems: 'center',
171
+ justifyContent: 'center',
172
+ },
173
+ channelLabel: {
174
+ ...globalStyleSheet.descriptionCard,
175
+ color: theme.title,
176
+ fontWeight: '600',
177
+ textTransform: 'uppercase',
178
+ letterSpacing: 0.8,
179
+ },
180
+ coverImage: {
181
+ width: '100%',
182
+ height: 220,
183
+ borderRadius: sizes.radius_sm,
184
+ backgroundColor: theme.backgroundSecond,
185
+ },
186
+ quote: {
187
+ color: theme.title,
188
+ fontStyle: 'italic',
189
+ },
190
+ showMore: {
191
+ color: theme.greyText,
192
+ fontSize: sizes.fontSm,
193
+ fontWeight: '500',
194
+ } as TextStyle,
195
+ footer: {
196
+ ...globalStyleSheet.flexRowCenterBetween,
197
+ alignItems: 'center',
198
+ },
199
+ actionsRow: {
200
+ flexDirection: 'row',
201
+ alignItems: 'center',
202
+ columnGap: sizes.md,
203
+ gap: sizes.md,
204
+ },
205
+ timeAgo: {
206
+ color: theme.greyText,
207
+ fontSize: sizes.fontSm,
208
+ } as TextStyle,
209
+ });
210
+
211
+ type ActionButtonProps = {
212
+ iconName: React.ComponentProps<typeof Ionicons>['name'];
213
+ iconColor: string;
214
+ label: number;
215
+ onPress?: () => void;
216
+ };
217
+
218
+ const ActionButton: React.FC<ActionButtonProps> = ({ iconName, iconColor, label, onPress }) => {
219
+ const { theme, sizes } = useTheme();
220
+ const styles = StyleSheet.create({
221
+ action: {
222
+ flexDirection: 'row',
223
+ alignItems: 'center',
224
+ columnGap: sizes.xs,
225
+ gap: sizes.xs,
226
+ },
227
+ label: {
228
+ color: theme.text,
229
+ fontSize: sizes.fontSm,
230
+ fontWeight: '500',
231
+ },
232
+ });
233
+
234
+ if (!onPress) {
235
+ return (
236
+ <View style={styles.action}>
237
+ <Ionicons name={iconName} size={18} color={iconColor} />
238
+ <Text style={styles.label}>{label}</Text>
239
+ </View>
240
+ );
241
+ }
242
+
243
+ return (
244
+ <TouchableOpacity onPress={onPress} activeOpacity={0.7} style={styles.action}>
245
+ <Ionicons name={iconName} size={18} color={iconColor} />
246
+ <Text style={styles.label}>{label}</Text>
247
+ </TouchableOpacity>
248
+ );
249
+ };
250
+
251
+ export default BusinessIdeaCard;
@@ -4,7 +4,7 @@ import EventCard from './EventCard';
4
4
  import { ThemeProvider } from '../../theme/themeContext';
5
5
 
6
6
  const meta: Meta<React.ComponentProps<typeof EventCard>> = {
7
- title: 'Cards/EventCard',
7
+ title: 'UI/Cards/EventCard',
8
8
  component: EventCard,
9
9
  decorators: [
10
10
  (Story) => (
@@ -57,12 +57,15 @@ export const Default: Story = {
57
57
  'Crash Drum Studio — пространство, где можно почувствовать силу ритма и научиться играть на ударных в любой форме.',
58
58
  organizerAvatarUri: 'https://i.pravatar.cc/150?img=11',
59
59
  organizerName: 'Admin Belgrade',
60
+ organizerRoleLabel: 'Organizer',
60
61
  likes: 42,
61
62
  views: 313,
62
63
  hasLike: false,
63
64
  isUserParticipantInPost: false,
64
65
  participantsCount: 0,
65
66
  visible: true,
67
+ participantLabel: 'You’re participating',
68
+ demoLabel: 'DEMO',
66
69
  },
67
70
  };
68
71
 
@@ -13,6 +13,7 @@ interface EventCardProps {
13
13
  description: string;
14
14
  organizerAvatarUri: string;
15
15
  organizerName: string;
16
+ organizerRoleLabel: string;
16
17
  onPress: () => void;
17
18
  likes: number;
18
19
  views: number;
@@ -24,6 +25,8 @@ interface EventCardProps {
24
25
  maxParticipants?: number;
25
26
  categories?: string[];
26
27
  price?: string;
28
+ participantLabel: string;
29
+ demoLabel: string;
27
30
 
28
31
  /** Новое: видимость карточки сообщает родитель (FlatList/ScrollView и т.д.) */
29
32
  visible?: boolean;
@@ -39,6 +42,7 @@ const EventCard: React.FC<EventCardProps> = ({
39
42
  description,
40
43
  organizerAvatarUri,
41
44
  organizerName,
45
+ organizerRoleLabel,
42
46
  onPress,
43
47
  likes,
44
48
  views,
@@ -50,6 +54,8 @@ const EventCard: React.FC<EventCardProps> = ({
50
54
  maxParticipants,
51
55
  categories,
52
56
  price,
57
+ participantLabel,
58
+ demoLabel,
53
59
  visible = false,
54
60
  triggerOnce = true,
55
61
  }) => {
@@ -77,7 +83,7 @@ const EventCard: React.FC<EventCardProps> = ({
77
83
  <Image source={{ uri: imageUri }} style={styles.image} resizeMode="cover" />
78
84
  {isUserParticipantInPost && (
79
85
  <View style={styles.participantOverlay}>
80
- <Text style={styles.participantText}>You’re participating</Text>
86
+ <Text style={styles.participantText}>{participantLabel}</Text>
81
87
  </View>
82
88
  )}
83
89
  </View>
@@ -86,7 +92,7 @@ const EventCard: React.FC<EventCardProps> = ({
86
92
  <View style={globalStyleSheet.flexRowCenterStart}>
87
93
  {date && <Text style={[typography.body, styles.date]}>{date}</Text>}
88
94
  {categories?.includes('bot') && (
89
- <Text style={[typography.body, styles.demo]}>DEMO</Text>
95
+ <Text style={[typography.body, styles.demo]}>{demoLabel}</Text>
90
96
  )}
91
97
  {price && <Text style={[typography.body, styles.demo]}>{price}</Text>}
92
98
  </View>
@@ -107,6 +113,7 @@ const EventCard: React.FC<EventCardProps> = ({
107
113
  <Organizer
108
114
  avatarUri={organizerAvatarUri}
109
115
  organizerName={organizerName}
116
+ roleLabel={organizerRoleLabel}
110
117
  onPress={onPress}
111
118
  />
112
119
  <SocialStatsEvent onLike={onLike} hasLike={hasLike} likes={likes} views={views} />
@@ -4,7 +4,7 @@ import PlaceCard from './PlaceCard';
4
4
  import { ThemeProvider } from '../../theme/themeContext';
5
5
 
6
6
  const meta: Meta<React.ComponentProps<typeof PlaceCard>> = {
7
- title: 'Cards/PlaceCard',
7
+ title: 'UI/Cards/PlaceCard',
8
8
  component: PlaceCard,
9
9
  decorators: [
10
10
  (Story) => (
@@ -2,3 +2,4 @@ export { default as PlaceCard } from './PlaceCard';
2
2
  export { default as EventCard } from './EventCard';
3
3
  export { default as Organazer } from '../UserCards/Organazer';
4
4
  export { default as SpecialistCard } from '../UserCards/SpecialistCard';
5
+ export { default as BusinessIdeaCard } from './BusinessIdeaCard';
@@ -10,7 +10,7 @@ const createPressHandler = (label: string) => () => {
10
10
  };
11
11
 
12
12
  const meta: Meta<ChatItemProps> = {
13
- title: 'Chat/ChatItem',
13
+ title: 'Features/Chat/ChatItem',
14
14
  component: ChatItem,
15
15
  decorators: [
16
16
  (Story) => (
@@ -4,7 +4,7 @@ import { View } from 'react-native';
4
4
  import ChatTabs, { type ChatTab, type BotSubTab } from './ChatTabs';
5
5
 
6
6
  const meta = {
7
- title: 'Chat/ChatTabs',
7
+ title: 'Features/Chat/ChatTabs',
8
8
  component: ChatTabs,
9
9
  decorators: [(Story) => <View style={{ paddingVertical: 12 }}><Story /></View>],
10
10
  argTypes: {
@@ -4,7 +4,7 @@ import { View } from 'react-native';
4
4
  import SubTabButton from './SubTabButton';
5
5
 
6
6
  const meta = {
7
- title: 'Chat/SubTabButton',
7
+ title: 'Features/Chat/SubTabButton',
8
8
  component: SubTabButton,
9
9
  decorators: [(Story) => <View style={{ padding: 12 }}><Story /></View>],
10
10
  argTypes: {