@oxyhq/services 5.13.12 → 5.13.16

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 (209) hide show
  1. package/README.md +10 -0
  2. package/lib/commonjs/core/OxyServices.base.js +271 -0
  3. package/lib/commonjs/core/OxyServices.base.js.map +1 -0
  4. package/lib/commonjs/core/OxyServices.errors.js +26 -0
  5. package/lib/commonjs/core/OxyServices.errors.js.map +1 -0
  6. package/lib/commonjs/core/OxyServices.js +58 -2009
  7. package/lib/commonjs/core/OxyServices.js.map +1 -1
  8. package/lib/commonjs/core/mixins/OxyServices.analytics.js +60 -0
  9. package/lib/commonjs/core/mixins/OxyServices.analytics.js.map +1 -0
  10. package/lib/commonjs/core/mixins/OxyServices.assets.js +406 -0
  11. package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -0
  12. package/lib/commonjs/core/mixins/OxyServices.auth.js +303 -0
  13. package/lib/commonjs/core/mixins/OxyServices.auth.js.map +1 -0
  14. package/lib/commonjs/core/mixins/OxyServices.developer.js +115 -0
  15. package/lib/commonjs/core/mixins/OxyServices.developer.js.map +1 -0
  16. package/lib/commonjs/core/mixins/OxyServices.devices.js +119 -0
  17. package/lib/commonjs/core/mixins/OxyServices.devices.js.map +1 -0
  18. package/lib/commonjs/core/mixins/OxyServices.karma.js +117 -0
  19. package/lib/commonjs/core/mixins/OxyServices.karma.js.map +1 -0
  20. package/lib/commonjs/core/mixins/OxyServices.language.js +124 -0
  21. package/lib/commonjs/core/mixins/OxyServices.language.js.map +1 -0
  22. package/lib/commonjs/core/mixins/OxyServices.location.js +55 -0
  23. package/lib/commonjs/core/mixins/OxyServices.location.js.map +1 -0
  24. package/lib/commonjs/core/mixins/OxyServices.payment.js +66 -0
  25. package/lib/commonjs/core/mixins/OxyServices.payment.js.map +1 -0
  26. package/lib/commonjs/core/mixins/OxyServices.privacy.js +174 -0
  27. package/lib/commonjs/core/mixins/OxyServices.privacy.js.map +1 -0
  28. package/lib/commonjs/core/mixins/OxyServices.totp.js +53 -0
  29. package/lib/commonjs/core/mixins/OxyServices.totp.js.map +1 -0
  30. package/lib/commonjs/core/mixins/OxyServices.user.js +389 -0
  31. package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -0
  32. package/lib/commonjs/core/mixins/OxyServices.utility.js +161 -0
  33. package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -0
  34. package/lib/commonjs/core/mixins/index.js +39 -0
  35. package/lib/commonjs/core/mixins/index.js.map +1 -0
  36. package/lib/commonjs/core/mixins/mixinHelpers.js +62 -0
  37. package/lib/commonjs/core/mixins/mixinHelpers.js.map +1 -0
  38. package/lib/commonjs/index.js.map +1 -1
  39. package/lib/commonjs/ui/context/OxyContext.js +26 -47
  40. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  41. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +239 -1
  42. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  43. package/lib/commonjs/utils/apiUtils.js +0 -14
  44. package/lib/commonjs/utils/apiUtils.js.map +1 -1
  45. package/lib/commonjs/utils/asyncUtils.js +0 -20
  46. package/lib/commonjs/utils/asyncUtils.js.map +1 -1
  47. package/lib/module/core/OxyServices.base.js +265 -0
  48. package/lib/module/core/OxyServices.base.js.map +1 -0
  49. package/lib/module/core/OxyServices.errors.js +20 -0
  50. package/lib/module/core/OxyServices.errors.js.map +1 -0
  51. package/lib/module/core/OxyServices.js +43 -2005
  52. package/lib/module/core/OxyServices.js.map +1 -1
  53. package/lib/module/core/mixins/OxyServices.analytics.js +56 -0
  54. package/lib/module/core/mixins/OxyServices.analytics.js.map +1 -0
  55. package/lib/module/core/mixins/OxyServices.assets.js +402 -0
  56. package/lib/module/core/mixins/OxyServices.assets.js.map +1 -0
  57. package/lib/module/core/mixins/OxyServices.auth.js +299 -0
  58. package/lib/module/core/mixins/OxyServices.auth.js.map +1 -0
  59. package/lib/module/core/mixins/OxyServices.developer.js +111 -0
  60. package/lib/module/core/mixins/OxyServices.developer.js.map +1 -0
  61. package/lib/module/core/mixins/OxyServices.devices.js +115 -0
  62. package/lib/module/core/mixins/OxyServices.devices.js.map +1 -0
  63. package/lib/module/core/mixins/OxyServices.karma.js +113 -0
  64. package/lib/module/core/mixins/OxyServices.karma.js.map +1 -0
  65. package/lib/module/core/mixins/OxyServices.language.js +120 -0
  66. package/lib/module/core/mixins/OxyServices.language.js.map +1 -0
  67. package/lib/module/core/mixins/OxyServices.location.js +51 -0
  68. package/lib/module/core/mixins/OxyServices.location.js.map +1 -0
  69. package/lib/module/core/mixins/OxyServices.payment.js +62 -0
  70. package/lib/module/core/mixins/OxyServices.payment.js.map +1 -0
  71. package/lib/module/core/mixins/OxyServices.privacy.js +170 -0
  72. package/lib/module/core/mixins/OxyServices.privacy.js.map +1 -0
  73. package/lib/module/core/mixins/OxyServices.totp.js +49 -0
  74. package/lib/module/core/mixins/OxyServices.totp.js.map +1 -0
  75. package/lib/module/core/mixins/OxyServices.user.js +385 -0
  76. package/lib/module/core/mixins/OxyServices.user.js.map +1 -0
  77. package/lib/module/core/mixins/OxyServices.utility.js +156 -0
  78. package/lib/module/core/mixins/OxyServices.utility.js.map +1 -0
  79. package/lib/module/core/mixins/index.js +36 -0
  80. package/lib/module/core/mixins/index.js.map +1 -0
  81. package/lib/module/core/mixins/mixinHelpers.js +56 -0
  82. package/lib/module/core/mixins/mixinHelpers.js.map +1 -0
  83. package/lib/module/index.js.map +1 -1
  84. package/lib/module/ui/context/OxyContext.js +26 -47
  85. package/lib/module/ui/context/OxyContext.js.map +1 -1
  86. package/lib/module/ui/navigation/types.js.map +1 -1
  87. package/lib/module/ui/screens/PrivacySettingsScreen.js +241 -3
  88. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  89. package/lib/module/utils/apiUtils.js +0 -13
  90. package/lib/module/utils/apiUtils.js.map +1 -1
  91. package/lib/module/utils/asyncUtils.js +0 -20
  92. package/lib/module/utils/asyncUtils.js.map +1 -1
  93. package/lib/typescript/core/OxyServices.base.d.ts +123 -0
  94. package/lib/typescript/core/OxyServices.base.d.ts.map +1 -0
  95. package/lib/typescript/core/OxyServices.d.ts +969 -682
  96. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  97. package/lib/typescript/core/OxyServices.errors.d.ts +12 -0
  98. package/lib/typescript/core/OxyServices.errors.d.ts.map +1 -0
  99. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts +70 -0
  100. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -0
  101. package/lib/typescript/core/mixins/OxyServices.assets.d.ts +159 -0
  102. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -0
  103. package/lib/typescript/core/mixins/OxyServices.auth.d.ts +168 -0
  104. package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -0
  105. package/lib/typescript/core/mixins/OxyServices.developer.d.ts +103 -0
  106. package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -0
  107. package/lib/typescript/core/mixins/OxyServices.devices.d.ts +93 -0
  108. package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -0
  109. package/lib/typescript/core/mixins/OxyServices.karma.d.ts +89 -0
  110. package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -0
  111. package/lib/typescript/core/mixins/OxyServices.language.d.ts +85 -0
  112. package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -0
  113. package/lib/typescript/core/mixins/OxyServices.location.d.ts +68 -0
  114. package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -0
  115. package/lib/typescript/core/mixins/OxyServices.payment.d.ts +74 -0
  116. package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -0
  117. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts +126 -0
  118. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -0
  119. package/lib/typescript/core/mixins/OxyServices.totp.d.ts +69 -0
  120. package/lib/typescript/core/mixins/OxyServices.totp.d.ts.map +1 -0
  121. package/lib/typescript/core/mixins/OxyServices.user.d.ts +189 -0
  122. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -0
  123. package/lib/typescript/core/mixins/OxyServices.utility.d.ts +97 -0
  124. package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -0
  125. package/lib/typescript/core/mixins/index.d.ts +898 -0
  126. package/lib/typescript/core/mixins/index.d.ts.map +1 -0
  127. package/lib/typescript/core/mixins/mixinHelpers.d.ts +32 -0
  128. package/lib/typescript/core/mixins/mixinHelpers.d.ts.map +1 -0
  129. package/lib/typescript/index.d.ts +1 -1
  130. package/lib/typescript/index.d.ts.map +1 -1
  131. package/lib/typescript/models/interfaces.d.ts +36 -0
  132. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  133. package/lib/typescript/ui/context/OxyContext.d.ts +2 -6
  134. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  135. package/lib/typescript/ui/navigation/types.d.ts +0 -1
  136. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  137. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  138. package/lib/typescript/utils/apiUtils.d.ts +0 -7
  139. package/lib/typescript/utils/apiUtils.d.ts.map +1 -1
  140. package/lib/typescript/utils/asyncUtils.d.ts +0 -11
  141. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
  142. package/package.json +1 -1
  143. package/src/core/OxyServices.base.ts +311 -0
  144. package/src/core/OxyServices.errors.ts +26 -0
  145. package/src/core/OxyServices.ts +43 -2026
  146. package/src/core/mixins/OxyServices.analytics.ts +53 -0
  147. package/src/core/mixins/OxyServices.assets.ts +390 -0
  148. package/src/core/mixins/OxyServices.auth.ts +275 -0
  149. package/src/core/mixins/OxyServices.developer.ts +114 -0
  150. package/src/core/mixins/OxyServices.devices.ts +103 -0
  151. package/src/core/mixins/OxyServices.karma.ts +111 -0
  152. package/src/core/mixins/OxyServices.language.ts +127 -0
  153. package/src/core/mixins/OxyServices.location.ts +46 -0
  154. package/src/core/mixins/OxyServices.payment.ts +59 -0
  155. package/src/core/mixins/OxyServices.privacy.ts +182 -0
  156. package/src/core/mixins/OxyServices.totp.ts +36 -0
  157. package/src/core/mixins/OxyServices.user.ts +380 -0
  158. package/src/core/mixins/OxyServices.utility.ts +187 -0
  159. package/src/core/mixins/index.ts +58 -0
  160. package/src/core/mixins/mixinHelpers.ts +69 -0
  161. package/src/index.ts +4 -0
  162. package/src/models/interfaces.ts +40 -0
  163. package/src/ui/context/OxyContext.tsx +35 -53
  164. package/src/ui/navigation/types.ts +0 -1
  165. package/src/ui/screens/PrivacySettingsScreen.tsx +240 -2
  166. package/src/utils/apiUtils.ts +0 -14
  167. package/src/utils/asyncUtils.ts +0 -20
  168. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +0 -192
  169. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +0 -1
  170. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +0 -142
  171. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +0 -1
  172. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +0 -113
  173. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +0 -1
  174. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +0 -132
  175. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +0 -1
  176. package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js +0 -83
  177. package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js.map +0 -1
  178. package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js +0 -58
  179. package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js.map +0 -1
  180. package/lib/module/ui/screens/internal/SignInPasswordStep.js +0 -186
  181. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +0 -1
  182. package/lib/module/ui/screens/internal/SignInUsernameStep.js +0 -136
  183. package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +0 -1
  184. package/lib/module/ui/screens/internal/SignUpIdentityStep.js +0 -108
  185. package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +0 -1
  186. package/lib/module/ui/screens/internal/SignUpSecurityStep.js +0 -127
  187. package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +0 -1
  188. package/lib/module/ui/screens/internal/SignUpSummaryStep.js +0 -78
  189. package/lib/module/ui/screens/internal/SignUpSummaryStep.js.map +0 -1
  190. package/lib/module/ui/screens/internal/SignUpWelcomeStep.js +0 -53
  191. package/lib/module/ui/screens/internal/SignUpWelcomeStep.js.map +0 -1
  192. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +0 -28
  193. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +0 -1
  194. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +0 -25
  195. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +0 -1
  196. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts +0 -20
  197. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +0 -1
  198. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts +0 -24
  199. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +0 -1
  200. package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts +0 -15
  201. package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts.map +0 -1
  202. package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts +0 -13
  203. package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts.map +0 -1
  204. package/src/ui/screens/internal/SignInPasswordStep.tsx +0 -184
  205. package/src/ui/screens/internal/SignInUsernameStep.tsx +0 -145
  206. package/src/ui/screens/internal/SignUpIdentityStep.tsx +0 -112
  207. package/src/ui/screens/internal/SignUpSecurityStep.tsx +0 -132
  208. package/src/ui/screens/internal/SignUpSummaryStep.tsx +0 -66
  209. package/src/ui/screens/internal/SignUpWelcomeStep.tsx +0 -52
