@oxyhq/services 6.9.5 → 6.9.6

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 (229) hide show
  1. package/lib/commonjs/ui/components/BottomSheet.js +5 -3
  2. package/lib/commonjs/ui/components/BottomSheet.js.map +1 -1
  3. package/lib/commonjs/ui/components/FontLoader.js +1 -1
  4. package/lib/commonjs/ui/components/FontLoader.js.map +1 -1
  5. package/lib/commonjs/ui/components/Header.js.map +1 -1
  6. package/lib/commonjs/ui/components/HelperText.js +1 -1
  7. package/lib/commonjs/ui/components/HelperText.js.map +1 -1
  8. package/lib/commonjs/ui/components/Icon.js +6 -2
  9. package/lib/commonjs/ui/components/Icon.js.map +1 -1
  10. package/lib/commonjs/ui/components/OxyProvider.js +9 -1
  11. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  12. package/lib/commonjs/ui/components/QuickActions.js.map +1 -1
  13. package/lib/commonjs/ui/components/SignInModal.js +5 -4
  14. package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
  15. package/lib/commonjs/ui/components/TouchableRipple/Pressable.js.map +1 -1
  16. package/lib/commonjs/ui/components/fileManagement/AnimatedButton.js.map +1 -1
  17. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +1 -1
  18. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
  19. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +26 -20
  20. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  21. package/lib/commonjs/ui/components/payment/PaymentMethodStep.js +9 -5
  22. package/lib/commonjs/ui/components/payment/PaymentMethodStep.js.map +1 -1
  23. package/lib/commonjs/ui/components/payment/PaymentReviewStep.js +19 -13
  24. package/lib/commonjs/ui/components/payment/PaymentReviewStep.js.map +1 -1
  25. package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js +9 -5
  26. package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js.map +1 -1
  27. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +14 -10
  28. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  29. package/lib/commonjs/ui/hooks/useAssets.js +4 -4
  30. package/lib/commonjs/ui/hooks/useAssets.js.map +1 -1
  31. package/lib/commonjs/ui/hooks/useAsyncAction.js +2 -2
  32. package/lib/commonjs/ui/hooks/useAsyncAction.js.map +1 -1
  33. package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
  34. package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
  35. package/lib/commonjs/ui/hooks/useSettingToggle.js +2 -2
  36. package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
  37. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +3 -3
  38. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/AccountVerificationScreen.js +1 -1
  40. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/FeedbackScreen.js +2 -2
  42. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +2 -2
  44. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  45. package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js +22 -17
  46. package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
  47. package/lib/commonjs/ui/screens/OxyAuthScreen.js +2 -2
  48. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +6 -2
  50. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  51. package/lib/commonjs/ui/screens/UserLinksScreen.js +11 -3
  52. package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -1
  53. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +1 -1
  54. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
  56. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  57. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +1 -1
  58. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  59. package/lib/commonjs/ui/stores/followStore.js +2 -2
  60. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  61. package/lib/commonjs/ui/utils/errorHandlers.js +2 -2
  62. package/lib/commonjs/ui/utils/errorHandlers.js.map +1 -1
  63. package/lib/commonjs/ui/utils/fileManagement.js +8 -3
  64. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
  65. package/lib/commonjs/ui/utils/sessionHelpers.js +3 -2
  66. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
  67. package/lib/commonjs/ui/utils/storageHelpers.js +1 -1
  68. package/lib/commonjs/ui/utils/storageHelpers.js.map +1 -1
  69. package/lib/commonjs/utils/hookUtils.js +4 -0
  70. package/lib/commonjs/utils/hookUtils.js.map +1 -1
  71. package/lib/module/ui/components/BottomSheet.js +5 -3
  72. package/lib/module/ui/components/BottomSheet.js.map +1 -1
  73. package/lib/module/ui/components/FontLoader.js +1 -1
  74. package/lib/module/ui/components/FontLoader.js.map +1 -1
  75. package/lib/module/ui/components/Header.js.map +1 -1
  76. package/lib/module/ui/components/HelperText.js +1 -1
  77. package/lib/module/ui/components/HelperText.js.map +1 -1
  78. package/lib/module/ui/components/Icon.js +6 -2
  79. package/lib/module/ui/components/Icon.js.map +1 -1
  80. package/lib/module/ui/components/OxyProvider.js +9 -1
  81. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  82. package/lib/module/ui/components/QuickActions.js.map +1 -1
  83. package/lib/module/ui/components/SignInModal.js +5 -4
  84. package/lib/module/ui/components/SignInModal.js.map +1 -1
  85. package/lib/module/ui/components/TouchableRipple/Pressable.js.map +1 -1
  86. package/lib/module/ui/components/fileManagement/AnimatedButton.js.map +1 -1
  87. package/lib/module/ui/components/modals/DeleteAccountModal.js +1 -1
  88. package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
  89. package/lib/module/ui/components/payment/PaymentDetailsStep.js +26 -20
  90. package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  91. package/lib/module/ui/components/payment/PaymentMethodStep.js +9 -5
  92. package/lib/module/ui/components/payment/PaymentMethodStep.js.map +1 -1
  93. package/lib/module/ui/components/payment/PaymentReviewStep.js +19 -13
  94. package/lib/module/ui/components/payment/PaymentReviewStep.js.map +1 -1
  95. package/lib/module/ui/components/payment/PaymentSuccessStep.js +9 -5
  96. package/lib/module/ui/components/payment/PaymentSuccessStep.js.map +1 -1
  97. package/lib/module/ui/components/payment/PaymentSummaryStep.js +14 -10
  98. package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  99. package/lib/module/ui/hooks/useAssets.js +4 -4
  100. package/lib/module/ui/hooks/useAssets.js.map +1 -1
  101. package/lib/module/ui/hooks/useAsyncAction.js +2 -2
  102. package/lib/module/ui/hooks/useAsyncAction.js.map +1 -1
  103. package/lib/module/ui/hooks/useFollow.js.map +1 -1
  104. package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
  105. package/lib/module/ui/hooks/useSettingToggle.js +2 -2
  106. package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
  107. package/lib/module/ui/screens/AccountOverviewScreen.js +3 -3
  108. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  109. package/lib/module/ui/screens/AccountVerificationScreen.js +1 -1
  110. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
  111. package/lib/module/ui/screens/FeedbackScreen.js +2 -2
  112. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  113. package/lib/module/ui/screens/LanguageSelectorScreen.js +2 -2
  114. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  115. package/lib/module/ui/screens/LearnMoreUsernamesScreen.js +22 -17
  116. package/lib/module/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
  117. package/lib/module/ui/screens/OxyAuthScreen.js +2 -2
  118. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  119. package/lib/module/ui/screens/PaymentGatewayScreen.js +6 -2
  120. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  121. package/lib/module/ui/screens/UserLinksScreen.js +11 -3
  122. package/lib/module/ui/screens/UserLinksScreen.js.map +1 -1
  123. package/lib/module/ui/screens/WelcomeNewUserScreen.js +1 -1
  124. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  125. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
  126. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  127. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +1 -1
  128. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  129. package/lib/module/ui/stores/followStore.js +2 -2
  130. package/lib/module/ui/stores/followStore.js.map +1 -1
  131. package/lib/module/ui/utils/errorHandlers.js +2 -2
  132. package/lib/module/ui/utils/errorHandlers.js.map +1 -1
  133. package/lib/module/ui/utils/fileManagement.js +8 -3
  134. package/lib/module/ui/utils/fileManagement.js.map +1 -1
  135. package/lib/module/ui/utils/sessionHelpers.js +3 -2
  136. package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
  137. package/lib/module/ui/utils/storageHelpers.js +1 -1
  138. package/lib/module/ui/utils/storageHelpers.js.map +1 -1
  139. package/lib/module/utils/hookUtils.js +4 -0
  140. package/lib/module/utils/hookUtils.js.map +1 -1
  141. package/lib/typescript/commonjs/ui/components/BottomSheet.d.ts.map +1 -1
  142. package/lib/typescript/commonjs/ui/components/Icon.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  144. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/ui/components/TextField/Adornment/types.d.ts +1 -1
  146. package/lib/typescript/commonjs/ui/components/TextField/Adornment/types.d.ts.map +1 -1
  147. package/lib/typescript/commonjs/ui/components/TouchableRipple/Pressable.d.ts.map +1 -1
  148. package/lib/typescript/commonjs/ui/components/fileManagement/AnimatedButton.d.ts +1 -1
  149. package/lib/typescript/commonjs/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -1
  150. package/lib/typescript/commonjs/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
  151. package/lib/typescript/commonjs/ui/components/payment/PaymentMethodStep.d.ts.map +1 -1
  152. package/lib/typescript/commonjs/ui/components/payment/PaymentReviewStep.d.ts.map +1 -1
  153. package/lib/typescript/commonjs/ui/components/payment/PaymentSuccessStep.d.ts.map +1 -1
  154. package/lib/typescript/commonjs/ui/components/payment/PaymentSummaryStep.d.ts.map +1 -1
  155. package/lib/typescript/commonjs/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
  156. package/lib/typescript/commonjs/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  157. package/lib/typescript/commonjs/ui/screens/UserLinksScreen.d.ts.map +1 -1
  158. package/lib/typescript/commonjs/ui/types/navigation.d.ts +2 -0
  159. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  160. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +1 -1
  161. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
  162. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +2 -1
  163. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
  164. package/lib/typescript/commonjs/utils/hookUtils.d.ts.map +1 -1
  165. package/lib/typescript/module/ui/components/BottomSheet.d.ts.map +1 -1
  166. package/lib/typescript/module/ui/components/Icon.d.ts.map +1 -1
  167. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  168. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
  169. package/lib/typescript/module/ui/components/TextField/Adornment/types.d.ts +1 -1
  170. package/lib/typescript/module/ui/components/TextField/Adornment/types.d.ts.map +1 -1
  171. package/lib/typescript/module/ui/components/TouchableRipple/Pressable.d.ts.map +1 -1
  172. package/lib/typescript/module/ui/components/fileManagement/AnimatedButton.d.ts +1 -1
  173. package/lib/typescript/module/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -1
  174. package/lib/typescript/module/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
  175. package/lib/typescript/module/ui/components/payment/PaymentMethodStep.d.ts.map +1 -1
  176. package/lib/typescript/module/ui/components/payment/PaymentReviewStep.d.ts.map +1 -1
  177. package/lib/typescript/module/ui/components/payment/PaymentSuccessStep.d.ts.map +1 -1
  178. package/lib/typescript/module/ui/components/payment/PaymentSummaryStep.d.ts.map +1 -1
  179. package/lib/typescript/module/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
  180. package/lib/typescript/module/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  181. package/lib/typescript/module/ui/screens/UserLinksScreen.d.ts.map +1 -1
  182. package/lib/typescript/module/ui/types/navigation.d.ts +2 -0
  183. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  184. package/lib/typescript/module/ui/utils/fileManagement.d.ts +1 -1
  185. package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
  186. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +2 -1
  187. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
  188. package/lib/typescript/module/utils/hookUtils.d.ts.map +1 -1
  189. package/package.json +1 -1
  190. package/src/ui/components/BottomSheet.tsx +4 -3
  191. package/src/ui/components/FontLoader.tsx +1 -1
  192. package/src/ui/components/Header.tsx +2 -2
  193. package/src/ui/components/HelperText.tsx +1 -1
  194. package/src/ui/components/Icon.tsx +2 -0
  195. package/src/ui/components/OxyProvider.tsx +6 -0
  196. package/src/ui/components/QuickActions.tsx +1 -1
  197. package/src/ui/components/SignInModal.tsx +6 -5
  198. package/src/ui/components/TextField/Adornment/types.tsx +1 -1
  199. package/src/ui/components/TouchableRipple/Pressable.tsx +1 -0
  200. package/src/ui/components/fileManagement/AnimatedButton.tsx +2 -2
  201. package/src/ui/components/modals/DeleteAccountModal.tsx +2 -2
  202. package/src/ui/components/payment/PaymentDetailsStep.tsx +24 -22
  203. package/src/ui/components/payment/PaymentMethodStep.tsx +7 -5
  204. package/src/ui/components/payment/PaymentReviewStep.tsx +15 -13
  205. package/src/ui/components/payment/PaymentSuccessStep.tsx +7 -5
  206. package/src/ui/components/payment/PaymentSummaryStep.tsx +12 -10
  207. package/src/ui/hooks/useAssets.ts +8 -8
  208. package/src/ui/hooks/useAsyncAction.ts +4 -4
  209. package/src/ui/hooks/useFollow.ts +1 -1
  210. package/src/ui/hooks/useProfileEditing.ts +1 -1
  211. package/src/ui/hooks/useSettingToggle.ts +4 -4
  212. package/src/ui/screens/AccountOverviewScreen.tsx +6 -6
  213. package/src/ui/screens/AccountVerificationScreen.tsx +2 -2
  214. package/src/ui/screens/FeedbackScreen.tsx +3 -3
  215. package/src/ui/screens/LanguageSelectorScreen.tsx +3 -3
  216. package/src/ui/screens/LearnMoreUsernamesScreen.tsx +22 -19
  217. package/src/ui/screens/OxyAuthScreen.tsx +3 -3
  218. package/src/ui/screens/PaymentGatewayScreen.tsx +4 -2
  219. package/src/ui/screens/UserLinksScreen.tsx +5 -3
  220. package/src/ui/screens/WelcomeNewUserScreen.tsx +2 -2
  221. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +1 -1
  222. package/src/ui/screens/karma/KarmaRulesScreen.tsx +1 -1
  223. package/src/ui/stores/followStore.ts +4 -4
  224. package/src/ui/types/navigation.ts +2 -0
  225. package/src/ui/utils/errorHandlers.ts +2 -2
  226. package/src/ui/utils/fileManagement.ts +13 -8
  227. package/src/ui/utils/sessionHelpers.ts +3 -2
  228. package/src/ui/utils/storageHelpers.ts +1 -1
  229. package/src/utils/hookUtils.ts +4 -2
