@oxyhq/services 5.21.5 → 5.21.7

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 (177) hide show
  1. package/lib/commonjs/crypto/keyManager.js +67 -22
  2. package/lib/commonjs/crypto/keyManager.js.map +1 -1
  3. package/lib/commonjs/index.js +66 -0
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/ui/components/BottomSheetRouter.js +100 -286
  6. package/lib/commonjs/ui/components/BottomSheetRouter.js.map +1 -1
  7. package/lib/commonjs/ui/components/GroupedItem.js +0 -3
  8. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  9. package/lib/commonjs/ui/components/OxyProvider.js +14 -19
  10. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  11. package/lib/commonjs/ui/components/fileManagement/AnimatedButton.js +57 -0
  12. package/lib/commonjs/ui/components/fileManagement/AnimatedButton.js.map +1 -0
  13. package/lib/commonjs/ui/components/profile/EditBioModal.js +24 -156
  14. package/lib/commonjs/ui/components/profile/EditBioModal.js.map +1 -1
  15. package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js +28 -178
  16. package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js.map +1 -1
  17. package/lib/commonjs/ui/components/profile/EditEmailModal.js +32 -159
  18. package/lib/commonjs/ui/components/profile/EditEmailModal.js.map +1 -1
  19. package/lib/commonjs/ui/components/profile/EditLocationModal.js +45 -227
  20. package/lib/commonjs/ui/components/profile/EditLocationModal.js.map +1 -1
  21. package/lib/commonjs/ui/components/profile/EditUsernameModal.js +30 -155
  22. package/lib/commonjs/ui/components/profile/EditUsernameModal.js.map +1 -1
  23. package/lib/commonjs/ui/hooks/mutations/mutationFactory.js +177 -0
  24. package/lib/commonjs/ui/hooks/mutations/mutationFactory.js.map +1 -0
  25. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +10 -123
  26. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  27. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +2 -32
  28. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  29. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +2 -31
  30. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  31. package/lib/commonjs/ui/hooks/useFileFiltering.js +76 -0
  32. package/lib/commonjs/ui/hooks/useFileFiltering.js.map +1 -0
  33. package/lib/commonjs/ui/navigation/bottomSheetManager.js +43 -145
  34. package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -1
  35. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +0 -2
  36. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/FileManagementScreen.js +2 -2
  38. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  39. package/lib/commonjs/ui/utils/authHelpers.js +164 -0
  40. package/lib/commonjs/ui/utils/authHelpers.js.map +1 -0
  41. package/lib/commonjs/ui/utils/avatarUtils.js +18 -61
  42. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  43. package/lib/module/crypto/keyManager.js +67 -22
  44. package/lib/module/crypto/keyManager.js.map +1 -1
  45. package/lib/module/index.js +6 -0
  46. package/lib/module/index.js.map +1 -1
  47. package/lib/module/ui/components/BottomSheetRouter.js +102 -284
  48. package/lib/module/ui/components/BottomSheetRouter.js.map +1 -1
  49. package/lib/module/ui/components/GroupedItem.js +0 -3
  50. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  51. package/lib/module/ui/components/OxyProvider.js +14 -19
  52. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  53. package/lib/module/ui/components/fileManagement/AnimatedButton.js +50 -0
  54. package/lib/module/ui/components/fileManagement/AnimatedButton.js.map +1 -0
  55. package/lib/module/ui/components/profile/EditBioModal.js +24 -156
  56. package/lib/module/ui/components/profile/EditBioModal.js.map +1 -1
  57. package/lib/module/ui/components/profile/EditDisplayNameModal.js +28 -178
  58. package/lib/module/ui/components/profile/EditDisplayNameModal.js.map +1 -1
  59. package/lib/module/ui/components/profile/EditEmailModal.js +32 -159
  60. package/lib/module/ui/components/profile/EditEmailModal.js.map +1 -1
  61. package/lib/module/ui/components/profile/EditLocationModal.js +45 -227
  62. package/lib/module/ui/components/profile/EditLocationModal.js.map +1 -1
  63. package/lib/module/ui/components/profile/EditUsernameModal.js +30 -155
  64. package/lib/module/ui/components/profile/EditUsernameModal.js.map +1 -1
  65. package/lib/module/ui/hooks/mutations/mutationFactory.js +173 -0
  66. package/lib/module/ui/hooks/mutations/mutationFactory.js.map +1 -0
  67. package/lib/module/ui/hooks/mutations/useAccountMutations.js +10 -122
  68. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  69. package/lib/module/ui/hooks/queries/useAccountQueries.js +2 -32
  70. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  71. package/lib/module/ui/hooks/queries/useServicesQueries.js +2 -31
  72. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  73. package/lib/module/ui/hooks/useFileFiltering.js +72 -0
  74. package/lib/module/ui/hooks/useFileFiltering.js.map +1 -0
  75. package/lib/module/ui/navigation/bottomSheetManager.js +37 -135
  76. package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -1
  77. package/lib/module/ui/screens/AccountSettingsScreen.js +0 -2
  78. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  79. package/lib/module/ui/screens/FileManagementScreen.js +2 -2
  80. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  81. package/lib/module/ui/utils/authHelpers.js +154 -0
  82. package/lib/module/ui/utils/authHelpers.js.map +1 -0
  83. package/lib/module/ui/utils/avatarUtils.js +18 -61
  84. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  85. package/lib/typescript/commonjs/crypto/keyManager.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/index.d.ts +6 -0
  87. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/ui/components/BottomSheetRouter.d.ts +2 -7
  89. package/lib/typescript/commonjs/ui/components/BottomSheetRouter.d.ts.map +1 -1
  90. package/lib/typescript/commonjs/ui/components/GroupedItem.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/ui/components/fileManagement/AnimatedButton.d.ts +16 -0
  93. package/lib/typescript/commonjs/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -0
  94. package/lib/typescript/commonjs/ui/components/profile/EditBioModal.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/ui/components/profile/EditDisplayNameModal.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/ui/components/profile/EditEmailModal.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/ui/components/profile/EditLocationModal.d.ts +1 -0
  98. package/lib/typescript/commonjs/ui/components/profile/EditLocationModal.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/ui/components/profile/EditUsernameModal.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/ui/hooks/mutations/mutationFactory.d.ts +76 -0
  101. package/lib/typescript/commonjs/ui/hooks/mutations/mutationFactory.d.ts.map +1 -0
  102. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +29 -4
  103. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +1 -1
  105. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts +1 -1
  107. package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/hooks/useFileFiltering.d.ts +29 -0
  109. package/lib/typescript/commonjs/ui/hooks/useFileFiltering.d.ts.map +1 -0
  110. package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts +11 -60
  111. package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/ui/utils/authHelpers.d.ts +99 -0
  114. package/lib/typescript/commonjs/ui/utils/authHelpers.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
  116. package/lib/typescript/module/crypto/keyManager.d.ts.map +1 -1
  117. package/lib/typescript/module/index.d.ts +6 -0
  118. package/lib/typescript/module/index.d.ts.map +1 -1
  119. package/lib/typescript/module/ui/components/BottomSheetRouter.d.ts +2 -7
  120. package/lib/typescript/module/ui/components/BottomSheetRouter.d.ts.map +1 -1
  121. package/lib/typescript/module/ui/components/GroupedItem.d.ts.map +1 -1
  122. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  123. package/lib/typescript/module/ui/components/fileManagement/AnimatedButton.d.ts +16 -0
  124. package/lib/typescript/module/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -0
  125. package/lib/typescript/module/ui/components/profile/EditBioModal.d.ts.map +1 -1
  126. package/lib/typescript/module/ui/components/profile/EditDisplayNameModal.d.ts.map +1 -1
  127. package/lib/typescript/module/ui/components/profile/EditEmailModal.d.ts.map +1 -1
  128. package/lib/typescript/module/ui/components/profile/EditLocationModal.d.ts +1 -0
  129. package/lib/typescript/module/ui/components/profile/EditLocationModal.d.ts.map +1 -1
  130. package/lib/typescript/module/ui/components/profile/EditUsernameModal.d.ts.map +1 -1
  131. package/lib/typescript/module/ui/hooks/mutations/mutationFactory.d.ts +76 -0
  132. package/lib/typescript/module/ui/hooks/mutations/mutationFactory.d.ts.map +1 -0
  133. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +29 -4
  134. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  135. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +1 -1
  136. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  137. package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts +1 -1
  138. package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  139. package/lib/typescript/module/ui/hooks/useFileFiltering.d.ts +29 -0
  140. package/lib/typescript/module/ui/hooks/useFileFiltering.d.ts.map +1 -0
  141. package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts +11 -60
  142. package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts.map +1 -1
  143. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  144. package/lib/typescript/module/ui/utils/authHelpers.d.ts +99 -0
  145. package/lib/typescript/module/ui/utils/authHelpers.d.ts.map +1 -0
  146. package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
  147. package/package.json +2 -2
  148. package/src/crypto/keyManager.ts +23 -22
  149. package/src/index.ts +25 -0
  150. package/src/ui/components/BottomSheetRouter.tsx +97 -319
  151. package/src/ui/components/GroupedItem.tsx +0 -4
  152. package/src/ui/components/OxyProvider.tsx +13 -18
  153. package/src/ui/components/fileManagement/AnimatedButton.tsx +56 -0
  154. package/src/ui/components/profile/EditBioModal.tsx +38 -176
  155. package/src/ui/components/profile/EditDisplayNameModal.tsx +48 -195
  156. package/src/ui/components/profile/EditEmailModal.tsx +49 -180
  157. package/src/ui/components/profile/EditLocationModal.tsx +76 -263
  158. package/src/ui/components/profile/EditUsernameModal.tsx +47 -175
  159. package/src/ui/hooks/mutations/mutationFactory.ts +215 -0
  160. package/src/ui/hooks/mutations/useAccountMutations.ts +48 -136
  161. package/src/ui/hooks/queries/useAccountQueries.ts +6 -33
  162. package/src/ui/hooks/queries/useServicesQueries.ts +6 -32
  163. package/src/ui/hooks/useFileFiltering.ts +115 -0
  164. package/src/ui/navigation/bottomSheetManager.ts +43 -150
  165. package/src/ui/screens/AccountSettingsScreen.tsx +0 -2
  166. package/src/ui/screens/FileManagementScreen.tsx +2 -2
  167. package/src/ui/utils/authHelpers.ts +183 -0
  168. package/src/ui/utils/avatarUtils.ts +25 -65
  169. package/lib/commonjs/ui/hooks/use-haptic-press.js +0 -21
  170. package/lib/commonjs/ui/hooks/use-haptic-press.js.map +0 -1
  171. package/lib/module/ui/hooks/use-haptic-press.js +0 -17
  172. package/lib/module/ui/hooks/use-haptic-press.js.map +0 -1
  173. package/lib/typescript/commonjs/ui/hooks/use-haptic-press.d.ts +0 -8
  174. package/lib/typescript/commonjs/ui/hooks/use-haptic-press.d.ts.map +0 -1
  175. package/lib/typescript/module/ui/hooks/use-haptic-press.d.ts +0 -8
  176. package/lib/typescript/module/ui/hooks/use-haptic-press.d.ts.map +0 -1
  177. package/src/ui/hooks/use-haptic-press.ts +0 -15