@@ -90,6 +90,34 @@ export interface Transaction {
90
90
  // Add other transaction fields as needed
91
91
  }
92
92
 
93
+ export interface BlockedUser {
94
+ _id?: string;
95
+ blockedId: string | {
96
+ _id: string;
97
+ username: string;
98
+ avatar?: string;
99
+ };
100
+ userId: string;
101
+ createdAt?: string;
102
+ blockedAt?: string;
103
+ username?: string;
104
+ avatar?: string;
105
+ }
106
+
107
+ export interface RestrictedUser {
108
+ _id?: string;
109
+ restrictedId: string | {
110
+ _id: string;
111
+ username: string;
112
+ avatar?: string;
113
+ };
114
+ userId: string;
115
+ createdAt?: string;
116
+ restrictedAt?: string;
117
+ username?: string;
118
+ avatar?: string;
119
+ }
120
+
93
121
  export interface TransferFundsRequest {
94
122
  fromUserId: string;
95
123
  toUserId: string;
@@ -113,6 +141,18 @@ export interface TransactionResponse {
113
141
  transaction: Transaction;
114
142
  }
115
143
 
144
+ export interface PaginationInfo {
145
+ total: number;
146
+ limit: number;
147
+ offset: number;
148
+ hasMore: boolean;
149
+ }
150
+
151
+ export interface SearchProfilesResponse {
152
+ data: User[];
153
+ pagination: PaginationInfo;
154
+ }
155
+
116
156
  export interface KarmaRule {
117
157
  id: string;
118
158
  description: string;
@@ -1,6 +1,5 @@
1
1
  import type React from 'react';
2
2
  import { createContext, useContext, useEffect, useCallback, useMemo, useRef, useState, type ReactNode } from 'react';
3
- import type { UseFollowHook } from '../hooks/useFollow.types';
4
3
  import { OxyServices } from '../../core';
5
4
  import type { User, ApiError } from '../../models/interfaces';
6
5
  import type { SessionLoginResponse, ClientSession, MinimalUserData } from '../../models/session';
@@ -13,11 +12,9 @@ import type { BottomSheetController } from '../navigation/types';
13
12
  import type { RouteName } from '../navigation/routes';
14
13
  import { getLanguageMetadata, getLanguageName, getNativeLanguageName, normalizeLanguageCode } from '../../utils/languageUtils';
15
14
  import type { LanguageMetadata } from '../../utils/languageUtils';
15
+ import type { UseFollowHook } from '../hooks/useFollow.types';
16
16
 
17
17
  // Define the context shape
18
- // NOTE: We intentionally avoid importing useFollow here to prevent a require cycle.
19
- // If consumers relied on `const { useFollow } = useOxy()`, we provide a lazy proxy below.
20
-
21
18
  export interface OxyContextState {
22
19
  // Authentication state
23
20
  user: User | null; // Current active user (loaded from server)
@@ -63,33 +60,10 @@ export interface OxyContextState {
63
60
  showBottomSheet?: (screenOrConfig?: RouteName | string | { screen: RouteName | string; props?: Record<string, any> }) => void;
64
61
  hideBottomSheet?: () => void;
65
62
 
66
- /**
67
- * (Deprecated) useFollow hook access via context. Prefer: import { useFollow } from '@oxyhq/services';
68
- * Kept for backward compatibility; implemented as a lazy dynamic require to avoid circular dependency.
69
- */
70
- useFollow: UseFollowHook; // Back-compat; prefer direct import
63
+ // Legacy hook access (for backward compatibility)
64
+ useFollow?: UseFollowHook;
71
65
  }
72
66
 
73
- // Empty follow hook fallback
74
- const createEmptyFollowHook = (): UseFollowHook => {
75
- const emptyResult = {
76
- isFollowing: false,
77
- isLoading: false,
78
- error: null,
79
- toggleFollow: async () => { },
80
- setFollowStatus: () => { },
81
- fetchStatus: async () => { },
82
- clearError: () => { },
83
- followerCount: null,
84
- followingCount: null,
85
- isLoadingCounts: false,
86
- fetchUserCounts: async () => { },
87
- setFollowerCount: () => { },
88
- setFollowingCount: () => { },
89
- };
90
- return () => emptyResult;
91
- };
92
-
93
67
  // Create the context with default values
94
68
  const OxyContext = createContext<OxyContextState | null>(null);
95
69
 
@@ -164,6 +138,35 @@ const getStorageKeys = (prefix = 'oxy_session') => ({
164
138
  language: `${prefix}_language`, // Store the selected language
165
139
  });
166
140
 
141
+ let cachedUseFollowHook: UseFollowHook | null = null;
142
+
143
+ const loadUseFollowHook = (): UseFollowHook => {
144
+ if (cachedUseFollowHook) {
145
+ return cachedUseFollowHook;
146
+ }
147
+
148
+ try {
149
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
150
+ const { useFollow } = require('../hooks/useFollow');
151
+ cachedUseFollowHook = useFollow as UseFollowHook;
152
+ return cachedUseFollowHook;
153
+ } catch (error) {
154
+ if (__DEV__) {
155
+ console.warn(
156
+ 'useFollow hook is not available. Please import useFollow from @oxyhq/services directly.',
157
+ error
158
+ );
159
+ }
160
+
161
+ const fallback: UseFollowHook = () => {
162
+ throw new Error('useFollow hook is only available in the UI bundle. Import it from @oxyhq/services.');
163
+ };
164
+
165
+ cachedUseFollowHook = fallback;
166
+ return cachedUseFollowHook;
167
+ }
168
+ };
169
+
167
170
  export const OxyProvider: React.FC<OxyContextProviderProps> = ({
168
171
  children,
169
172
  oxyServices: providedOxyServices,
@@ -207,6 +210,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
207
210
 
208
211
  const [storage, setStorage] = useState<StorageInterface | null>(null);
209
212
  const [currentLanguage, setCurrentLanguage] = useState<string>('en-US');
213
+ const useFollowHook = useMemo(() => loadUseFollowHook(), []);
210
214
 
211
215
  // Storage keys (memoized to prevent infinite loops) - declared early for use in helpers
212
216
  const keys = useMemo(() => getStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
@@ -903,29 +907,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
903
907
  }, [logout]),
904
908
  });
905
909
 
906
- // Context value - optimized to prevent unnecessary re-renders
907
- // Lazy proxy to load the hook only when accessed, breaking the static import cycle.
908
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
909
- const useFollowProxy: UseFollowHook = (userId?: string | string[]) => {
910
- try {
911
- // Dynamically require to avoid top-level cycle
912
- // eslint-disable-next-line @typescript-eslint/no-var-requires
913
- const mod = require('../hooks/useFollow');
914
- if (mod && typeof mod.useFollow === 'function') {
915
- return mod.useFollow(userId);
916
- }
917
- if (__DEV__) {
918
- console.warn('useFollow module did not export a function as expected');
919
- }
920
- return createEmptyFollowHook()(userId);
921
- } catch (e) {
922
- if (__DEV__) {
923
- console.warn('Failed to dynamically load useFollow hook:', e);
924
- }
925
- return createEmptyFollowHook()(userId);
926
- }
927
- };
928
-
929
910
  // Compute language metadata from currentLanguage
930
911
  const languageMetadata = useMemo(() => getLanguageMetadata(currentLanguage), [currentLanguage]);
931
912
  const languageName = useMemo(() => getLanguageName(currentLanguage), [currentLanguage]);
@@ -960,7 +941,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
960
941
  bottomSheetRef,
961
942
  showBottomSheet,
962
943
  hideBottomSheet,
963
- useFollow: useFollowProxy,
944
+ useFollow: useFollowHook,
964
945
  }), [
965
946
  user?.id, // Only depend on user ID, not the entire user object
966
947
  minimalUser?.id,
@@ -990,6 +971,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
990
971
  bottomSheetRef,
991
972
  showBottomSheet,
992
973
  hideBottomSheet,
974
+ useFollowHook,
993
975
  ]);