@@ -58,11 +58,11 @@ const LanguageSelectorScreen: React.FC<LanguageSelectorScreenProps> = ({
58
58
  if (isAuthenticated && user?.id) {
59
59
  try {
60
60
  await oxyServices.updateProfile({ language: languageId });
61
- } catch (e: any) {
61
+ } catch (e: unknown) {
62
62
  // Server sync failed, but we'll save locally anyway
63
63
  serverSyncFailed = true;
64
64
  if (__DEV__) {
65
- console.warn('Failed to sync language to server (will save locally only):', e?.message || e);
65
+ console.warn('Failed to sync language to server (will save locally only):', e instanceof Error ? e.message : e);
66
66
  }
67
67
  }
68
68
  }
@@ -89,7 +89,7 @@ const LanguageSelectorScreen: React.FC<LanguageSelectorScreenProps> = ({
89
89
  if (__DEV__) {
90
90
  console.error('Error saving language preference:', error);
91
91
  }
92
- toast.error('Failed to save language preference');
92
+ toast.error(t('language.saveFailed'));
93
93
  setIsLoading(false);
94
94
  }
95
95
  }, [currentLanguage, isLoading, isAuthenticated, user?.id, oxyServices, setLanguage, t, onClose, goBack]);
@@ -10,43 +10,44 @@ import {
10
10
  import { Ionicons } from '@expo/vector-icons';
11
11
  import type { BaseScreenProps } from '../types/navigation';
12
12
  import { useThemeStyles } from '../hooks/useThemeStyles';
13
+ import { useI18n } from '../hooks/useI18n';
13
14
 
14
15
  interface InfoSection {
15
16
  id: string;
16
- title: string;
17
- content: string;
17
+ titleKey: string;
18
+ contentKey: string;
18
19
  icon: string;
19
20
  }
20
21
 
21
22
  const INFO_SECTIONS: InfoSection[] = [
22
23
  {
23
24
  id: 'what',
24
- title: 'What is a username?',
25
- content: 'Your username is your unique identifier on Oxy. It\'s how other people find and mention you. Think of it like your handle on social media - it\'s public and represents your identity across all Oxy apps.',
25
+ titleKey: 'learnMoreUsernames.sections.what.title',
26
+ contentKey: 'learnMoreUsernames.sections.what.content',
26
27
  icon: 'at-outline',
27
28
  },
28
29
  {
29
30
  id: 'rules',
30
- title: 'Username rules',
31
- content: 'Usernames can only contain lowercase letters (a-z) and numbers (0-9). They must be at least 4 characters long. Special characters, spaces, and uppercase letters are not allowed to keep usernames simple and easy to remember.',
31
+ titleKey: 'learnMoreUsernames.sections.rules.title',
32
+ contentKey: 'learnMoreUsernames.sections.rules.content',
32
33
  icon: 'list-outline',
33
34
  },
34
35
  {
35
36
  id: 'unique',
36
- title: 'Why must it be unique?',
37
- content: 'Each username can only belong to one person. This ensures that when someone searches for you or mentions you, they find the right person. It also prevents confusion and impersonation.',
37
+ titleKey: 'learnMoreUsernames.sections.unique.title',
38
+ contentKey: 'learnMoreUsernames.sections.unique.content',
38
39
  icon: 'finger-print-outline',
39
40
  },
40
41
  {
41
42
  id: 'change',
42
- title: 'Can I change it later?',
43
- content: 'Yes! You can change your username anytime in your account settings. Keep in mind that your old username will become available for others to use, and people who knew your old username will need to find you with the new one.',
43
+ titleKey: 'learnMoreUsernames.sections.change.title',
44
+ contentKey: 'learnMoreUsernames.sections.change.content',
44
45
  icon: 'refresh-outline',
45
46
  },
46
47
  {
47
48
  id: 'tips',
48
- title: 'Tips for choosing a username',
49
- content: 'Choose something memorable and easy to spell. Avoid using personal information like your birth year or phone number. Consider using a name that represents you across all contexts - professional and personal.',
49
+ titleKey: 'learnMoreUsernames.sections.tips.title',
50
+ contentKey: 'learnMoreUsernames.sections.tips.content',
50
51
  icon: 'bulb-outline',
51
52
  },
52
53
  ];
@@ -55,6 +56,7 @@ const LearnMoreUsernamesScreen: React.FC<BaseScreenProps> = ({
55
56
  theme,
56
57
  }) => {
57
58
  const themeStyles = useThemeStyles(theme || 'light');
59
+ const { t } = useI18n();
58
60
  const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set(['what'])); // Start with first section expanded
59
61
 
60
62
  const toggleExpanded = useCallback((id: string) => {
@@ -83,15 +85,16 @@ const LearnMoreUsernamesScreen: React.FC<BaseScreenProps> = ({
83
85
  <Ionicons name="at" size={32} color={themeStyles.primaryColor} />
84
86
  </View>
85
87
  <Text style={[styles.introTitle, { color: themeStyles.textColor }]}>
86
- Your unique identity
88
+ {t('learnMoreUsernames.introTitle')}
87
89
  </Text>
88
90
  <Text style={[styles.introText, { color: themeStyles.mutedTextColor }]}>
89
- Your username is how people find and recognize you across all Oxy apps.
91
+ {t('learnMoreUsernames.introText')}
90
92
  </Text>
91
93
  </View>
92
94
 
93
95
  {INFO_SECTIONS.map((section, index) => {
94
96
  const isExpanded = expandedIds.has(section.id);
97
+ const sectionTitle = t(section.titleKey);
95
98
  return (
96
99
  <View
97
100
  key={section.id}
@@ -105,8 +108,8 @@ const LearnMoreUsernamesScreen: React.FC<BaseScreenProps> = ({
105
108
  style={styles.sectionHeader}
106
109
  onPress={() => toggleExpanded(section.id)}
107
110
  accessibilityRole="button"
108
- accessibilityLabel={section.title}
109
- accessibilityHint={isExpanded ? 'Collapse section' : 'Expand section'}
111
+ accessibilityLabel={sectionTitle}
112
+ accessibilityHint={isExpanded ? t('learnMoreUsernames.collapseHint') : t('learnMoreUsernames.expandHint')}
110
113
  accessibilityState={{ expanded: isExpanded }}
111
114
  >
112
115
  <View style={[styles.sectionIconContainer, { backgroundColor: `${themeStyles.primaryColor}15` }]}>
@@ -117,7 +120,7 @@ const LearnMoreUsernamesScreen: React.FC<BaseScreenProps> = ({
117
120
  />
118
121
  </View>
119
122
  <Text style={[styles.sectionTitle, { color: themeStyles.textColor }]}>
120
- {section.title}
123
+ {sectionTitle}
121
124
  </Text>
122
125
  <Ionicons
123
126
  name={isExpanded ? 'chevron-up' : 'chevron-down'}
@@ -128,7 +131,7 @@ const LearnMoreUsernamesScreen: React.FC<BaseScreenProps> = ({
128
131
  {isExpanded && (
129
132
  <View style={[styles.sectionContent, { borderTopColor: themeStyles.borderColor }]}>
130
133
  <Text style={[styles.sectionText, { color: themeStyles.mutedTextColor }]}>
131
- {section.content}
134
+ {t(section.contentKey)}
132
135
  </Text>
133
136
  </View>
134
137
  )}
@@ -138,7 +141,7 @@ const LearnMoreUsernamesScreen: React.FC<BaseScreenProps> = ({
138
141
 
139
142
  <View style={styles.footer}>
140
143
  <Text style={[styles.footerText, { color: themeStyles.mutedTextColor }]}>
141
- Need more help? Visit our Help Center for additional information.
144
+ {t('learnMoreUsernames.footer')}
142
145
  </Text>
143
146
  </View>
144
147
  </ScrollView>
@@ -200,7 +200,7 @@ const OxyAuthScreen: React.FC<BaseScreenProps> = ({
200
200
  });
201
201
 
202
202
  socket.on('connect_error', (err) => {
203
- debug.log('Socket connection error, falling back to polling:', err.message);
203
+ debug.log('Socket connection error, falling back to polling:', (err instanceof Error ? err.message : null));
204
204
  // Fall back to polling if socket fails
205
205
  socket.disconnect();
206
206
  startPolling(sessionToken);
@@ -281,8 +281,8 @@ const OxyAuthScreen: React.FC<BaseScreenProps> = ({
281
281
 
282
282
  // Try socket first, will fall back to polling if needed
283
283
  connectSocket(sessionToken);
284
- } catch (err: any) {
285
- setError(err.message || 'Failed to create auth session');
284
+ } catch (err: unknown) {
285
+ setError((err instanceof Error ? err.message : null) || 'Failed to create auth session');
286
286
  } finally {
287
287
  setIsLoading(false);
288
288
  }
@@ -13,6 +13,7 @@ import { useThemeColors } from '../styles';
13
13
  import { normalizeTheme } from '../utils/themeUtils';
14
14
  import GroupedPillButtons from '../components/internal/GroupedPillButtons';
15
15
  import { useThemeStyles } from '../hooks/useThemeStyles';
16
+ import { useI18n } from '../hooks/useI18n';
16
17
  import QRCode from 'react-native-qrcode-svg';
17
18
 
18
19
  import {
@@ -80,6 +81,7 @@ const PaymentGatewayScreen: React.FC<PaymentGatewayScreenProps> = (props) => {
80
81
  const normalizedTheme = normalizeTheme(theme);
81
82
  const colors = useThemeColors(normalizedTheme);
82
83
  const themeStyles = useThemeStyles(normalizedTheme);
84
+ const { t } = useI18n();
83
85
  const styles = useMemo(() => createPaymentStyles(colors), [colors]);
84
86
 
85
87
  // Determine if the payment is for a recurring item (subscription)
@@ -183,11 +185,11 @@ const PaymentGatewayScreen: React.FC<PaymentGatewayScreenProps> = (props) => {
183
185
  if (!amount || Number.isNaN(Number(amount)) || Number(amount) <= 0) {
184
186
  return (
185
187
  <View style={styles.errorContainer}>
186
- <Text style={styles.errorText}>Invalid or missing payment amount.</Text>
188
+ <Text style={styles.errorText}>{t('payment.errors.invalidAmount')}</Text>
187
189
  <GroupedPillButtons
188
190
  buttons={[
189
191
  {
190
- text: 'Close',
192
+ text: t('payment.actions.close'),
191
193
  onPress: handleClose,
192
194
  icon: 'close',
193
195
  variant: 'primary',
@@ -5,6 +5,7 @@ import type { BaseScreenProps } from '../types/navigation';
5
5
  import { Header, GroupedSection } from '../components';
6
6
  import { useThemeStyles } from '../hooks/useThemeStyles';
7
7
  import { normalizeTheme } from '../utils/themeUtils';
8
+ import { useI18n } from '../hooks/useI18n';
8
9
 
9
10
  interface UserLinksScreenProps extends BaseScreenProps {
10
11
  userId: string;
@@ -28,6 +29,7 @@ const UserLinksScreen: React.FC<UserLinksScreenProps> = ({
28
29
  // primaryColor from hook (#007AFF) is already correct for this screen
29
30
  const normalizedTheme = normalizeTheme(theme);
30
31
  const themeStyles = useThemeStyles(normalizedTheme);
32
+ const { t } = useI18n();
31
33
 
32
34
  const handleLinkPress = async (url: string) => {
33
35
  try {
@@ -54,15 +56,15 @@ const UserLinksScreen: React.FC<UserLinksScreenProps> = ({
54
56
  return (
55
57
  <View style={[styles.container, { backgroundColor: themeStyles.backgroundColor }]}>
56
58
  <Header
57
- title="Links"
58
- subtitle={`${links.length} link${links.length !== 1 ? 's' : ''}`}
59
+ title={t('userLinks.title')}
60
+ subtitle={links.length !== 1 ? t('userLinks.linkCount_plural', { count: links.length }) : t('userLinks.linkCount', { count: links.length })}
59
61
  onBack={goBack}
60
62
  elevation="subtle"
61
63
  />
62
64
 
63
65
  <ScrollView style={styles.content}>
64
66
  <View style={styles.section}>
65
- <Text style={[styles.sectionTitle, { color: themeStyles.textColor }]}>Links</Text>
67
+ <Text style={[styles.sectionTitle, { color: themeStyles.textColor }]}>{t('userLinks.title')}</Text>
66
68
 
67
69
  <GroupedSection
68
70
  items={groupedItems}
@@ -164,8 +164,8 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
164
164
  if (avatarStepIndex >= 0 && currentStep !== avatarStepIndex) {
165
165
  animateToStepCallback(avatarStepIndex);
166
166
  }
167
- } catch (e: any) {
168
- toast.error(e.message || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
167
+ } catch (e: unknown) {
168
+ toast.error((e instanceof Error ? e.message : null) || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
169
169
  }
170
170
  }
171
171
  });
@@ -29,7 +29,7 @@ const KarmaLeaderboardScreen: React.FC<BaseScreenProps> = ({ goBack, theme, navi
29
29
  setError(null);
30
30
  oxyServices.getKarmaLeaderboard()
31
31
  .then((data: any) => setLeaderboard(Array.isArray(data) ? data : []))
32
- .catch((err: any) => setError(err.message || 'Failed to load leaderboard'))
32
+ .catch((err: unknown) => setError((err instanceof Error ? err.message : null) || 'Failed to load leaderboard'))
33
33
  .finally(() => setIsLoading(false));
34
34
  }, [oxyServices]);
35
35
 
@@ -28,7 +28,7 @@ const KarmaRulesScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
28
28
  setError(null);
29
29
  oxyServices.getKarmaRules()
30
30
  .then((data: any) => setRules(Array.isArray(data) ? data : []))
31
- .catch((err: any) => setError(err.message || 'Failed to load rules'))
31
+ .catch((err: unknown) => setError((err instanceof Error ? err.message : null) || 'Failed to load rules'))
32
32
  .finally(() => setIsLoading(false));
33
33
  }, [oxyServices]);
34
34
 
@@ -59,10 +59,10 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
59
59
  fetchingUsers: { ...state.fetchingUsers, [userId]: false },
60
60
  errors: { ...state.errors, [userId]: null },
61
61
  }));
62
- } catch (error: any) {
62
+ } catch (error: unknown) {
63
63
  set((state: FollowState) => ({
64
64
  fetchingUsers: { ...state.fetchingUsers, [userId]: false },
65
- errors: { ...state.errors, [userId]: error?.message || 'Failed to fetch follow status' },
65
+ errors: { ...state.errors, [userId]: (error instanceof Error ? error.message : null) || 'Failed to fetch follow status' },
66
66
  }));
67
67
  }
68
68
  },
@@ -119,10 +119,10 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
119
119
  return updates;
120
120
  });
121
121
  }
122
- } catch (error: any) {
122
+ } catch (error: unknown) {
123
123
  set((state: FollowState) => ({
124
124
  loadingUsers: { ...state.loadingUsers, [userId]: false },
125
- errors: { ...state.errors, [userId]: error?.message || 'Failed to update follow status' },
125
+ errors: { ...state.errors, [userId]: (error instanceof Error ? error.message : null) || 'Failed to update follow status' },
126
126
  }));
127
127
  }
128
128
  },
@@ -56,6 +56,8 @@ export interface OxyProviderProps {
56
56
  authWebUrl?: string;
57
57
  authRedirectUri?: string;
58
58
  queryClient?: QueryClient;
59
+ /** Skip wrapping in SafeAreaProvider/GestureHandlerRootView (use when parent already provides them) */
60
+ skipProviderWrappers?: boolean;
59
61
  }
60
62
 
61
63
 
@@ -54,7 +54,7 @@ export const isInvalidSessionError = (error: unknown): boolean => {
54
54
  }
55
55
 
56
56
  // Check error.status directly (HttpService sets this)
57
- if ((error as any).status === 401) {
57
+ if ('status' in (error as object) && (error as Record<string, unknown>).status === 401) {
58
58
  return true;
59
59
  }
60
60
 
@@ -83,7 +83,7 @@ export const isTimeoutOrNetworkError = (error: unknown): boolean => {
83
83
  }
84
84
 
85
85
  const message = extractErrorMessage(error, '').toLowerCase();
86
- const errorCode = (error as any).code;
86
+ const errorCode = isObject(error) && 'code' in (error as object) ? (error as Record<string, unknown>).code : undefined;
87
87
 
88
88
  // Check for timeout/cancelled messages
89
89
  if (
@@ -82,6 +82,7 @@ export async function convertDocumentPickerAssetToFile(
82
82
  }
83
83
  // Preserve URI for preview if available (useful for mobile previews)
84
84
  if (doc.uri) {
85
+ // biome-ignore lint/suspicious/noExplicitAny: attaching uri for mobile preview on a File object requires dynamic property assignment
85
86
  (file as any).uri = doc.uri;
86
87
  }
87
88
  return file;
@@ -102,6 +103,7 @@ export async function convertDocumentPickerAssetToFile(
102
103
  const fileType = doc.mimeType || blob.type || 'application/octet-stream';
103
104
  file = new globalThis.File([blob], fileName, { type: fileType });
104
105
  // Preserve URI for preview
106
+ // biome-ignore lint/suspicious/noExplicitAny: attaching uri for mobile preview on a File object requires dynamic property assignment
105
107
  (file as any).uri = doc.uri;
106
108
  return file;
107
109
  }
@@ -110,7 +112,7 @@ export async function convertDocumentPickerAssetToFile(
110
112
  // React Native's Blob doesn't support Uint8Array directly, so we use fetch
111
113
  const fileName = doc.name || `file-${index + 1}`;
112
114
  const fileType = doc.mimeType || 'application/octet-stream';
113
-
115
+
114
116
  // Use fetch to get the file as a blob (works with file:// and content:// URIs in React Native)
115
117
  const response = await fetch(doc.uri);
116
118
  if (!response.ok) {
@@ -119,17 +121,18 @@ export async function convertDocumentPickerAssetToFile(
119
121
  const blob = await response.blob();
120
122
  file = new globalThis.File([blob], fileName, { type: fileType });
121
123
  // Preserve URI for preview (especially important for mobile)
124
+ // biome-ignore lint/suspicious/noExplicitAny: attaching uri for mobile preview on a File object requires dynamic property assignment
122
125
  (file as any).uri = doc.uri;
123
126
  return file;
124
- } catch (error: any) {
127
+ } catch (error: unknown) {
125
128
  console.error('Failed to read file from URI:', error);
126
- throw new Error(`Failed to load file: ${error.message || 'Unknown error'}`);
129
+ throw new Error(`Failed to load file: ${(error instanceof Error ? error.message : null) || 'Unknown error'}`);
127
130
  }
128
131
  }
129
132
 
130
133
  // No file or URI available - this shouldn't happen with Expo 54
131
134
  throw new Error('Missing file data (no file or uri property)');
132
- } catch (error: any) {
135
+ } catch (error: unknown) {
133
136
  console.error('Error converting document to file:', error);
134
137
  throw error;
135
138
  }
@@ -185,6 +188,7 @@ export function getSafeDownloadUrl(
185
188
  export async function uploadFileRaw(
186
189
  file: File | Blob,
187
190
  userId: string,
191
+ // biome-ignore lint/suspicious/noExplicitAny: OxyServices type cannot be fully resolved due to mixin composition pattern
188
192
  oxyServices: any,
189
193
  visibility?: 'private' | 'public' | 'unlisted'
190
194
  ) {
@@ -198,10 +202,11 @@ export interface AvatarPickerConfig {
198
202
  /** Navigation function from BaseScreenProps */
199
203
  navigate?: (screen: RouteName, props?: Record<string, unknown>) => void;
200
204
  /** OxyServices instance */
205
+ // biome-ignore lint/suspicious/noExplicitAny: OxyServices type cannot be fully resolved due to mixin composition pattern
201
206
  oxyServices: any;
202
207
  /** TanStack Query mutation for updating profile */
203
208
  updateProfileMutation: {
204
- mutateAsync: (updates: { avatar: string }) => Promise<any>;
209
+ mutateAsync: (updates: { avatar: string }) => Promise<unknown>;
205
210
  };
206
211
  /** Callback to update local avatar state */
207
212
  onAvatarSelected?: (fileId: string) => void;
@@ -257,7 +262,7 @@ export function createAvatarPickerHandler(config: AvatarPickerConfig): () => voi
257
262
  multiSelect: false,
258
263
  disabledMimeTypes: ['video/', 'audio/', 'application/pdf'],
259
264
  afterSelect: 'none', // Don't navigate away - stay on current screen
260
- onSelect: async (file: any) => {
265
+ onSelect: async (file: FileMetadata) => {
261
266
  if (!file.contentType.startsWith('image/')) {
262
267
  toast.error(t('editProfile.toasts.selectImage') || 'Please select an image file');
263
268
  return;
@@ -276,8 +281,8 @@ export function createAvatarPickerHandler(config: AvatarPickerConfig): () => voi
276
281
  await updateProfileMutation.mutateAsync({ avatar: file.id });
277
282
 
278
283
  toast.success(t('editProfile.toasts.avatarUpdated') || 'Avatar updated');
279
- } catch (e: any) {
280
- toast.error(e.message || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
284
+ } catch (e: unknown) {
285
+ toast.error((e instanceof Error ? e.message : null) || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
281
286
  }
282
287
  }
283
288
  });
@@ -13,10 +13,11 @@ interface DeviceSession {
13
13
 
14
14
  /**
15
15
  * Service type for session helpers.
16
- * Uses 'any' to work around TypeScript mixin composition type inference issues.
16
+ * Uses 'unknown' to work around TypeScript mixin composition type inference issues.
17
17
  * The OxyServices class has these methods but TypeScript can't see them due to the mixin pattern.
18
+ * Methods are accessed dynamically, so callers must pass a properly typed instance.
18
19
  */
19
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ // biome-ignore lint/suspicious/noExplicitAny: OxyServices methods are accessed dynamically due to mixin composition; TypeScript cannot infer the full type
20
21
  type OxyServicesAny = any;
21
22
 
22
23
  export interface FetchSessionsWithFallbackOptions {
@@ -19,7 +19,7 @@ const MEMORY_STORAGE = (): StorageInterface => {
19
19
 
20
20
  return {
21
21
  async getItem(key: string) {
22
- return store.has(key) ? store.get(key)! : null;
22
+ return store.get(key) ?? null;
23
23
  },
24
24
  async setItem(key: string, value: string) {
25
25
  store.set(key, value);
@@ -21,10 +21,11 @@ export function useAsync<T>(
21
21
  const [loading, setLoading] = useState(false);
22
22
  const [error, setError] = useState<Error | null>(null);
23
23
 
24
+ // biome-ignore lint/correctness/useExhaustiveDependencies: asyncFn is intentionally excluded — this is a mount-only pattern where the callback identity should not cause re-creation
24
25
  const execute = useCallback(async () => {
25
26
  setLoading(true);
26
27
  setError(null);
27
-
28
+
28
29
  try {
29
30
  const result = await asyncFn();
30
31
  setData(result);
@@ -52,9 +53,10 @@ export function useAsyncEffect<T>(
52
53
  const [loading, setLoading] = useState(true);
53
54
  const [error, setError] = useState<Error | null>(null);
54
55
 
56
+ // biome-ignore lint/correctness/useExhaustiveDependencies: asyncFn is intentionally excluded — this is a mount-only pattern where the callback identity should not trigger re-execution
55
57
  useEffect(() => {
56
58
  let mounted = true;
57
-
59
+
58
60
  const execute = async () => {
59
61
  try {
60
62
  const result = await asyncFn();