@@ -1,188 +1,57 @@
1
- import React, { useState, useEffect } from 'react';
2
- import {
3
- View,
4
- Text,
5
- TextInput,
6
- TouchableOpacity,
7
- StyleSheet,
8
- Modal,
9
- Platform,
10
- } from 'react-native';
11
- import { Ionicons } from '@expo/vector-icons';
12
- import { useThemeStyles } from '../../hooks/useThemeStyles';
13
- import { useColorScheme } from '../../hooks/use-color-scheme';
14
- import { useI18n } from '../../hooks/useI18n';
15
- import { fontFamilies } from '../../styles/fonts';
1
+ import React from 'react';
2
+ import { EditFieldModal } from './EditFieldModal';
16
3
  import { useProfileEditing } from '../../hooks/useProfileEditing';
4
+ import { useI18n } from '../../hooks/useI18n';
5
+ import { EMAIL_REGEX } from '../../../utils/validationUtils';
17
6
 
18
7
  interface EditEmailModalProps {
19
- visible: boolean;
20
- onClose: () => void;
21
- initialValue?: string;
22
- theme?: 'light' | 'dark';
23
- onSave?: () => void;
8
+ visible: boolean;
9
+ onClose: () => void;
10
+ initialValue?: string;
11
+ theme?: 'light' | 'dark';
12
+ onSave?: () => void;
24
13
  }
