@umituz/react-native-settings 4.21.10 → 4.21.12

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 (46) hide show
  1. package/package.json +11 -2
  2. package/src/domains/gamification/components/GamificationScreenWrapper.tsx +42 -87
  3. package/src/domains/gamification/components/index.ts +1 -0
  4. package/src/domains/gamification/index.ts +3 -11
  5. package/src/index.ts +3 -0
  6. package/src/notifications/domains/quietHours/infrastructure/hooks/useQuietHoursActions.ts +52 -0
  7. package/src/notifications/domains/quietHours/presentation/components/QuietHoursCard.tsx +112 -0
  8. package/src/notifications/domains/reminders/infrastructure/config/reminderPresets.ts +120 -0
  9. package/src/notifications/domains/reminders/infrastructure/hooks/useReminderActions.ts +106 -0
  10. package/src/notifications/domains/reminders/infrastructure/storage/RemindersStore.ts +148 -0
  11. package/src/notifications/domains/reminders/presentation/components/FormButton.tsx +66 -0
  12. package/src/notifications/domains/reminders/presentation/components/FrequencySelector.tsx +72 -0
  13. package/src/notifications/domains/reminders/presentation/components/ReminderForm.tsx +169 -0
  14. package/src/notifications/domains/reminders/presentation/components/ReminderItem.tsx +130 -0
  15. package/src/notifications/domains/reminders/presentation/components/TimePresetSelector.tsx +100 -0
  16. package/src/notifications/domains/reminders/presentation/components/WeekdaySelector.tsx +61 -0
  17. package/src/notifications/domains/reminders/presentation/screens/ReminderListScreen.tsx +131 -0
  18. package/src/notifications/index.ts +139 -0
  19. package/src/notifications/infrastructure/config/notificationsConfig.ts +98 -0
  20. package/src/notifications/infrastructure/hooks/useNotificationSettings.ts +37 -0
  21. package/src/notifications/infrastructure/services/NotificationBadgeManager.ts +28 -0
  22. package/src/notifications/infrastructure/services/NotificationManager.ts +138 -0
  23. package/src/notifications/infrastructure/services/NotificationPermissions.ts +80 -0
  24. package/src/notifications/infrastructure/services/NotificationScheduler.ts +77 -0
  25. package/src/notifications/infrastructure/services/NotificationService.ts +50 -0
  26. package/src/notifications/infrastructure/services/types.ts +176 -0
  27. package/src/notifications/infrastructure/storage/NotificationsStore.ts +45 -0
  28. package/src/notifications/infrastructure/utils/dev.ts +25 -0
  29. package/src/notifications/infrastructure/utils/idGenerator.ts +14 -0
  30. package/src/notifications/infrastructure/utils/triggerBuilder.ts +45 -0
  31. package/src/notifications/presentation/components/NotificationsSection.tsx +84 -0
  32. package/src/notifications/presentation/components/RemindersNavRow.styles.ts +38 -0
  33. package/src/notifications/presentation/components/RemindersNavRow.tsx +51 -0
  34. package/src/notifications/presentation/components/SettingRow.tsx +86 -0
  35. package/src/notifications/presentation/hooks/useNotificationSettingsUI.ts +52 -0
  36. package/src/notifications/presentation/hooks/useTimePicker.ts +71 -0
  37. package/src/notifications/presentation/screens/NotificationSettingsScreen.styles.ts +30 -0
  38. package/src/notifications/presentation/screens/NotificationSettingsScreen.tsx +131 -0
  39. package/src/notifications/presentation/screens/NotificationsScreen.tsx +107 -0
  40. package/src/presentation/navigation/SettingsStackNavigator.tsx +21 -11
  41. package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +1 -1
  42. package/src/domains/gamification/README.md +0 -343
  43. package/src/domains/gamification/components/GamificationSettingsItem.tsx +0 -33
  44. package/src/domains/gamification/examples/gamification.config.example.ts +0 -70
  45. package/src/domains/gamification/examples/localization.example.json +0 -71
  46. package/src/domains/gamification/types/settings.ts +0 -28
