@umituz/react-native-notifications 1.1.2 → 1.1.3
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/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/presentation/components/NotificationsSection.d.ts +17 -0
- package/lib/presentation/components/NotificationsSection.d.ts.map +1 -0
- package/lib/presentation/components/NotificationsSection.js +132 -0
- package/lib/presentation/components/NotificationsSection.js.map +1 -0
- package/package.json +4 -3
- package/src/index.ts +5 -0
- package/src/presentation/components/NotificationsSection.tsx +182 -0
package/lib/index.d.ts
CHANGED
|
@@ -11,4 +11,6 @@ export { NotificationService, notificationService } from './infrastructure/servi
|
|
|
11
11
|
export { NotificationManager } from './infrastructure/services/NotificationManager';
|
|
12
12
|
export { useNotificationSettings } from './infrastructure/hooks/useNotificationSettings';
|
|
13
13
|
export { NotificationsScreen } from './presentation/screens/NotificationsScreen';
|
|
14
|
+
export { NotificationsSection } from './presentation/components/NotificationsSection';
|
|
15
|
+
export type { NotificationsSectionProps, NotificationsSectionConfig } from './presentation/components/NotificationsSection';
|
|
14
16
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,YAAY,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAClF,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAGtG,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AAGpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,gDAAgD,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,YAAY,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAClF,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAGtG,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AAGpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,gDAAgD,CAAC;AAQzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAGjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,YAAY,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,gDAAgD,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -16,5 +16,8 @@ export { useNotificationSettings } from './infrastructure/hooks/useNotificationS
|
|
|
16
16
|
// PRESENTATION LAYER EXPORTS
|
|
17
17
|
// ============================================================================
|
|
18
18
|
// Screens
|
|
19
|
+
// Screens
|
|
19
20
|
export { NotificationsScreen } from './presentation/screens/NotificationsScreen';
|
|
21
|
+
// Components
|
|
22
|
+
export { NotificationsSection } from './presentation/components/NotificationsSection';
|
|
20
23
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,gBAAgB;AAChB,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAOlF,wBAAwB;AACxB,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAEtG,WAAW;AACX,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AAEpF,QAAQ;AACR,OAAO,EAAE,uBAAuB,EAAE,MAAM,gDAAgD,CAAC;AAEzF,+EAA+E;AAC/E,6BAA6B;AAC7B,+EAA+E;AAE/E,UAAU;AACV,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,gBAAgB;AAChB,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAOlF,wBAAwB;AACxB,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAEtG,WAAW;AACX,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AAEpF,QAAQ;AACR,OAAO,EAAE,uBAAuB,EAAE,MAAM,gDAAgD,CAAC;AAEzF,+EAA+E;AAC/E,6BAA6B;AAC7B,+EAA+E;AAE/E,UAAU;AACV,UAAU;AACV,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAEjF,aAAa;AACb,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ViewStyle } from 'react-native';
|
|
3
|
+
export interface NotificationsSectionConfig {
|
|
4
|
+
initialValue?: boolean;
|
|
5
|
+
onToggleChange?: (value: boolean) => void;
|
|
6
|
+
route?: string;
|
|
7
|
+
defaultRoute?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
showToggle?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface NotificationsSectionProps {
|
|
13
|
+
config?: NotificationsSectionConfig;
|
|
14
|
+
containerStyle?: ViewStyle;
|
|
15
|
+
}
|
|
16
|
+
export declare const NotificationsSection: React.FC<NotificationsSectionProps>;
|
|
17
|
+
//# sourceMappingURL=NotificationsSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationsSection.d.ts","sourceRoot":"","sources":["../../../src/presentation/components/NotificationsSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAChE,OAAO,EAA6C,SAAS,EAAE,MAAM,cAAc,CAAC;AAMpF,MAAM,WAAW,0BAA0B;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACtC,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,cAAc,CAAC,EAAE,SAAS,CAAC;CAC9B;AAED,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAgHpE,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { View, Text, Pressable, StyleSheet, Switch } from 'react-native';
|
|
3
|
+
import { Feather } from '@expo/vector-icons';
|
|
4
|
+
import { useNavigation } from '@react-navigation/native';
|
|
5
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system-theme';
|
|
6
|
+
import { notificationService } from '../../infrastructure/services/NotificationService';
|
|
7
|
+
export const NotificationsSection = ({ config, containerStyle, }) => {
|
|
8
|
+
const navigation = useNavigation();
|
|
9
|
+
const tokens = useAppDesignTokens();
|
|
10
|
+
const colors = tokens.colors;
|
|
11
|
+
const [notificationsEnabled, setNotificationsEnabled] = useState(config?.initialValue ?? true);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (config?.initialValue !== undefined) {
|
|
14
|
+
setNotificationsEnabled(config.initialValue);
|
|
15
|
+
}
|
|
16
|
+
}, [config?.initialValue]);
|
|
17
|
+
const handleToggle = useCallback(async (value) => {
|
|
18
|
+
if (!value) {
|
|
19
|
+
// When turning ON (value is false -> true? Wait. onChange value is the NEW value)
|
|
20
|
+
// If value is true (turning on)
|
|
21
|
+
}
|
|
22
|
+
// Logic from settings:
|
|
23
|
+
/*
|
|
24
|
+
if (notificationService && !value) { // Wait, logic in settings was: if (!value) ...?
|
|
25
|
+
// Actually typically we request permissions when enabling.
|
|
26
|
+
// Settings code:
|
|
27
|
+
// if (notificationService && !value) { ... } -> This implies when value is FALSE?
|
|
28
|
+
// Ah, maybe the switch value logic was inverted or I misread.
|
|
29
|
+
// Let's re-read settings code.
|
|
30
|
+
}
|
|
31
|
+
*/
|
|
32
|
+
if (value) { // Enabling
|
|
33
|
+
const hasPermissions = await notificationService.hasPermissions();
|
|
34
|
+
if (!hasPermissions) {
|
|
35
|
+
const granted = await notificationService.requestPermissions();
|
|
36
|
+
if (!granted) {
|
|
37
|
+
// Permission denied, maybe don't enable switch?
|
|
38
|
+
// For now just allow toggle and let app handle it or sync with state
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
setNotificationsEnabled(value);
|
|
43
|
+
config?.onToggleChange?.(value);
|
|
44
|
+
}, [config]);
|
|
45
|
+
const handlePress = useCallback(async () => {
|
|
46
|
+
const hasPermissions = await notificationService.hasPermissions();
|
|
47
|
+
if (!hasPermissions) {
|
|
48
|
+
await notificationService.requestPermissions();
|
|
49
|
+
}
|
|
50
|
+
navigation.navigate((config?.route || config?.defaultRoute || 'Notifications'));
|
|
51
|
+
}, [navigation, config?.route, config?.defaultRoute]);
|
|
52
|
+
const title = config?.title || 'Notifications';
|
|
53
|
+
const description = config?.description || 'Manage notification preferences';
|
|
54
|
+
const showToggle = config?.showToggle ?? true;
|
|
55
|
+
return (<View style={[styles.sectionContainer, { backgroundColor: colors.surface }, containerStyle]}>
|
|
56
|
+
<Text style={[styles.sectionTitle, { color: colors.textPrimary }]}>General</Text>
|
|
57
|
+
|
|
58
|
+
<Pressable style={({ pressed }) => [
|
|
59
|
+
styles.itemContainer,
|
|
60
|
+
{
|
|
61
|
+
backgroundColor: pressed && !showToggle ? `${colors.primary}08` : 'transparent',
|
|
62
|
+
},
|
|
63
|
+
]} onPress={showToggle ? undefined : handlePress} disabled={showToggle}>
|
|
64
|
+
<View style={styles.content}>
|
|
65
|
+
<View style={[
|
|
66
|
+
styles.iconContainer,
|
|
67
|
+
{ backgroundColor: `${colors.primary}15` },
|
|
68
|
+
]}>
|
|
69
|
+
<Feather name="bell" size={24} color={colors.primary}/>
|
|
70
|
+
</View>
|
|
71
|
+
<View style={styles.textContainer}>
|
|
72
|
+
<Text style={[styles.title, { color: colors.textPrimary }]}>{title}</Text>
|
|
73
|
+
{!showToggle && (<Text style={[styles.description, { color: colors.textSecondary }]}>
|
|
74
|
+
{description}
|
|
75
|
+
</Text>)}
|
|
76
|
+
</View>
|
|
77
|
+
|
|
78
|
+
{showToggle ? (<Switch value={notificationsEnabled} onValueChange={handleToggle} trackColor={{
|
|
79
|
+
false: `${colors.textSecondary}30`,
|
|
80
|
+
true: colors.primary,
|
|
81
|
+
}} thumbColor={"#FFFFFF"} ios_backgroundColor={`${colors.textSecondary}30`}/>) : (<Feather name="chevron-right" size={20} color={colors.textSecondary}/>)}
|
|
82
|
+
</View>
|
|
83
|
+
</Pressable>
|
|
84
|
+
</View>);
|
|
85
|
+
};
|
|
86
|
+
const styles = StyleSheet.create({
|
|
87
|
+
sectionContainer: {
|
|
88
|
+
marginBottom: 16,
|
|
89
|
+
borderRadius: 12,
|
|
90
|
+
overflow: 'hidden',
|
|
91
|
+
},
|
|
92
|
+
sectionTitle: {
|
|
93
|
+
fontSize: 18,
|
|
94
|
+
fontWeight: '600',
|
|
95
|
+
paddingHorizontal: 16,
|
|
96
|
+
paddingTop: 16,
|
|
97
|
+
paddingBottom: 8,
|
|
98
|
+
},
|
|
99
|
+
itemContainer: {
|
|
100
|
+
flexDirection: 'row',
|
|
101
|
+
alignItems: 'center',
|
|
102
|
+
paddingHorizontal: 16,
|
|
103
|
+
paddingVertical: 16,
|
|
104
|
+
minHeight: 72,
|
|
105
|
+
},
|
|
106
|
+
content: {
|
|
107
|
+
flex: 1,
|
|
108
|
+
flexDirection: 'row',
|
|
109
|
+
alignItems: 'center',
|
|
110
|
+
},
|
|
111
|
+
iconContainer: {
|
|
112
|
+
width: 48,
|
|
113
|
+
height: 48,
|
|
114
|
+
borderRadius: 12,
|
|
115
|
+
justifyContent: 'center',
|
|
116
|
+
alignItems: 'center',
|
|
117
|
+
marginRight: 16,
|
|
118
|
+
},
|
|
119
|
+
textContainer: {
|
|
120
|
+
flex: 1,
|
|
121
|
+
marginRight: 8,
|
|
122
|
+
},
|
|
123
|
+
title: {
|
|
124
|
+
fontSize: 16,
|
|
125
|
+
fontWeight: '500',
|
|
126
|
+
marginBottom: 4,
|
|
127
|
+
},
|
|
128
|
+
description: {
|
|
129
|
+
fontSize: 14,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=NotificationsSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationsSection.js","sourceRoot":"","sources":["../../../src/presentation/components/NotificationsSection.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-notifications",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "Offline-first local notifications system for React Native apps using expo-notifications",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -41,12 +41,14 @@
|
|
|
41
41
|
"zustand": "^5.0.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
+
"@expo/vector-icons": "^15.0.3",
|
|
44
45
|
"@react-native-async-storage/async-storage": "^1.21.0",
|
|
46
|
+
"@react-navigation/native": "^7.1.25",
|
|
45
47
|
"@types/react": "^18.2.45",
|
|
46
48
|
"@types/react-native": "^0.73.0",
|
|
47
49
|
"@umituz/react-native-design-system": "^1.5.28",
|
|
48
|
-
"@umituz/react-native-design-system-theme": "latest",
|
|
49
50
|
"@umituz/react-native-design-system-atoms": "*",
|
|
51
|
+
"@umituz/react-native-design-system-theme": "^1.8.0",
|
|
50
52
|
"expo-device": "~6.0.2",
|
|
51
53
|
"expo-notifications": "~0.28.0",
|
|
52
54
|
"react": "^18.2.0",
|
|
@@ -65,7 +67,6 @@
|
|
|
65
67
|
],
|
|
66
68
|
"dependencies": {
|
|
67
69
|
"@react-native-community/datetimepicker": "^8.5.0",
|
|
68
|
-
"@react-navigation/native": "^7.1.19",
|
|
69
70
|
"@umituz/react-native-design-system-atoms": "*",
|
|
70
71
|
"expo-linear-gradient": "^15.0.7",
|
|
71
72
|
"react-native-safe-area-context": "^5.6.2"
|
package/src/index.ts
CHANGED
|
@@ -37,5 +37,10 @@ export { useNotificationSettings } from './infrastructure/hooks/useNotificationS
|
|
|
37
37
|
// PRESENTATION LAYER EXPORTS
|
|
38
38
|
// ============================================================================
|
|
39
39
|
|
|
40
|
+
// Screens
|
|
40
41
|
// Screens
|
|
41
42
|
export { NotificationsScreen } from './presentation/screens/NotificationsScreen';
|
|
43
|
+
|
|
44
|
+
// Components
|
|
45
|
+
export { NotificationsSection } from './presentation/components/NotificationsSection';
|
|
46
|
+
export type { NotificationsSectionProps, NotificationsSectionConfig } from './presentation/components/NotificationsSection';
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { View, Text, Pressable, StyleSheet, Switch, ViewStyle } from 'react-native';
|
|
3
|
+
import { Feather } from '@expo/vector-icons';
|
|
4
|
+
import { useNavigation } from '@react-navigation/native';
|
|
5
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system-theme';
|
|
6
|
+
import { notificationService } from '../../infrastructure/services/NotificationService';
|
|
7
|
+
|
|
8
|
+
export interface NotificationsSectionConfig {
|
|
9
|
+
initialValue?: boolean;
|
|
10
|
+
onToggleChange?: (value: boolean) => void;
|
|
11
|
+
route?: string;
|
|
12
|
+
defaultRoute?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
showToggle?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface NotificationsSectionProps {
|
|
19
|
+
config?: NotificationsSectionConfig;
|
|
20
|
+
containerStyle?: ViewStyle;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const NotificationsSection: React.FC<NotificationsSectionProps> = ({
|
|
24
|
+
config,
|
|
25
|
+
containerStyle,
|
|
26
|
+
}) => {
|
|
27
|
+
const navigation = useNavigation();
|
|
28
|
+
const tokens = useAppDesignTokens();
|
|
29
|
+
const colors = tokens.colors;
|
|
30
|
+
|
|
31
|
+
const [notificationsEnabled, setNotificationsEnabled] = useState(
|
|
32
|
+
config?.initialValue ?? true,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (config?.initialValue !== undefined) {
|
|
37
|
+
setNotificationsEnabled(config.initialValue);
|
|
38
|
+
}
|
|
39
|
+
}, [config?.initialValue]);
|
|
40
|
+
|
|
41
|
+
const handleToggle = useCallback(async (value: boolean) => {
|
|
42
|
+
if (!value) {
|
|
43
|
+
// When turning ON (value is false -> true? Wait. onChange value is the NEW value)
|
|
44
|
+
// If value is true (turning on)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Logic from settings:
|
|
48
|
+
/*
|
|
49
|
+
if (notificationService && !value) { // Wait, logic in settings was: if (!value) ...?
|
|
50
|
+
// Actually typically we request permissions when enabling.
|
|
51
|
+
// Settings code:
|
|
52
|
+
// if (notificationService && !value) { ... } -> This implies when value is FALSE?
|
|
53
|
+
// Ah, maybe the switch value logic was inverted or I misread.
|
|
54
|
+
// Let's re-read settings code.
|
|
55
|
+
}
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
if (value) { // Enabling
|
|
59
|
+
const hasPermissions = await notificationService.hasPermissions();
|
|
60
|
+
if (!hasPermissions) {
|
|
61
|
+
const granted = await notificationService.requestPermissions();
|
|
62
|
+
if (!granted) {
|
|
63
|
+
// Permission denied, maybe don't enable switch?
|
|
64
|
+
// For now just allow toggle and let app handle it or sync with state
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setNotificationsEnabled(value);
|
|
70
|
+
config?.onToggleChange?.(value);
|
|
71
|
+
}, [config]);
|
|
72
|
+
|
|
73
|
+
const handlePress = useCallback(async () => {
|
|
74
|
+
const hasPermissions = await notificationService.hasPermissions();
|
|
75
|
+
if (!hasPermissions) {
|
|
76
|
+
await notificationService.requestPermissions();
|
|
77
|
+
}
|
|
78
|
+
navigation.navigate((config?.route || config?.defaultRoute || 'Notifications') as never);
|
|
79
|
+
}, [navigation, config?.route, config?.defaultRoute]);
|
|
80
|
+
|
|
81
|
+
const title = config?.title || 'Notifications';
|
|
82
|
+
const description = config?.description || 'Manage notification preferences';
|
|
83
|
+
const showToggle = config?.showToggle ?? true;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<View style={[styles.sectionContainer, { backgroundColor: colors.surface }, containerStyle]}>
|
|
87
|
+
<Text style={[styles.sectionTitle, { color: colors.textPrimary }]}>General</Text>
|
|
88
|
+
|
|
89
|
+
<Pressable
|
|
90
|
+
style={({ pressed }) => [
|
|
91
|
+
styles.itemContainer,
|
|
92
|
+
{
|
|
93
|
+
backgroundColor: pressed && !showToggle ? `${colors.primary}08` : 'transparent',
|
|
94
|
+
},
|
|
95
|
+
]}
|
|
96
|
+
onPress={showToggle ? undefined : handlePress}
|
|
97
|
+
disabled={showToggle}
|
|
98
|
+
>
|
|
99
|
+
<View style={styles.content}>
|
|
100
|
+
<View
|
|
101
|
+
style={[
|
|
102
|
+
styles.iconContainer,
|
|
103
|
+
{ backgroundColor: `${colors.primary}15` },
|
|
104
|
+
]}
|
|
105
|
+
>
|
|
106
|
+
<Feather name="bell" size={24} color={colors.primary} />
|
|
107
|
+
</View>
|
|
108
|
+
<View style={styles.textContainer}>
|
|
109
|
+
<Text style={[styles.title, { color: colors.textPrimary }]}>{title}</Text>
|
|
110
|
+
{!showToggle && (
|
|
111
|
+
<Text style={[styles.description, { color: colors.textSecondary }]}>
|
|
112
|
+
{description}
|
|
113
|
+
</Text>
|
|
114
|
+
)}
|
|
115
|
+
</View>
|
|
116
|
+
|
|
117
|
+
{showToggle ? (
|
|
118
|
+
<Switch
|
|
119
|
+
value={notificationsEnabled}
|
|
120
|
+
onValueChange={handleToggle}
|
|
121
|
+
trackColor={{
|
|
122
|
+
false: `${colors.textSecondary}30`,
|
|
123
|
+
true: colors.primary,
|
|
124
|
+
}}
|
|
125
|
+
thumbColor={"#FFFFFF"}
|
|
126
|
+
ios_backgroundColor={`${colors.textSecondary}30`}
|
|
127
|
+
/>
|
|
128
|
+
) : (
|
|
129
|
+
<Feather name="chevron-right" size={20} color={colors.textSecondary} />
|
|
130
|
+
)}
|
|
131
|
+
</View>
|
|
132
|
+
</Pressable>
|
|
133
|
+
</View>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const styles = StyleSheet.create({
|
|
138
|
+
sectionContainer: {
|
|
139
|
+
marginBottom: 16,
|
|
140
|
+
borderRadius: 12,
|
|
141
|
+
overflow: 'hidden',
|
|
142
|
+
},
|
|
143
|
+
sectionTitle: {
|
|
144
|
+
fontSize: 18,
|
|
145
|
+
fontWeight: '600',
|
|
146
|
+
paddingHorizontal: 16,
|
|
147
|
+
paddingTop: 16,
|
|
148
|
+
paddingBottom: 8,
|
|
149
|
+
},
|
|
150
|
+
itemContainer: {
|
|
151
|
+
flexDirection: 'row',
|
|
152
|
+
alignItems: 'center',
|
|
153
|
+
paddingHorizontal: 16,
|
|
154
|
+
paddingVertical: 16,
|
|
155
|
+
minHeight: 72,
|
|
156
|
+
},
|
|
157
|
+
content: {
|
|
158
|
+
flex: 1,
|
|
159
|
+
flexDirection: 'row',
|
|
160
|
+
alignItems: 'center',
|
|
161
|
+
},
|
|
162
|
+
iconContainer: {
|
|
163
|
+
width: 48,
|
|
164
|
+
height: 48,
|
|
165
|
+
borderRadius: 12,
|
|
166
|
+
justifyContent: 'center',
|
|
167
|
+
alignItems: 'center',
|
|
168
|
+
marginRight: 16,
|
|
169
|
+
},
|
|
170
|
+
textContainer: {
|
|
171
|
+
flex: 1,
|
|
172
|
+
marginRight: 8,
|
|
173
|
+
},
|
|
174
|
+
title: {
|
|
175
|
+
fontSize: 16,
|
|
176
|
+
fontWeight: '500',
|
|
177
|
+
marginBottom: 4,
|
|
178
|
+
},
|
|
179
|
+
description: {
|
|
180
|
+
fontSize: 14,
|
|
181
|
+
},
|
|
182
|
+
});
|