25
14
 
26
15
  export const EditEmailModal: React.FC<EditEmailModalProps> = ({
27
- visible,
28
- onClose,
29
- initialValue = '',
30
- theme = 'light',
31
- onSave,
16
+ visible,
17
+ onClose,
18
+ initialValue = '',
19
+ theme = 'light',
20
+ onSave,
32
21
  }) => {
33
- const { t } = useI18n();
34
- const colorScheme = useColorScheme();
35
- const themeStyles = useThemeStyles(theme || 'light', colorScheme);
36
- const colors = themeStyles.colors;
37
- const { updateField, isSaving } = useProfileEditing();
38
-
39
- const [email, setEmail] = useState(initialValue);
40
-
41
- useEffect(() => {
42
- if (visible) {
43
- setEmail(initialValue);
44
- }
45
- }, [visible, initialValue]);
46
-
47
- const handleSave = async () => {
48
- const success = await updateField('email', email);
49
- if (success) {
50
- onSave?.();
51
- onClose();
52
- }
53
- };
54
-
55
- const isValidEmail = (email: string) => {
56
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
57
- };
58
-
59
- return (
60
- <Modal
61
- visible={visible}
62
- animationType="slide"
63
- transparent={true}
64
- onRequestClose={onClose}
65
- >
66
- <View style={styles.modalOverlay}>
67
- <View style={[styles.modalContent, { backgroundColor: colors.background }]}>
68
- <View style={styles.modalHeader}>
69
- <TouchableOpacity onPress={onClose} style={styles.closeButton}>
70
- <Ionicons name="close" size={24} color={colors.text} />
71
- </TouchableOpacity>
72
- <Text style={[styles.modalTitle, { color: colors.text }]}>
73
- {t('editProfile.items.email.title') || 'Email'}
74
- </Text>
75
- <TouchableOpacity
76
- onPress={handleSave}
77
- disabled={isSaving || !isValidEmail(email)}
78
- style={[styles.saveButton, { opacity: (isSaving || !isValidEmail(email)) ? 0.5 : 1 }]}
79
- >
80
- <Text style={[styles.saveButtonText, { color: colors.tint }]}>
81
- {isSaving ? 'Saving...' : 'Save'}
82
- </Text>
83
- </TouchableOpacity>
84
- </View>
85
-
86
- <View style={styles.modalBody}>
87
- <View style={styles.inputGroup}>
88
- <Text style={[styles.label, { color: colors.text }]}>
89
- {t('editProfile.items.email.label') || 'Email Address'}
90
- </Text>
91
- <TextInput
92
- style={[
93
- styles.input,
94
- {
95
- backgroundColor: colors.card,
96
- color: colors.text,
97
- borderColor: colors.border,
98
- },
99
- ]}
100
- value={email}
101
- onChangeText={setEmail}
102
- placeholder={t('editProfile.items.email.placeholder') || 'Enter your email address'}
103
- placeholderTextColor={colors.secondaryText}
104
- autoFocus
105
- keyboardType="email-address"
106
- autoCapitalize="none"
107
- autoCorrect={false}
108
- selectionColor={colors.tint}
109
- />
110
- </View>
111
- </View>
112
- </View>
113
- </View>
114
- </Modal>
115
- );
22
+ const { t } = useI18n();
23
+ const { updateField } = useProfileEditing();
24
+
25
+ return (
26
+ <EditFieldModal
27
+ visible={visible}
28
+ onClose={onClose}
29
+ title={t('editProfile.items.email.title') || 'Email'}
30
+ theme={theme}
31
+ onSave={onSave}
32
+ variant="single"
33
+ fields={[
34
+ {
35
+ key: 'email',
36
+ label: t('editProfile.items.email.label') || 'Email Address',
37
+ initialValue,
38
+ placeholder: t('editProfile.items.email.placeholder') || 'Enter your email address',
39
+ validation: (value) => {
40
+ if (!EMAIL_REGEX.test(value)) {
41
+ return t('editProfile.items.email.invalid') || 'Please enter a valid email address';
42
+ }
43
+ return undefined;
44
+ },
45
+ inputProps: {
46
+ keyboardType: 'email-address',
47
+ autoCapitalize: 'none',
48
+ autoCorrect: false,
49
+ },
50
+ },
51
+ ]}
52
+ onSubmit={async (data) => {
53
+ return await updateField('email', data.email as string);
54
+ }}
55
+ />
56
+ );
116
57
  };