994
976
 
995
977
  // Always render children - let the consuming app decide how to handle token loading state
@@ -80,7 +80,6 @@ export interface OxyProviderProps {
80
80
  /**
81
81
  * @internal
82
82
  * Reference to the bottom sheet component (for internal use only)
83
- * @deprecated External bottom sheet ref is no longer required as OxyProvider handles the bottom sheet internally
84
83
  * @hidden
85
84
  */
86
85
  bottomSheetRef?: React.RefObject<BottomSheetController | null>;
@@ -6,12 +6,14 @@ import {
6
6
  ScrollView,
7
7
  Switch,
8
8
  ActivityIndicator,
9
+ TouchableOpacity,
9
10
  } from 'react-native';
10
11
  import type { BaseScreenProps } from '../navigation/types';
11
12
  import { useOxy } from '../context/OxyContext';
12
13
  import { toast } from '../../lib/sonner';
13
- import { Header, Section } from '../components';
14
+ import { Header, Section, Avatar } from '../components';
14
15
  import { useI18n } from '../hooks/useI18n';
16
+ import type { BlockedUser, RestrictedUser } from '../../models/interfaces';
15
17
 
16
18
  interface PrivacySettings {
17
19
  isPrivateAccount: boolean;
@@ -67,8 +69,11 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
67
69
  });