@@ -0,0 +1,80 @@
1
+ import * as Notifications from 'expo-notifications';
2
+ import * as Device from 'expo-device';
3
+ import { Platform } from 'react-native';
4
+ import { devWarn, devError, devLog } from '../utils/dev';
5
+
6
+ export class NotificationPermissions {
7
+ async requestPermissions(): Promise<boolean> {
8
+ try {
9
+ if (!Device.isDevice) {
10
+ devWarn('[NotificationPermissions] Notifications only work on physical devices');
11
+ return false;
12
+ }
13
+
14
+ const permissionsResponse = await Notifications.getPermissionsAsync();
15
+ const existingStatus = (permissionsResponse as any).status || ((permissionsResponse as any).granted ? 'granted' : 'denied');
16
+ let finalStatus = existingStatus;
17
+
18
+ if (existingStatus !== 'granted') {
19
+ const requestResponse = await Notifications.requestPermissionsAsync();
20
+ finalStatus = (requestResponse as any).status || ((requestResponse as any).granted ? 'granted' : 'denied');
21
+ }
22
+
23
+ if (Platform.OS === 'android') {
24
+ await this.createAndroidChannels();
25
+ }
26
+
27
+ return finalStatus === 'granted';
28
+ } catch (error) {
29
+ devError('[NotificationPermissions] Permission request failed:', error);
30
+ return false;
31
+ }
32
+ }
33
+
34
+ async hasPermissions(): Promise<boolean> {
35
+ try {
36
+ if (!Device.isDevice) return false;
37
+ const permissionsResponse = await Notifications.getPermissionsAsync();
38
+ return (permissionsResponse as any).status === 'granted' || (permissionsResponse as any).granted === true;
39
+ } catch (error) {
40
+ devError('[NotificationPermissions] Permission check failed:', error);
41
+ return false;
42
+ }
43
+ }
44
+
45
+ private async createAndroidChannels(): Promise<void> {
46
+ if (Platform.OS !== 'android') return;
47
+
48
+ try {
49
+ await Notifications.setNotificationChannelAsync('default', {
50
+ name: 'Default',
51
+ importance: Notifications.AndroidImportance.DEFAULT,
52
+ vibrationPattern: [0, 250, 250, 250],
53
+ sound: 'default',
54
+ lightColor: '#3B82F6',
55
+ });
56
+
57
+ await Notifications.setNotificationChannelAsync('reminders', {
58
+ name: 'Reminders',
59
+ importance: Notifications.AndroidImportance.HIGH,
60
+ vibrationPattern: [0, 250, 250, 250],
61
+ sound: 'default',
62
+ lightColor: '#3B82F6',
63
+ enableVibrate: true,
64
+ });
65
+
66
+ await Notifications.setNotificationChannelAsync('urgent', {
67
+ name: 'Urgent',
68
+ importance: Notifications.AndroidImportance.MAX,
69
+ vibrationPattern: [0, 500, 250, 500],
70
+ sound: 'default',
71
+ lightColor: '#EF4444',
72
+ enableVibrate: true,
73
+ });
74
+
75
+ devLog('[NotificationPermissions] Android channels created');
76
+ } catch (error) {
77
+ devError('[NotificationPermissions] Android channel creation failed:', error);
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,77 @@
1
+ import * as Notifications from 'expo-notifications';
2
+ import type { NotificationTrigger, ScheduleNotificationOptions, ScheduledNotification } from './types';
3
+
4
+ export class NotificationScheduler {
5
+ async scheduleNotification(options: ScheduleNotificationOptions): Promise<string> {
6
+ const { title, body, data = {}, trigger, sound = true, badge, categoryIdentifier } = options;
7
+
8
+ let notificationTrigger: any;
9
+
10
+ if (trigger.type === 'date') {
11
+ notificationTrigger = {
12
+ date: trigger.date,
13
+ channelId: categoryIdentifier || 'default',
14
+ };
15
+ } else if (trigger.type === 'daily') {
16
+ notificationTrigger = {
17
+ hour: trigger.hour,
18
+ minute: trigger.minute,
19
+ repeats: true,
20
+ channelId: categoryIdentifier || 'reminders',
21
+ };
22
+ } else if (trigger.type === 'weekly') {
23
+ notificationTrigger = {
24
+ weekday: trigger.weekday,
25
+ hour: trigger.hour,
26
+ minute: trigger.minute,
27
+ repeats: true,
28
+ channelId: categoryIdentifier || 'reminders',
29
+ };
30
+ } else if (trigger.type === 'monthly') {
31
+ notificationTrigger = {
32
+ day: trigger.day,
33
+ hour: trigger.hour,
34
+ minute: trigger.minute,
35
+ repeats: true,
36
+ channelId: categoryIdentifier || 'reminders',
37
+ };
38
+ }
39
+
40
+ const notificationId = await Notifications.scheduleNotificationAsync({
41
+ content: {
42
+ title,
43
+ body,
44
+ data,
45
+ sound: sound === true ? 'default' : sound || undefined,
46
+ badge,
47
+ categoryIdentifier,
48
+ priority: Notifications.AndroidNotificationPriority.HIGH,
49
+ vibrate: [0, 250, 250, 250],
50
+ },
51
+ trigger: notificationTrigger,
52
+ });
53
+
54
+ return notificationId;
55
+ }
56
+
57
+ async cancelNotification(notificationId: string): Promise<void> {
58
+ await Notifications.cancelScheduledNotificationAsync(notificationId);
59
+ }
60
+
61
+ async cancelAllNotifications(): Promise<void> {
62
+ await Notifications.cancelAllScheduledNotificationsAsync();
63
+ }
64
+
65
+ async getScheduledNotifications(): Promise<ScheduledNotification[]> {
66
+ const notifications = await Notifications.getAllScheduledNotificationsAsync();
67
+ return notifications.map(notification => ({
68
+ identifier: notification.identifier,
69
+ content: {
70
+ title: notification.content.title || '',
71
+ body: notification.content.body || '',
72
+ data: notification.content.data as Record<string, any>,
73
+ },
74
+ trigger: notification.trigger,
75
+ }));
76
+ }
77
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * NotificationService
3
+ *
4
+ * Simple facade for offline notification system.
5
+ * Works in ALL apps - offline, online, hybrid - no backend required.
6
+ *
7
+ * @module NotificationService
8
+ */
9
+
10
+ import { NotificationManager } from './NotificationManager';
11
+
12
+ export * from './types';
13
+
14
+ /**
15
+ * Notification service singleton
16
+ * Provides simple access to notification manager
17
+ */
18
+ export class NotificationService {
19
+ private static instance: NotificationService;
20
+
21
+ readonly notifications = new NotificationManager();
22
+
23
+ private constructor() {
24
+ // Configure notification handler on initialization
25
+ NotificationManager.configure();
26
+ }
27
+
28
+ static getInstance(): NotificationService {
29
+ if (!NotificationService.instance) {
30
+ NotificationService.instance = new NotificationService();
31
+ }
32
+ return NotificationService.instance;
33
+ }
34
+
35
+ /**
36
+ * Request notification permissions
37
+ */
38
+ async requestPermissions(): Promise<boolean> {
39
+ return await this.notifications.requestPermissions();
40
+ }
41
+
42
+ /**
43
+ * Check if permissions are granted
44
+ */
45
+ async hasPermissions(): Promise<boolean> {
46
+ return await this.notifications.hasPermissions();
47
+ }
48
+ }
49
+
50
+ export const notificationService = NotificationService.getInstance();
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Offline-First Notification Types
3
+ * Uses expo-notifications for local device notifications
4
+ */
5
+
6
+ import * as Notifications from 'expo-notifications';
7
+
8
+ // ============================================================================
9
+ // NOTIFICATION CHANNEL TYPES
10
+ // ============================================================================
11
+
12
+ export interface NotificationChannel {
13
+ id: string;
14
+ channel_type: 'push' | 'in_app';
15
+ channel_address: string;
16
+ preferences: Record<string, unknown>;
17
+ is_verified: boolean;
18
+ is_active: boolean;
19
+ created_at: string;
20
+ }
21
+
22
+ // ============================================================================
23
+ // NOTIFICATION TRIGGER TYPES
24
+ // ============================================================================
25
+
26
+ export type NotificationTrigger =
27
+ | { type: 'date'; date: Date }
28
+ | { type: 'daily'; hour: number; minute: number }
29
+ | { type: 'weekly'; weekday: number; hour: number; minute: number }
30
+ | { type: 'monthly'; day: number; hour: number; minute: number };
31
+
32
+ export interface ScheduleNotificationOptions {
33
+ title: string;
34
+ body: string;
35
+ data?: Record<string, unknown>;
36
+ trigger: NotificationTrigger;
37
+ sound?: boolean | string;
38
+ badge?: number;
39
+ categoryIdentifier?: string;
40
+ }
41
+
42
+ export interface ScheduledNotification {
43
+ identifier: string;
44
+ content: {
45
+ title: string;
46
+ body: string;
47
+ data: Record<string, unknown>;
48
+ };
49
+ trigger: Notifications.NotificationTrigger;
50
+ }
51
+
52
+ // ============================================================================
53
+ // TIME PRESET TYPES
54
+ // ============================================================================
55
+
56
+ export interface TimePreset {
57
+ id: string;
58
+ hour: number;
59
+ minute: number;
60
+ labelKey: string;
61
+ iconName: string;
62
+ }
63
+
64
+ // ============================================================================
65
+ // REMINDER TYPES
66
+ // ============================================================================
67
+
68
+ export type ReminderFrequency = 'once' | 'daily' | 'weekly' | 'monthly';
69
+
70
+ export interface Reminder {
71
+ id: string;
72
+ title: string;
73
+ body: string;
74
+ frequency: ReminderFrequency;
75
+ timePresetId?: string;
76
+ hour: number;
77
+ minute: number;
78
+ weekday?: number;
79
+ dayOfMonth?: number;
80
+ enabled: boolean;
81
+ notificationId?: string;
82
+ createdAt: string;
83
+ updatedAt: string;
84
+ }
85
+
86
+ export interface CreateReminderInput {
87
+ title: string;
88
+ body: string;
89
+ frequency: ReminderFrequency;
90
+ timePresetId?: string;
91
+ hour: number;
92
+ minute: number;
93
+ weekday?: number;
94
+ dayOfMonth?: number;
95
+ }
96
+
97
+ export interface UpdateReminderInput {
98
+ title?: string;
99
+ body?: string;
100
+ frequency?: ReminderFrequency;
101
+ timePresetId?: string;
102
+ hour?: number;
103
+ minute?: number;
104
+ weekday?: number;
105
+ dayOfMonth?: number;
106
+ enabled?: boolean;
107
+ }
108
+
109
+ // ============================================================================
110
+ // QUIET HOURS TYPES
111
+ // ============================================================================
112
+
113
+ export interface QuietHoursConfig {
114
+ enabled: boolean;
115
+ startHour: number;
116
+ startMinute: number;
117
+ endHour: number;
118
+ endMinute: number;
119
+ }
120
+
121
+ // ============================================================================
122
+ // NOTIFICATION PREFERENCES
123
+ // ============================================================================
124
+
125
+ export interface NotificationPreferences {
126
+ enabled: boolean;
127
+ sound: boolean;
128
+ vibration: boolean;
129
+ quietHours: QuietHoursConfig;
130
+ }
131
+
132
+ // ============================================================================
133
+ // SCREEN CONFIGURATION TYPES
134
+ // ============================================================================
135
+
136
+ export interface ReminderTranslations {
137
+ screenTitle: string;
138
+ emptyTitle: string;
139
+ emptyDescription: string;
140
+ addButtonLabel: string;
141
+ editButtonLabel: string;
142
+ deleteButtonLabel: string;
143
+ enabledLabel: string;
144
+ disabledLabel: string;
145
+ frequencyOnce: string;
146
+ frequencyDaily: string;
147
+ frequencyWeekly: string;
148
+ frequencyMonthly: string;
149
+ timeLabel: string;
150
+ titlePlaceholder: string;
151
+ bodyPlaceholder: string;
152
+ saveButtonLabel: string;
153
+ cancelButtonLabel: string;
154
+ }
155
+
156
+ export interface QuietHoursTranslations {
157
+ title: string;
158
+ description: string;
159
+ startTimeLabel: string;
160
+ endTimeLabel: string;
161
+ enabledLabel: string;
162
+ }
163
+
164
+ export interface NotificationSettingsTranslations {
165
+ screenTitle: string;
166
+ masterToggleTitle: string;
167
+ masterToggleDescription: string;
168
+ soundTitle: string;
169
+ soundDescription: string;
170
+ vibrationTitle: string;
171
+ vibrationDescription: string;
172
+ remindersTitle: string;
173
+ remindersDescription: string;
174
+ quietHoursTitle: string;
175
+ quietHoursDescription: string;
176
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Notifications Store - Zustand State Management
3
+ * Simple offline-first notification state
4
+ * NO backend, NO user IDs, NO notification history
5
+ */
6
+
7
+ import { createStore } from '@umituz/react-native-storage';
8
+
9
+ interface NotificationsState {
10
+ hasPermissions: boolean;
11
+ isInitialized: boolean;
12
+ }
13
+
14
+ interface NotificationsActions {
15
+ setPermissions: (granted: boolean) => void;
16
+ setInitialized: (initialized: boolean) => void;
17
+ }
18
+
19
+ export const useNotificationsStore = createStore<NotificationsState, NotificationsActions>({
20
+ name: 'notifications-store',
21
+ initialState: {
22
+ hasPermissions: false,
23
+ isInitialized: false,
24
+ },
25
+ persist: false,
26
+ actions: (set) => ({
27
+ setPermissions: (granted) => set({ hasPermissions: granted }),
28
+ setInitialized: (initialized) => set({ isInitialized: initialized }),
29
+ }),
30
+ });
31
+
32
+ /**
33
+ * Hook for accessing notifications state
34
+ */
35
+ export const useNotifications = () => {
36
+ const store = useNotificationsStore();
37
+ const { hasPermissions, isInitialized, setPermissions, setInitialized } = store;
38
+
39
+ return {
40
+ hasPermissions,
41
+ isInitialized,
42
+ setPermissions,
43
+ setInitialized,
44
+ };
45
+ };
@@ -0,0 +1,25 @@
1
+ export const isDev = () => {
2
+ try {
3
+ return typeof __DEV__ !== 'undefined' && __DEV__;
4
+ } catch {
5
+ return false;
6
+ }
7
+ };
8
+
9
+ export const devLog = (message: string, ...args: any[]) => {
10
+ if (isDev()) {
11
+ console.log(message, ...args);
12
+ }
13
+ };
14
+
15
+ export const devError = (message: string, ...args: any[]) => {
16
+ if (isDev()) {
17
+ console.error(message, ...args);
18
+ }
19
+ };
20
+
21
+ export const devWarn = (message: string, ...args: any[]) => {
22
+ if (isDev()) {
23
+ console.warn(message, ...args);
24
+ }
25
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ID Generator Utility
3
+ * Generates unique IDs for entities
4
+ */
5
+
6
+ export const generateId = (prefix: string = 'id'): string => {
7
+ const timestamp = Date.now();
8
+ const random = Math.random().toString(36).substring(2, 11);
9
+ return `${prefix}_${timestamp}_${random}`;
10
+ };
11
+
12
+ export const generateReminderId = (): string => {
13
+ return generateId('reminder');
14
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Trigger Builder Utility
3
+ * Builds notification triggers from reminder data
4
+ */
5
+
6
+ import type { NotificationTrigger, Reminder } from '../services/types';
7
+
8
+ export const buildTrigger = (reminder: Reminder): NotificationTrigger => {
9
+ const { frequency, hour, minute, weekday, dayOfMonth } = reminder;
10
+
11
+ switch (frequency) {
12
+ case 'once': {
13
+ const date = new Date();
14
+ date.setHours(hour, minute, 0, 0);
15
+
16
+ if (date <= new Date()) {
17
+ date.setDate(date.getDate() + 1);
18
+ }
19
+
20
+ return { type: 'date', date };
21
+ }
22
+
23
+ case 'daily':
24
+ return { type: 'daily', hour, minute };
25
+
26
+ case 'weekly':
27
+ return {
28
+ type: 'weekly',
29
+ weekday: weekday || 1,
30
+ hour,
31
+ minute,
32
+ };
33
+
34
+ case 'monthly':
35
+ return {
36
+ type: 'monthly',
37
+ day: dayOfMonth || 1,
38
+ hour,
39
+ minute,
40
+ };
41
+
42
+ default:
43
+ return { type: 'daily', hour, minute };
44
+ }
45
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * NotificationsSection Component
3
+ * Settings section for notifications - navigates to full screen
4
+ */
5
+
6
+ import React, { useCallback, useMemo } from 'react';
7
+ import type { StyleProp, ViewStyle } from 'react-native';
8
+ import { View, TouchableOpacity, StyleSheet } from 'react-native';
9
+ import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
10
+ import { useAppDesignTokens } from '@umituz/react-native-design-system';
11
+ // @ts-ignore - Optional peer dependency
12
+ import { useNavigation } from '@react-navigation/native';
13
+ // @ts-ignore - Optional peer dependency
14
+ import { useLocalization } from '@umituz/react-native-localization';
15
+
16
+ export interface NotificationsSectionConfig {
17
+ route?: string;
18
+ title?: string;
19
+ description?: string;
20
+ sectionTitle?: string;
21
+ }
22
+
23
+ export interface NotificationsSectionProps {
24
+ config?: NotificationsSectionConfig;
25
+ containerStyle?: StyleProp<ViewStyle>;
26
+ }
27
+
28
+ export const NotificationsSection: React.FC<NotificationsSectionProps> = ({
29
+ config,
30
+ containerStyle,
31
+ }) => {
32
+ const navigation = useNavigation();
33
+ const { t } = useLocalization();
34
+ const tokens = useAppDesignTokens();
35
+ const styles = useMemo(() => createStyles(tokens), [tokens]);
36
+
37
+ const handlePress = useCallback(() => {
38
+ const route = config?.route || 'Notifications';
39
+ navigation.navigate(route as never);
40
+ }, [config?.route, navigation]);
41
+
42
+ const title = config?.title || t('settings.notifications.title');
43
+ const description = config?.description || t('settings.notifications.description');
44
+ const sectionTitle = config?.sectionTitle || t('settings.notifications.sectionTitle');
45
+
46
+ return (
47
+ <View style={[styles.container, containerStyle]}>
48
+ <AtomicText type="bodyLarge" style={styles.sectionTitle}>{sectionTitle}</AtomicText>
49
+
50
+ <TouchableOpacity
51
+ style={styles.itemContainer}
52
+ onPress={handlePress}
53
+ activeOpacity={0.7}
54
+ >
55
+ <View style={styles.iconContainer}>
56
+ <AtomicIcon name="notifications" size="md" color="primary" />
57
+ </View>
58
+ <View style={styles.textContainer}>
59
+ <AtomicText type="bodyLarge">{title}</AtomicText>
60
+ <AtomicText type="bodySmall" style={styles.description}>{description}</AtomicText>
61
+ </View>
62
+ <AtomicIcon name="chevron-forward" size="md" color="secondary" />
63
+ </TouchableOpacity>
64
+ </View>
65
+ );
66
+ };
67
+
68
+ const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
69
+ StyleSheet.create({
70
+ container: { marginBottom: 16, borderRadius: 12, overflow: 'hidden', backgroundColor: tokens.colors.surface },
71
+ sectionTitle: { fontWeight: '600', paddingHorizontal: 16, paddingTop: 16, paddingBottom: 8 },
72
+ itemContainer: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 16, minHeight: 72 },
73
+ iconContainer: {
74
+ width: 48,
75
+ height: 48,
76
+ borderRadius: 12,
77
+ justifyContent: 'center',
78
+ alignItems: 'center',
79
+ marginRight: 16,
80
+ backgroundColor: tokens.colors.surfaceSecondary,
81
+ },
82
+ textContainer: { flex: 1, marginRight: 8 },
83
+ description: { color: tokens.colors.textSecondary, marginTop: 4 },
84
+ });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * RemindersNavRow Styles
3
+ */
4
+
5
+ import { StyleSheet } from 'react-native';
6
+ import type { DesignTokens } from '@umituz/react-native-design-system';
7
+
8
+ export const createStyles = (tokens: DesignTokens) =>
9
+ StyleSheet.create({
10
+ navRow: {
11
+ flexDirection: 'row',
12
+ alignItems: 'center',
13
+ },
14
+ iconContainer: {
15
+ width: 44,
16
+ height: 44,
17
+ borderRadius: 22,
18
+ backgroundColor: tokens.colors.surfaceSecondary,
19
+ justifyContent: 'center',
20
+ alignItems: 'center',
21
+ marginRight: 12,
22
+ },
23
+ textContainer: {
24
+ flex: 1,
25
+ marginRight: 12,
26
+ },
27
+ badge: {
28
+ backgroundColor: tokens.colors.primary,
29
+ paddingHorizontal: 8,
30
+ paddingVertical: 2,
31
+ borderRadius: 10,
32
+ marginRight: 8,
33
+ },
34
+ badgeText: {
35
+ color: tokens.colors.surface,
36
+ fontWeight: '600',
37
+ },
38
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * RemindersNavRow Component
3
+ * Reusable navigation row for reminders section with badge
4
+ */
5
+
6
+ import React from 'react';
7
+ import { View, TouchableOpacity } from 'react-native';
8
+ import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
9
+ import { createStyles } from './RemindersNavRow.styles';
10
+ import { useAppDesignTokens } from '@umituz/react-native-design-system';
11
+
12
+ export interface RemindersNavRowProps {
13
+ title: string;
14
+ description: string;
15
+ count: number;
16
+ onPress: () => void;
17
+ }
18
+
19
+ export const RemindersNavRow: React.FC<RemindersNavRowProps> = ({
20
+ title,
21
+ description,
22
+ count,
23
+ onPress,
24
+ }) => {
25
+ const tokens = useAppDesignTokens();
26
+ const styles = createStyles(tokens);
27
+
28
+ return (
29
+ <TouchableOpacity style={styles.navRow} onPress={onPress} activeOpacity={0.7}>
30
+ <View style={styles.iconContainer}>
31
+ <AtomicIcon name="time" size="md" color="primary" />
32
+ </View>
33
+ <View style={styles.textContainer}>
34
+ <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
35
+ {title}
36
+ </AtomicText>
37
+ <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary }}>
38
+ {description}
39
+ </AtomicText>
40
+ </View>
41
+ {count > 0 && (
42
+ <View style={styles.badge}>
43
+ <AtomicText type="bodySmall" style={styles.badgeText}>
44
+ {count}
45
+ </AtomicText>
46
+ </View>
47
+ )}
48
+ <AtomicIcon name="chevron-forward" size="md" color="secondary" />
49
+ </TouchableOpacity>
50
+ );
51
+ };