117
-
118
- const styles = StyleSheet.create({
119
- modalOverlay: {
120
- flex: 1,
121
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
122
- justifyContent: 'flex-end',
123
- },
124
- modalContent: {
125
- borderTopLeftRadius: 20,
126
- borderTopRightRadius: 20,
127
- paddingTop: Platform.OS === 'ios' ? 20 : 16,
128
- maxHeight: '80%',
129
- },
130
- modalHeader: {
131
- flexDirection: 'row',
132
- alignItems: 'center',
133
- justifyContent: 'space-between',
134
- paddingHorizontal: 16,
135
- paddingBottom: 16,
136
- borderBottomWidth: StyleSheet.hairlineWidth,
137
- borderBottomColor: '#E5E5EA',
138
- },
139
- closeButton: {
140
- width: 40,
141
- height: 40,
142
- alignItems: 'center',
143
- justifyContent: 'center',
144
- },
145
- modalTitle: {
146
- fontSize: 18,
147
- fontWeight: '600',
148
- fontFamily: fontFamilies.phuduSemiBold,
149
- flex: 1,
150
- textAlign: 'center',
151
- },
152
- saveButton: {
153
- paddingHorizontal: 16,
154
- paddingVertical: 8,
155
- },
156
- saveButtonText: {
157
- fontSize: 16,
158
- fontWeight: '600',
159
- fontFamily: fontFamilies.phuduSemiBold,
160
- },
161
- modalBody: {
162
- padding: 16,
163
- gap: 16,
164
- },
165
- inputGroup: {
166
- gap: 8,
167
- },
168
- label: {
169
- fontSize: 14,
170
- fontWeight: '600',
171
- fontFamily: fontFamilies.phuduSemiBold,
172
- },
173
- input: {
174
- borderWidth: StyleSheet.hairlineWidth,
175
- borderRadius: 12,
176
- padding: 16,
177
- fontSize: 16,
178
- minHeight: 52,
179
- },
180
- });
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
@@ -1,278 +1,91 @@
1
- import React, { useState, useEffect } from 'react';
2
- import {
3
- View,
4
- Text,
5
- TextInput,
6
- TouchableOpacity,
7
- StyleSheet,
8
- Modal,
9
- Platform,
10
- ScrollView,
11
- } from 'react-native';
1
+ import React from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
12
3
  import { Ionicons } from '@expo/vector-icons';