68
70
  const [isLoading, setIsLoading] = useState(true);
69
71
  const [isSaving, setIsSaving] = useState(false);
72
+ const [blockedUsers, setBlockedUsers] = useState<BlockedUser[]>([]);
73
+ const [restrictedUsers, setRestrictedUsers] = useState<RestrictedUser[]>([]);
74
+ const [isLoadingUsers, setIsLoadingUsers] = useState(false);
70
75
 
71
- // Load settings
76
+ // Load settings and users
72
77
  useEffect(() => {
73
78
  const loadSettings = async () => {
74
79
  try {
@@ -90,6 +95,28 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
90
95
  loadSettings();
91
96
  }, [user?.id, oxyServices, t]);
92
97
 
98
+ // Load blocked and restricted users
99
+ useEffect(() => {
100
+ const loadUsers = async () => {
101
+ if (!oxyServices) return;
102
+ try {
103
+ setIsLoadingUsers(true);
104
+ const [blocked, restricted] = await Promise.all([
105
+ oxyServices.getBlockedUsers(),
106
+ oxyServices.getRestrictedUsers(),
107
+ ]);
108
+ setBlockedUsers(blocked);
109
+ setRestrictedUsers(restricted);
110
+ } catch (error) {
111
+ console.error('Failed to load blocked/restricted users:', error);
112
+ } finally {
113
+ setIsLoadingUsers(false);
114
+ }
115
+ };
116
+
117
+ loadUsers();
118
+ }, [oxyServices]);
119
+
93
120
  const updateSetting = useCallback(async (key: keyof PrivacySettings, value: boolean) => {
94
121
  try {
95
122
  setIsSaving(true);
@@ -110,6 +137,113 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
110
137
  }
111
138
  }, [settings, user?.id, oxyServices, t]);
112
139
 
140
+ const handleUnblock = useCallback(async (userId: string) => {
141
+ if (!oxyServices) return;
142
+ try {
143
+ await oxyServices.unblockUser(userId);
144
+ setBlockedUsers(prev => prev.filter(u => {
145
+ const id = typeof u.blockedId === 'string' ? u.blockedId : u.blockedId._id;
146
+ return id !== userId;
147
+ }));
148
+ toast.success(t('privacySettings.userUnblocked') || 'User unblocked');
149
+ } catch (error) {
150
+ console.error('Failed to unblock user:', error);
151
+ toast.error(t('privacySettings.unblockError') || 'Failed to unblock user');
152
+ }
153
+ }, [oxyServices, t]);
154
+
155
+ const handleUnrestrict = useCallback(async (userId: string) => {
156
+ if (!oxyServices) return;
157
+ try {
158
+ await oxyServices.unrestrictUser(userId);
159
+ setRestrictedUsers(prev => prev.filter(u => {
160
+ const id = typeof u.restrictedId === 'string' ? u.restrictedId : u.restrictedId._id;
161
+ return id !== userId;
162
+ }));
163
+ toast.success(t('privacySettings.userUnrestricted') || 'User unrestricted');
164
+ } catch (error) {
165
+ console.error('Failed to unrestrict user:', error);
166
+ toast.error(t('privacySettings.unrestrictError') || 'Failed to unrestrict user');
167
+ }
168
+ }, [oxyServices, t]);
169
+
170
+ // Helper to extract user info from blocked/restricted objects
171
+ const extractUserInfo = useCallback((
172
+ item: BlockedUser | RestrictedUser,
173
+ idField: 'blockedId' | 'restrictedId'
174
+ ) => {
175
+ let userIdField: string | { _id: string; username?: string; avatar?: string };
176
+ let username: string;
177
+ let avatar: string | undefined;
178
+
179
+ if (idField === 'blockedId' && 'blockedId' in item) {
180
+ userIdField = item.blockedId;
181
+ username = typeof item.blockedId === 'string'
182
+ ? (item.username || 'Unknown')
183
+ : (item.blockedId.username || 'Unknown');
184
+ avatar = typeof item.blockedId === 'string' ? item.avatar : item.blockedId.avatar;
185
+ } else if (idField === 'restrictedId' && 'restrictedId' in item) {
186
+ userIdField = item.restrictedId;
187
+ username = typeof item.restrictedId === 'string'
188
+ ? (item.username || 'Unknown')
189
+ : (item.restrictedId.username || 'Unknown');
190
+ avatar = typeof item.restrictedId === 'string' ? item.avatar : item.restrictedId.avatar;
191
+ } else {
192
+ // Fallback (should not happen)
193
+ return { userId: '', username: 'Unknown', avatar: undefined };
194
+ }
195
+
196
+ const userId = typeof userIdField === 'string' ? userIdField : userIdField._id;
197
+ return { userId, username, avatar };
198
+ }, []);
199
+
200
+ // Reusable user list item component
201
+ const UserListItem: React.FC<{
202
+ item: BlockedUser | RestrictedUser;
203
+ idField: 'blockedId' | 'restrictedId';
204
+ onAction: (userId: string) => void;
205
+ actionLabel: string;
206
+ actionColor: string;
207
+ subtitle?: string;
208
+ }> = ({ item, idField, onAction, actionLabel, actionColor, subtitle }) => {
209
+ const { userId, username, avatar } = extractUserInfo(item, idField);
210
+ // Convert avatar file ID to URI if needed
211
+ const avatarUri = avatar && oxyServices
212
+ ? oxyServices.getFileDownloadUrl(avatar, 'thumb')
213
+ : undefined;
214
+
215
+ return (
216
+ <View style={[styles.userRow, { borderBottomColor: themeStyles.borderColor }]}>
217
+ <View style={styles.userInfo}>
218
+ <Avatar
219
+ uri={avatarUri}
220
+ name={username}
221
+ size={40}
222
+ theme={theme}
223
+ />
224
+ <View style={styles.userDetails}>
225
+ <Text style={[styles.username, { color: themeStyles.textColor }]}>
226
+ {username}
227
+ </Text>
228
+ {subtitle && (
229
+ <Text style={[styles.userSubtext, { color: themeStyles.mutedTextColor }]}>
230
+ {subtitle}
231
+ </Text>
232
+ )}
233
+ </View>
234
+ </View>
235
+ <TouchableOpacity
236
+ onPress={() => onAction(userId)}
237
+ style={[styles.actionButton, { backgroundColor: themeStyles.secondaryBackgroundColor }]}
238
+ >
239
+ <Text style={[styles.actionButtonText, { color: actionColor }]}>
240
+ {actionLabel}
241
+ </Text>
242
+ </TouchableOpacity>
243
+ </View>
244
+ );
245
+ };
246
+
113
247
  const themeStyles = useMemo(() => {
114
248
  const isDarkTheme = theme === 'dark';
115
249
  return {
@@ -288,6 +422,65 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
288
422
  onValueChange={(value) => updateSetting('blockScreenshots', value)}
289
423
  />
290
424
  </Section>
425
+
426
+ {/* Blocked Users */}
427
+ <Section title={t('privacySettings.sections.blockedUsers') || 'BLOCKED USERS'} theme={theme}>
428
+ {isLoadingUsers ? (
429
+ <View style={styles.loadingUsersContainer}>
430
+ <ActivityIndicator size="small" color={themeStyles.textColor} />
431
+ </View>
432
+ ) : blockedUsers.length === 0 ? (
433
+ <View style={styles.emptyContainer}>
434
+ <Text style={[styles.emptyText, { color: themeStyles.mutedTextColor }]}>
435
+ {t('privacySettings.noBlockedUsers') || 'No blocked users'}
436
+ </Text>
437
+ </View>
438
+ ) : (
439
+ blockedUsers.map((blocked) => {
440
+ const { userId } = extractUserInfo(blocked, 'blockedId');
441
+ return (
442
+ <UserListItem
443
+ key={userId}
444
+ item={blocked}
445
+ idField="blockedId"
446
+ onAction={handleUnblock}
447
+ actionLabel={t('privacySettings.unblock') || 'Unblock'}
448
+ actionColor="#FF3B30"
449
+ />
450
+ );
451
+ })
452
+ )}
453
+ </Section>
454
+
455
+ {/* Restricted Users */}
456
+ <Section title={t('privacySettings.sections.restrictedUsers') || 'RESTRICTED USERS'} theme={theme}>
457
+ {isLoadingUsers ? (
458
+ <View style={styles.loadingUsersContainer}>
459
+ <ActivityIndicator size="small" color={themeStyles.textColor} />
460
+ </View>
461
+ ) : restrictedUsers.length === 0 ? (
462
+ <View style={styles.emptyContainer}>
463
+ <Text style={[styles.emptyText, { color: themeStyles.mutedTextColor }]}>
464
+ {t('privacySettings.noRestrictedUsers') || 'No restricted users'}
465
+ </Text>
466
+ </View>
467
+ ) : (
468
+ restrictedUsers.map((restricted) => {
469
+ const { userId } = extractUserInfo(restricted, 'restrictedId');
470
+ return (
471
+ <UserListItem
472
+ key={userId}
473
+ item={restricted}
474
+ idField="restrictedId"
475
+ onAction={handleUnrestrict}
476
+ actionLabel={t('privacySettings.unrestrict') || 'Unrestrict'}
477
+ actionColor="#007AFF"
478
+ subtitle={t('privacySettings.restrictedDescription') || 'Limited interactions'}
479
+ />
480
+ );
481
+ })
482
+ )}
483
+ </Section>
291
484
  </ScrollView>
292
485
  </View>
293
486
  );
@@ -326,6 +519,51 @@ const styles = StyleSheet.create({
326
519
  fontSize: 14,
327
520
  opacity: 0.7,
328
521
  },
522
+ loadingUsersContainer: {
523
+ paddingVertical: 20,
524
+ alignItems: 'center',
525
+ },
526
+ emptyContainer: {
527
+ paddingVertical: 20,
528
+ alignItems: 'center',
529
+ },
530
+ emptyText: {
531
+ fontSize: 14,
532
+ },
533
+ userRow: {
534
+ flexDirection: 'row',
535
+ justifyContent: 'space-between',
536
+ alignItems: 'center',
537
+ paddingVertical: 12,
538
+ borderBottomWidth: 1,
539
+ },
540
+ userInfo: {
541
+ flexDirection: 'row',
542
+ alignItems: 'center',
543
+ flex: 1,
544
+ marginRight: 12,
545
+ },
546
+ userDetails: {
547
+ marginLeft: 12,
548
+ flex: 1,
549
+ },
550
+ username: {
551
+ fontSize: 16,
552
+ fontWeight: '500',
553
+ marginBottom: 2,
554
+ },
555
+ userSubtext: {
556
+ fontSize: 13,
557
+ },
558
+ actionButton: {
559
+ paddingHorizontal: 16,
560
+ paddingVertical: 8,
561
+ borderRadius: 8,
562
+ },
563
+ actionButtonText: {
564
+ fontSize: 14,
565
+ fontWeight: '600',
566
+ },
329
567
  });
330
568
 
331
569
  export default React.memo(PrivacySettingsScreen);
@@ -70,20 +70,6 @@ export interface ErrorResponse {
70
70
  details?: any;
71
71
  }
72
72
 
73
- /**
74
- * Validate required parameters
75
- * @param params Object to validate
76
- * @param requiredKeys Array of required keys
77
- * @throws Error if any required key is missing
78
- */
79
- export function validateRequiredParams(params: Record<string, any>, requiredKeys: string[]): void {
80
- const missing = requiredKeys.filter(key => params[key] === undefined || params[key] === null);
81
-
82
- if (missing.length > 0) {
83
- throw new Error(`Missing required parameters: ${missing.join(', ')}`);
84
- }
85
- }
86
-
87
73
  /**
88
74
  * Safe JSON parsing with error handling
89
75
  * @param data Data to parse
@@ -196,26 +196,6 @@ export async function withTimeout<T>(
196
196
  return Promise.race([operation, timeoutPromise]);
197
197
  }
198
198
 
199
- /**
200
- * Cache async operation results
201
- * @deprecated Use TTLCache from '../utils/cache' instead
202
- * This function is kept for backward compatibility but will be removed in a future version
203
- */
204
- export function createAsyncCache<T>(
205
- ttl: number = 5 * 60 * 1000 // 5 minutes default
206
- ) {
207
- // Re-export from centralized cache utility
208
- const cache = new TTLCache<T>(ttl);
209
- registerCacheForCleanup(cache);
210
-
211
- return {
212
- get: (key: string): T | null => cache.get(key),
213
- set: (key: string, data: T): void => cache.set(key, data),
214
- clear: (): void => cache.clear(),
215
- delete: (key: string): boolean => cache.delete(key),
216
- };
217
- }
218
-
219
199
  /**
220
200
  * Execute async operation with loading state
221
201
  */