create-gufran-expo-app 2.0.3 → 2.0.5
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/README.md +1 -2
- package/package.json +3 -3
- package/template/src/navigation/AuthStack.tsx +6 -25
- package/template/src/navigation/MainStack.tsx +0 -148
- package/template/src/navigation/RootNavigator.tsx +4 -26
- package/template/src/navigation/index.ts +0 -1
- package/template/src/navigation/navigationRef.ts +1 -1
- package/template/src/screens/HomeScreen.tsx +3 -215
- package/template/src/screens/auth/LoginScreen.tsx +13 -13
- package/template/src/screens/auth/index.ts +1 -6
- package/template/src/screens/index.ts +0 -35
- package/template/src/services/api.ts +5 -5
- package/template/src/services/authService.ts +3 -299
- package/template/src/services/mainServices.ts +19 -1914
- package/template/src/types/navigation.ts +5 -155
- package/template/src/utils/index.ts +5 -8
- package/template/src/navigation/MiddleStack.tsx +0 -35
- package/template/src/screens/auth/AddMamber.tsx +0 -428
- package/template/src/screens/auth/ForgotPasswordScreen.tsx +0 -176
- package/template/src/screens/auth/OTPVerifyScreen.tsx +0 -359
- package/template/src/screens/auth/RegisterScreen.tsx +0 -430
- package/template/src/screens/auth/SuccessScreen.tsx +0 -201
- package/template/src/screens/chat/ChatScreen.tsx +0 -1819
- package/template/src/screens/chat/ChatThreadsScreen.tsx +0 -360
- package/template/src/screens/chat/ReportMessageScreen.tsx +0 -238
- package/template/src/screens/clubs/Announcements.tsx +0 -426
- package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +0 -568
- package/template/src/screens/clubs/ClubDeteils.tsx +0 -497
- package/template/src/screens/clubs/JoinClub.tsx +0 -841
- package/template/src/screens/events/EventScreen.tsx +0 -460
- package/template/src/screens/raffles/MyReferralMembersScreen.tsx +0 -758
- package/template/src/screens/raffles/RaffleDetailsScreen.tsx +0 -762
- package/template/src/screens/raffles/RafflesScreen.tsx +0 -495
- package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +0 -390
- package/template/src/screens/teams/JoinTeamScreen.tsx +0 -464
- package/template/src/screens/teams/MyTeamDetailsScreen.tsx +0 -979
- package/template/src/screens/teams/MyTeamScreen.tsx +0 -568
- package/template/src/screens/teams/PendingRequestsScreen.tsx +0 -426
- package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +0 -631
- package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +0 -1049
- package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +0 -608
- package/template/src/utils/ClubSearchManager.ts +0 -222
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useMemo } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, FlatList, StatusBar, Platform, TouchableOpacity, Image, RefreshControl, Keyboard, ActivityIndicator } from 'react-native';
|
|
3
|
-
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
4
|
-
import { Strings, theme } from '@constants';
|
|
5
|
-
import { moderateScale } from '@utils/scaling';
|
|
6
|
-
import { Fonts } from '@constants/Fonts';
|
|
7
|
-
import SVG from '@assets/icons';
|
|
8
|
-
import { AnnouncementsScreenProps } from '@navigation';
|
|
9
|
-
import { useInfiniteAnnouncements, Announcement } from '@services/mainServices';
|
|
10
|
-
|
|
11
|
-
export const Announcements: React.FC<AnnouncementsScreenProps> = ({ navigation, route }) => {
|
|
12
|
-
const { selectedClub, selectedMember: initialSelectedMember } = route?.params ?? {}
|
|
13
|
-
const clubId = selectedClub?.id || 0;
|
|
14
|
-
console.log("Announcements selectedClub", selectedClub);
|
|
15
|
-
const pageSize = 10; // Default page size
|
|
16
|
-
|
|
17
|
-
// Use infinite query for announcements
|
|
18
|
-
const {
|
|
19
|
-
data,
|
|
20
|
-
isLoading,
|
|
21
|
-
isError,
|
|
22
|
-
error,
|
|
23
|
-
fetchNextPage,
|
|
24
|
-
hasNextPage,
|
|
25
|
-
isFetchingNextPage,
|
|
26
|
-
refetch,
|
|
27
|
-
isRefetching,
|
|
28
|
-
} = useInfiniteAnnouncements(clubId, pageSize);
|
|
29
|
-
|
|
30
|
-
// Flatten all pages into a single array
|
|
31
|
-
const announcementsData = useMemo(() => {
|
|
32
|
-
if (!data?.pages) return [];
|
|
33
|
-
return data.pages.flatMap(page => {
|
|
34
|
-
const pageData = page?.data?.data;
|
|
35
|
-
if (!pageData) return [];
|
|
36
|
-
|
|
37
|
-
// Handle both structures: { announcements: [] } or direct array
|
|
38
|
-
if (Array.isArray(pageData)) {
|
|
39
|
-
return pageData;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Handle object structure with announcements property
|
|
43
|
-
if (pageData && typeof pageData === 'object' && 'announcements' in pageData) {
|
|
44
|
-
return pageData.announcements || [];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return [];
|
|
48
|
-
});
|
|
49
|
-
}, [data]);
|
|
50
|
-
|
|
51
|
-
const keyExtractor = (item: Announcement, index: number): string => {
|
|
52
|
-
// Create a unique key using message and sentOn date
|
|
53
|
-
const messageHash = item.message?.substring(0, 20) || '';
|
|
54
|
-
const dateHash = item.sentOn || index.toString();
|
|
55
|
-
return `announcement-${dateHash}-${index}`;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// Pull to refresh
|
|
59
|
-
const onRefresh = useCallback(async () => {
|
|
60
|
-
await refetch();
|
|
61
|
-
}, [refetch]);
|
|
62
|
-
|
|
63
|
-
// Handle load more when reaching the end
|
|
64
|
-
const handleLoadMore = useCallback(async () => {
|
|
65
|
-
console.log('handleLoadMore called - hasNextPage:', hasNextPage, 'isFetchingNextPage:', isFetchingNextPage);
|
|
66
|
-
if (hasNextPage && !isFetchingNextPage) {
|
|
67
|
-
console.log('Fetching next page...');
|
|
68
|
-
await fetchNextPage();
|
|
69
|
-
} else {
|
|
70
|
-
console.log('Not fetching - hasNextPage:', hasNextPage, 'isFetchingNextPage:', isFetchingNextPage);
|
|
71
|
-
}
|
|
72
|
-
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const renderEmptyComponent = () => {
|
|
78
|
-
if (isLoading) {
|
|
79
|
-
return (
|
|
80
|
-
<View style={styles.emptyContainer}>
|
|
81
|
-
<ActivityIndicator size="large" color={theme.colors.primary} />
|
|
82
|
-
<Text style={styles.emptyListText}>Loading announcements...</Text>
|
|
83
|
-
</View>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (isError) {
|
|
88
|
-
return (
|
|
89
|
-
<View style={styles.emptyContainer}>
|
|
90
|
-
<Text style={styles.errorText}>Failed to load announcements</Text>
|
|
91
|
-
<Text style={styles.emptySubText}>{error?.message || 'Please try again'}</Text>
|
|
92
|
-
<TouchableOpacity style={styles.retryButton} onPress={() => refetch()}>
|
|
93
|
-
<Text style={styles.retryButtonText}>Retry</Text>
|
|
94
|
-
</TouchableOpacity>
|
|
95
|
-
</View>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return (
|
|
100
|
-
<View style={styles.emptyContainer}>
|
|
101
|
-
<Text style={styles.emptyListText}>No announcements found</Text>
|
|
102
|
-
<Text style={styles.emptySubText}>Try refreshing or check back later</Text>
|
|
103
|
-
</View>
|
|
104
|
-
);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const renderFooter = () => {
|
|
108
|
-
if (!isFetchingNextPage) return null;
|
|
109
|
-
return (
|
|
110
|
-
<View style={styles.footerLoader}>
|
|
111
|
-
<ActivityIndicator size="small" color={theme.colors.primary} />
|
|
112
|
-
<Text style={styles.footerText}>Loading more...</Text>
|
|
113
|
-
</View>
|
|
114
|
-
);
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const handleScroll = useCallback(() => {
|
|
118
|
-
Keyboard.dismiss();
|
|
119
|
-
}, []);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const formatSentOn = (sentOn?: string | number | Date): string => {
|
|
124
|
-
if (!sentOn) return '';
|
|
125
|
-
|
|
126
|
-
const tryFormat = (d: Date) => {
|
|
127
|
-
const day = String(d.getDate()).padStart(2, '0');
|
|
128
|
-
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
129
|
-
const year = d.getFullYear();
|
|
130
|
-
let hours = d.getHours();
|
|
131
|
-
const minutes = String(d.getMinutes()).padStart(2, '0');
|
|
132
|
-
const ampm = hours >= 12 ? 'pm' : 'am';
|
|
133
|
-
hours = hours % 12;
|
|
134
|
-
if (hours === 0) hours = 12;
|
|
135
|
-
return `${day}/${month}/${year}, ${hours}:${minutes} ${ampm}`;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
let date = new Date(sentOn);
|
|
139
|
-
|
|
140
|
-
if (isNaN(date.getTime()) && typeof sentOn === 'string') {
|
|
141
|
-
// Fallback parser for formats like: "29 Oct 2025 07:13 PM"
|
|
142
|
-
const match = sentOn.match(/^(\d{1,2})\s([A-Za-z]{3})\s(\d{4})\s(\d{1,2}):(\d{2})\s?(AM|PM)$/i);
|
|
143
|
-
if (match) {
|
|
144
|
-
const [, dStr, monStr, yStr, hStr, mStr, apStr] = match;
|
|
145
|
-
const monthMap: Record<string, number> = {
|
|
146
|
-
Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5,
|
|
147
|
-
Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11,
|
|
148
|
-
};
|
|
149
|
-
const monthIndex = monthMap[monStr as keyof typeof monthMap];
|
|
150
|
-
if (monthIndex !== undefined) {
|
|
151
|
-
let hour24 = parseInt(hStr, 10) % 12;
|
|
152
|
-
const isPM = /pm/i.test(apStr);
|
|
153
|
-
if (isPM) hour24 += 12;
|
|
154
|
-
const year = parseInt(yStr, 10);
|
|
155
|
-
const day = parseInt(dStr, 10);
|
|
156
|
-
const minute = parseInt(mStr, 10);
|
|
157
|
-
date = new Date(year, monthIndex, day, hour24, minute, 0, 0);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (isNaN(date.getTime())) return String(sentOn);
|
|
163
|
-
|
|
164
|
-
return tryFormat(date);
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const renderAnnouncementItem = ({ item }: { item: Announcement }) => {
|
|
169
|
-
if (!item) return null;
|
|
170
|
-
|
|
171
|
-
return (
|
|
172
|
-
<View style={styles.announcementCard}>
|
|
173
|
-
{/* Message Content */}
|
|
174
|
-
<Text style={styles.announcementMessage}>{item.message}</Text>
|
|
175
|
-
|
|
176
|
-
{/* Image if available */}
|
|
177
|
-
{item.image && (
|
|
178
|
-
<Image
|
|
179
|
-
source={{ uri: item.image }}
|
|
180
|
-
style={styles.announcementImage}
|
|
181
|
-
resizeMode="cover"
|
|
182
|
-
/>
|
|
183
|
-
)}
|
|
184
|
-
|
|
185
|
-
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
|
186
|
-
|
|
187
|
-
<Text style={styles.announcementBy}>By Club Admin</Text>
|
|
188
|
-
|
|
189
|
-
{/* Date/Time */}
|
|
190
|
-
{item.sentOn && (
|
|
191
|
-
<Text style={styles.announcementDate}>{formatSentOn(item.sentOn)}</Text>
|
|
192
|
-
)}
|
|
193
|
-
</View>
|
|
194
|
-
</View>
|
|
195
|
-
);
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<View style={styles.container}>
|
|
201
|
-
<StatusBar
|
|
202
|
-
barStyle="light-content"
|
|
203
|
-
backgroundColor={theme.colors.blue}
|
|
204
|
-
translucent={Platform.OS === 'android' ? true : false}
|
|
205
|
-
/>
|
|
206
|
-
{Platform.OS === 'ios' && <View style={styles.statusBarBackground} />}
|
|
207
|
-
<SafeAreaView style={styles.header} >
|
|
208
|
-
<View style={{ flexDirection: 'row', paddingHorizontal: theme.spacing.lg }}>
|
|
209
|
-
<TouchableOpacity onPress={() => navigation.goBack()}>
|
|
210
|
-
<SVG.arrowLeft_white width={moderateScale(25)} height={moderateScale(25)} />
|
|
211
|
-
</TouchableOpacity>
|
|
212
|
-
<View style={{ flexDirection: 'row' }}>
|
|
213
|
-
<View style={styles.userConSty}>
|
|
214
|
-
{!!selectedClub?.clubImage ? (<Image
|
|
215
|
-
// onLoadEnd={() => setIsLoading(false)}
|
|
216
|
-
// onLoad={() => setIsLoading(true)}
|
|
217
|
-
// onLoadStart={() => setIsLoading(true)}
|
|
218
|
-
source={{ uri: selectedClub?.clubImage }}
|
|
219
|
-
resizeMode='cover'
|
|
220
|
-
style={styles.userDetailsSty} />
|
|
221
|
-
) : (
|
|
222
|
-
<View style={styles.placeholderLogoHeader}>
|
|
223
|
-
<SVG.UsersIcon width={moderateScale(20)} height={moderateScale(20)} />
|
|
224
|
-
</View>
|
|
225
|
-
)}
|
|
226
|
-
</View>
|
|
227
|
-
<Text style={styles.userNameSty}>{selectedClub?.clubName || 'Unknown Club'}</Text>
|
|
228
|
-
</View>
|
|
229
|
-
</View>
|
|
230
|
-
<Text style={styles.headerTitle}>{Strings.Announcements.ANNOUNCEMENTS}</Text>
|
|
231
|
-
<View style={styles.content}>
|
|
232
|
-
<FlatList
|
|
233
|
-
data={announcementsData}
|
|
234
|
-
renderItem={renderAnnouncementItem}
|
|
235
|
-
keyExtractor={keyExtractor}
|
|
236
|
-
style={styles.clubsList}
|
|
237
|
-
showsVerticalScrollIndicator={false}
|
|
238
|
-
contentContainerStyle={styles.flatListContent}
|
|
239
|
-
removeClippedSubviews={false}
|
|
240
|
-
initialNumToRender={10}
|
|
241
|
-
ListEmptyComponent={renderEmptyComponent}
|
|
242
|
-
onScroll={handleScroll}
|
|
243
|
-
scrollEventThrottle={16}
|
|
244
|
-
refreshControl={
|
|
245
|
-
<RefreshControl
|
|
246
|
-
refreshing={isRefetching}
|
|
247
|
-
onRefresh={onRefresh}
|
|
248
|
-
colors={[theme.colors.primary]}
|
|
249
|
-
tintColor={theme.colors.primary}
|
|
250
|
-
/>
|
|
251
|
-
}
|
|
252
|
-
onEndReached={handleLoadMore}
|
|
253
|
-
onEndReachedThreshold={0.5}
|
|
254
|
-
ListFooterComponent={renderFooter}
|
|
255
|
-
maxToRenderPerBatch={10}
|
|
256
|
-
updateCellsBatchingPeriod={50}
|
|
257
|
-
windowSize={10}
|
|
258
|
-
/>
|
|
259
|
-
</View>
|
|
260
|
-
</SafeAreaView>
|
|
261
|
-
|
|
262
|
-
</View>
|
|
263
|
-
);
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const styles = StyleSheet.create({
|
|
267
|
-
container: {
|
|
268
|
-
flex: 1,
|
|
269
|
-
backgroundColor: theme.colors.blue,
|
|
270
|
-
},
|
|
271
|
-
statusBarBackground: {
|
|
272
|
-
position: 'absolute',
|
|
273
|
-
top: 0,
|
|
274
|
-
left: 0,
|
|
275
|
-
right: 0,
|
|
276
|
-
height: Platform.OS === 'ios' ? 44 : 0, // Status bar height for iOS
|
|
277
|
-
backgroundColor: theme.colors.blue,
|
|
278
|
-
zIndex: 1000,
|
|
279
|
-
},
|
|
280
|
-
header: {
|
|
281
|
-
flex: 1,
|
|
282
|
-
backgroundColor: theme.colors.blue,
|
|
283
|
-
paddingVertical: theme.spacing.xs,
|
|
284
|
-
paddingTop: Platform.OS === 'android' ? theme.spacing.lg : theme.spacing.xs,
|
|
285
|
-
},
|
|
286
|
-
headerTitle: {
|
|
287
|
-
marginTop: moderateScale(20),
|
|
288
|
-
fontFamily: Fonts.outfitMedium,
|
|
289
|
-
fontSize: moderateScale(22),
|
|
290
|
-
color: theme.colors.white,
|
|
291
|
-
paddingHorizontal: theme.spacing.lg,
|
|
292
|
-
marginBottom: theme.spacing.sm,
|
|
293
|
-
},
|
|
294
|
-
content: {
|
|
295
|
-
flex: 1,
|
|
296
|
-
backgroundColor: theme.colors.white,
|
|
297
|
-
paddingHorizontal: theme.spacing.lg,
|
|
298
|
-
paddingTop: theme.spacing.md,
|
|
299
|
-
borderTopLeftRadius: moderateScale(30),
|
|
300
|
-
borderTopRightRadius: moderateScale(30),
|
|
301
|
-
},
|
|
302
|
-
clubsList: {
|
|
303
|
-
flex: 1,
|
|
304
|
-
},
|
|
305
|
-
flatListContent: {
|
|
306
|
-
paddingBottom: theme.spacing.xl,
|
|
307
|
-
},
|
|
308
|
-
emptyContainer: {
|
|
309
|
-
flex: 1,
|
|
310
|
-
justifyContent: 'center',
|
|
311
|
-
alignItems: 'center',
|
|
312
|
-
paddingVertical: theme.spacing.xl,
|
|
313
|
-
},
|
|
314
|
-
emptyListText: {
|
|
315
|
-
fontFamily: Fonts.outfitMedium,
|
|
316
|
-
fontSize: theme.typography.fontSize.md,
|
|
317
|
-
color: theme.colors.text,
|
|
318
|
-
textAlign: 'center',
|
|
319
|
-
marginBottom: theme.spacing.xs,
|
|
320
|
-
},
|
|
321
|
-
emptySubText: {
|
|
322
|
-
fontFamily: Fonts.outfitRegular,
|
|
323
|
-
fontSize: theme.typography.fontSize.sm,
|
|
324
|
-
color: theme.colors.textSecondary,
|
|
325
|
-
textAlign: 'center',
|
|
326
|
-
},
|
|
327
|
-
footerLoader: {
|
|
328
|
-
flexDirection: 'row',
|
|
329
|
-
justifyContent: 'center',
|
|
330
|
-
alignItems: 'center',
|
|
331
|
-
paddingVertical: theme.spacing.md,
|
|
332
|
-
gap: theme.spacing.sm,
|
|
333
|
-
},
|
|
334
|
-
footerText: {
|
|
335
|
-
fontFamily: Fonts.outfitMedium,
|
|
336
|
-
fontSize: theme.typography.fontSize.sm,
|
|
337
|
-
color: theme.colors.textSecondary,
|
|
338
|
-
},
|
|
339
|
-
errorText: {
|
|
340
|
-
fontFamily: Fonts.outfitMedium,
|
|
341
|
-
fontSize: theme.typography.fontSize.md,
|
|
342
|
-
color: theme.colors.error,
|
|
343
|
-
textAlign: 'center',
|
|
344
|
-
marginBottom: theme.spacing.md,
|
|
345
|
-
},
|
|
346
|
-
retryButton: {
|
|
347
|
-
backgroundColor: theme.colors.primary,
|
|
348
|
-
paddingHorizontal: theme.spacing.lg,
|
|
349
|
-
paddingVertical: theme.spacing.sm,
|
|
350
|
-
borderRadius: theme.borderRadius.md,
|
|
351
|
-
},
|
|
352
|
-
retryButtonText: {
|
|
353
|
-
fontFamily: Fonts.outfitMedium,
|
|
354
|
-
fontSize: theme.typography.fontSize.md,
|
|
355
|
-
color: theme.colors.white,
|
|
356
|
-
textAlign: 'center',
|
|
357
|
-
},
|
|
358
|
-
userConSty: {
|
|
359
|
-
marginHorizontal: moderateScale(10),
|
|
360
|
-
width: moderateScale(30),
|
|
361
|
-
height: moderateScale(30),
|
|
362
|
-
borderRadius: moderateScale(15),
|
|
363
|
-
borderWidth: 1.5,
|
|
364
|
-
borderColor: theme.colors.imageBorder,
|
|
365
|
-
backgroundColor: theme.colors.background,
|
|
366
|
-
alignItems: 'center',
|
|
367
|
-
justifyContent: 'center'
|
|
368
|
-
},
|
|
369
|
-
userNameSty: {
|
|
370
|
-
marginTop: moderateScale(5),
|
|
371
|
-
color: theme.colors.white,
|
|
372
|
-
fontFamily: Fonts.outfitMedium,
|
|
373
|
-
fontSize: moderateScale(15)
|
|
374
|
-
},
|
|
375
|
-
userDetailsSty: {
|
|
376
|
-
width: moderateScale(30),
|
|
377
|
-
height: moderateScale(30),
|
|
378
|
-
borderRadius: moderateScale(15),
|
|
379
|
-
},
|
|
380
|
-
placeholderLogoHeader: {
|
|
381
|
-
width: moderateScale(20),
|
|
382
|
-
height: moderateScale(20),
|
|
383
|
-
borderRadius: moderateScale(10),
|
|
384
|
-
alignItems: 'center',
|
|
385
|
-
justifyContent: 'center'
|
|
386
|
-
},
|
|
387
|
-
|
|
388
|
-
// New Announcement Card Styles
|
|
389
|
-
announcementCard: {
|
|
390
|
-
backgroundColor: theme.colors.lightLavenderGray,
|
|
391
|
-
borderRadius: moderateScale(12),
|
|
392
|
-
padding: theme.spacing.md,
|
|
393
|
-
marginBottom: theme.spacing.md,
|
|
394
|
-
shadowColor: '#000',
|
|
395
|
-
shadowOffset: { width: 0, height: 2 },
|
|
396
|
-
shadowOpacity: 0.1,
|
|
397
|
-
shadowRadius: 4,
|
|
398
|
-
elevation: 3,
|
|
399
|
-
},
|
|
400
|
-
announcementMessage: {
|
|
401
|
-
fontFamily: Fonts.outfitRegular,
|
|
402
|
-
fontSize: moderateScale(15),
|
|
403
|
-
color: theme.colors.text,
|
|
404
|
-
lineHeight: moderateScale(22),
|
|
405
|
-
marginBottom: theme.spacing.sm,
|
|
406
|
-
},
|
|
407
|
-
announcementImage: {
|
|
408
|
-
width: '100%',
|
|
409
|
-
height: moderateScale(200),
|
|
410
|
-
borderRadius: moderateScale(8),
|
|
411
|
-
marginBottom: theme.spacing.sm,
|
|
412
|
-
backgroundColor: theme.colors.border,
|
|
413
|
-
},
|
|
414
|
-
announcementDate: {
|
|
415
|
-
fontFamily: Fonts.outfitRegular,
|
|
416
|
-
fontSize: moderateScale(12),
|
|
417
|
-
color: theme.colors.textSecondary,
|
|
418
|
-
marginTop: theme.spacing.xs,
|
|
419
|
-
},
|
|
420
|
-
announcementBy: {
|
|
421
|
-
fontFamily: Fonts.outfitRegular,
|
|
422
|
-
fontSize: moderateScale(12),
|
|
423
|
-
color: theme.colors.black,
|
|
424
|
-
marginTop: theme.spacing.xs,
|
|
425
|
-
},
|
|
426
|
-
});
|