13
- import { useThemeStyles } from '../../hooks/useThemeStyles';
14
- import { useColorScheme } from '../../hooks/use-color-scheme';
15
- import { useI18n } from '../../hooks/useI18n';
16
- import { fontFamilies } from '../../styles/fonts';
4
+ import { EditFieldModal } from './EditFieldModal';
17
5
  import { useProfileEditing } from '../../hooks/useProfileEditing';
6
+ import { useI18n } from '../../hooks/useI18n';
18
7
 
19
8
  interface Location {
20
- id: string;
21
- name: string;
22
- label?: string;
23
- coordinates?: { lat: number; lon: number };
9
+ id: string;
10
+ name: string;
11
+ label?: string;
12
+ coordinates?: { lat: number; lon: number };
13
+ [key: string]: unknown;
24
14
  }
25
15
 
26
16
  interface EditLocationModalProps {
27
- visible: boolean;
28
- onClose: () => void;
29
- initialLocations?: Location[];
30
- theme?: 'light' | 'dark';
31
- onSave?: () => void;
17
+ visible: boolean;
18
+ onClose: () => void;
19
+ initialLocations?: Location[];
20
+ theme?: 'light' | 'dark';
21
+ onSave?: () => void;
32
22
  }
33
23
 
34
24
  export const EditLocationModal: React.FC<EditLocationModalProps> = ({
35
- visible,
36
- onClose,
37
- initialLocations = [],
38
- theme = 'light',
39
- onSave,
25
+ visible,
26
+ onClose,
27
+ initialLocations = [],
28
+ theme = 'light',
29
+ onSave,
40
30
  }) => {
41
- const { t } = useI18n();
42
- const colorScheme = useColorScheme();
43
- const themeStyles = useThemeStyles(theme || 'light', colorScheme);
44
- const colors = themeStyles.colors;
45
- const { saveProfile, isSaving } = useProfileEditing();
46
-
47
- const [locations, setLocations] = useState<Location[]>(initialLocations);
48
- const [newLocation, setNewLocation] = useState('');
49
-
50
- useEffect(() => {
51
- if (visible) {
52
- setLocations(initialLocations);
53
- setNewLocation('');
54
- }
55
- }, [visible, initialLocations]);
56
-
57
- const handleAddLocation = () => {
58
- if (!newLocation.trim()) return;
59
- const location: Location = {
60
- id: `location-${Date.now()}`,
61
- name: newLocation.trim(),
62
- };
63
- setLocations([...locations, location]);
64
- setNewLocation('');
65
- };
66
-
67
- const handleRemoveLocation = (id: string) => {
68
- setLocations(locations.filter(loc => loc.id !== id));
69
- };
70
-
71
- const handleSave = async () => {
72
- const success = await saveProfile({ locations });
73
- if (success) {
74
- onSave?.();
75
- onClose();
76
- }
77
- };
78
-
79
- return (
80
- <Modal
81
- visible={visible}
82
- animationType="slide"
83
- transparent={true}
84
- onRequestClose={onClose}
85
- >
86
- <View style={styles.modalOverlay}>
87
- <View style={[styles.modalContent, { backgroundColor: colors.background }]}>
88
- <View style={styles.modalHeader}>
89
- <TouchableOpacity onPress={onClose} style={styles.closeButton}>
90
- <Ionicons name="close" size={24} color={colors.text} />
91
- </TouchableOpacity>
92
- <Text style={[styles.modalTitle, { color: colors.text }]}>
93
- {t('editProfile.items.locations.title') || 'Locations'}
94
- </Text>
95
- <TouchableOpacity
96
- onPress={handleSave}
97
- disabled={isSaving}
98
- style={[styles.saveButton, { opacity: isSaving ? 0.5 : 1 }]}
99
- >
100
- <Text style={[styles.saveButtonText, { color: colors.tint }]}>
101
- {isSaving ? 'Saving...' : 'Save'}
102
- </Text>
103
- </TouchableOpacity>
104
- </View>
105
-
106
- <ScrollView style={styles.modalBody}>
107
- <View style={styles.inputGroup}>
108
- <Text style={[styles.label, { color: colors.text }]}>
109
- {t('editProfile.items.locations.add') || 'Add Location'}
110
- </Text>
111
- <View style={styles.addLocationRow}>
112
- <TextInput
113
- style={[
114
- styles.input,
115
- {
116
- backgroundColor: colors.card,
117
- color: colors.text,
118
- borderColor: colors.border,
119
- flex: 1,
120
- },
121
- ]}
122
- value={newLocation}
123
- onChangeText={setNewLocation}
124
- placeholder={t('editProfile.items.locations.placeholder') || 'Enter location name'}
125
- placeholderTextColor={colors.secondaryText}
126
- selectionColor={colors.tint}
127
- />
128
- <TouchableOpacity
129
- style={[styles.addButton, { backgroundColor: colors.tint }]}
130
- onPress={handleAddLocation}
131
- disabled={!newLocation.trim()}
132
- >
133
- <Ionicons name="add" size={20} color="#fff" />
134
- </TouchableOpacity>
135
- </View>
136
- </View>
137
-
138
- {locations.length > 0 && (
139
- <View style={styles.locationsList}>
140
- <Text style={[styles.listTitle, { color: colors.text }]}>
141
- {t('editProfile.items.locations.yourLocations') || 'Your Locations'} ({locations.length})
142
- </Text>
143
- {locations.map((location, index) => (
144
- <View
145
- key={location.id}
146
- style={[
147
- styles.locationItem,
148
- { backgroundColor: colors.card, borderColor: colors.border },
149
- index < locations.length - 1 && { borderBottomWidth: StyleSheet.hairlineWidth },
150
- ]}
151
- >
152
- <Text style={[styles.locationName, { color: colors.text }]}>
153
- {location.name}
154
- </Text>
155
- <TouchableOpacity
156
- onPress={() => handleRemoveLocation(location.id)}
157
- style={styles.removeButton}
158
- >
159
- <Ionicons name="trash-outline" size={18} color="#FF3B30" />
160
- </TouchableOpacity>
161
- </View>
162
- ))}
163
- </View>
164
- )}
165
- </ScrollView>
166
- </View>
167
- </View>
168
- </Modal>
169
- );
31
+ const { t } = useI18n();
32
+ const { saveProfile } = useProfileEditing();
33
+
34
+ return (
35
+ <EditFieldModal<Location>
36
+ visible={visible}
37
+ onClose={onClose}
38
+ title={t('editProfile.items.locations.title') || 'Locations'}
39
+ theme={theme}
40
+ onSave={onSave}
41
+ variant="list"
42
+ listConfig={{
43
+ items: initialLocations,
44
+ addItemLabel: t('editProfile.items.locations.add') || 'Add Location',
45
+ listTitle: t('editProfile.items.locations.yourLocations') || 'Your Locations',
46
+ addItemPlaceholder: t('editProfile.items.locations.placeholder') || 'Enter location name',
47
+ createItem: (value: string) => ({
48
+ id: `location-${Date.now()}`,
49
+ name: value.trim(),
50
+ }),
51
+ renderItem: (item: Location, onRemove: () => void, colors: Record<string, string>) => (
52
+ <View
53
+ style={[
54
+ styles.locationItem,
55
+ { backgroundColor: colors.card, borderColor: colors.border },
56
+ ]}
57
+ >
58
+ <Text style={[styles.locationName, { color: colors.text }]}>
59
+ {item.name}
60
+ </Text>
61
+ <TouchableOpacity onPress={onRemove} style={styles.removeButton}>
62
+ <Ionicons name="trash-outline" size={18} color="#FF3B30" />
63
+ </TouchableOpacity>
64
+ </View>
65
+ ),
66
+ }}
67
+ onSubmit={async (data) => {
68
+ return await saveProfile({ locations: data.items as Location[] });
69
+ }}
70
+ />
71
+ );
170
72
  };
171
73
 
172
74
  const styles = StyleSheet.create({
173
- modalOverlay: {
174
- flex: 1,
175
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
176
- justifyContent: 'flex-end',
177
- },
178
- modalContent: {
179
- borderTopLeftRadius: 20,
180
- borderTopRightRadius: 20,
181
- paddingTop: Platform.OS === 'ios' ? 20 : 16,
182
- maxHeight: '80%',
183
- },
184
- modalHeader: {
185
- flexDirection: 'row',
186
- alignItems: 'center',
187
- justifyContent: 'space-between',
188
- paddingHorizontal: 16,
189
- paddingBottom: 16,
190
- borderBottomWidth: StyleSheet.hairlineWidth,
191
- borderBottomColor: '#E5E5EA',
192
- },
193
- closeButton: {
194
- width: 40,
195
- height: 40,
196
- alignItems: 'center',
197
- justifyContent: 'center',
198
- },
199
- modalTitle: {
200
- fontSize: 18,
201
- fontWeight: '600',
202
- fontFamily: fontFamilies.phuduSemiBold,
203
- flex: 1,
204
- textAlign: 'center',
205
- },
206
- saveButton: {
207
- paddingHorizontal: 16,
208
- paddingVertical: 8,
209
- },
210
- saveButtonText: {
211
- fontSize: 16,
212
- fontWeight: '600',
213
- fontFamily: fontFamilies.phuduSemiBold,
214
- },
215
- modalBody: {
216
- padding: 16,
217
- },
218
- inputGroup: {
219
- gap: 8,
220
- marginBottom: 24,
221
- },
222
- label: {
223
- fontSize: 14,
224
- fontWeight: '600',
225
- fontFamily: fontFamilies.phuduSemiBold,
226
- },
227
- addLocationRow: {
228
- flexDirection: 'row',
229
- gap: 8,
230
- alignItems: 'center',
231
- },
232
- input: {
233
- borderWidth: StyleSheet.hairlineWidth,
234
- borderRadius: 12,
235
- padding: 16,
236
- fontSize: 16,
237
- minHeight: 52,
238
- },
239
- addButton: {
240
- width: 52,
241
- height: 52,
242
- borderRadius: 12,
243
- alignItems: 'center',
244
- justifyContent: 'center',
245
- },
246
- locationsList: {
247
- gap: 8,
248
- },
249
- listTitle: {
250
- fontSize: 16,
251
- fontWeight: '600',
252
- fontFamily: fontFamilies.phuduSemiBold,
253
- marginBottom: 8,
254
- },
255
- locationItem: {
256
- flexDirection: 'row',
257
- alignItems: 'center',
258
- justifyContent: 'space-between',
259
- padding: 16,
260
- borderRadius: 12,
261
- borderWidth: StyleSheet.hairlineWidth,
262
- },
263
- locationName: {
264
- fontSize: 16,
265
- flex: 1,
266
- },
267
- removeButton: {
268
- padding: 8,
269
- },
75
+ locationItem: {
76
+ flexDirection: 'row',
77
+ alignItems: 'center',
78
+ justifyContent: 'space-between',
79
+ padding: 16,
80
+ borderRadius: 12,
81
+ borderWidth: StyleSheet.hairlineWidth,
82
+ marginBottom: 8,
83
+ },
84
+ locationName: {
85
+ fontSize: 16,
86
+ flex: 1,
87
+ },
88
+ removeButton: {
89
+ padding: 8,
90
+ },
270
91
  });
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-