@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.
- package/README.md +10 -0
- package/lib/commonjs/core/OxyServices.base.js +271 -0
- package/lib/commonjs/core/OxyServices.base.js.map +1 -0
- package/lib/commonjs/core/OxyServices.errors.js +26 -0
- package/lib/commonjs/core/OxyServices.errors.js.map +1 -0
- package/lib/commonjs/core/OxyServices.js +58 -2009
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.analytics.js +60 -0
- package/lib/commonjs/core/mixins/OxyServices.analytics.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.assets.js +406 -0
- package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.auth.js +303 -0
- package/lib/commonjs/core/mixins/OxyServices.auth.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.developer.js +115 -0
- package/lib/commonjs/core/mixins/OxyServices.developer.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.devices.js +119 -0
- package/lib/commonjs/core/mixins/OxyServices.devices.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.karma.js +117 -0
- package/lib/commonjs/core/mixins/OxyServices.karma.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.language.js +124 -0
- package/lib/commonjs/core/mixins/OxyServices.language.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.location.js +55 -0
- package/lib/commonjs/core/mixins/OxyServices.location.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.payment.js +66 -0
- package/lib/commonjs/core/mixins/OxyServices.payment.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.privacy.js +174 -0
- package/lib/commonjs/core/mixins/OxyServices.privacy.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.totp.js +53 -0
- package/lib/commonjs/core/mixins/OxyServices.totp.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.user.js +389 -0
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.utility.js +161 -0
- package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -0
- package/lib/commonjs/core/mixins/index.js +39 -0
- package/lib/commonjs/core/mixins/index.js.map +1 -0
- package/lib/commonjs/core/mixins/mixinHelpers.js +62 -0
- package/lib/commonjs/core/mixins/mixinHelpers.js.map +1 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +26 -47
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +239 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/utils/apiUtils.js +0 -14
- package/lib/commonjs/utils/apiUtils.js.map +1 -1
- package/lib/commonjs/utils/asyncUtils.js +0 -20
- package/lib/commonjs/utils/asyncUtils.js.map +1 -1
- package/lib/module/core/OxyServices.base.js +265 -0
- package/lib/module/core/OxyServices.base.js.map +1 -0
- package/lib/module/core/OxyServices.errors.js +20 -0
- package/lib/module/core/OxyServices.errors.js.map +1 -0
- package/lib/module/core/OxyServices.js +43 -2005
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.analytics.js +56 -0
- package/lib/module/core/mixins/OxyServices.analytics.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.assets.js +402 -0
- package/lib/module/core/mixins/OxyServices.assets.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.auth.js +299 -0
- package/lib/module/core/mixins/OxyServices.auth.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.developer.js +111 -0
- package/lib/module/core/mixins/OxyServices.developer.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.devices.js +115 -0
- package/lib/module/core/mixins/OxyServices.devices.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.karma.js +113 -0
- package/lib/module/core/mixins/OxyServices.karma.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.language.js +120 -0
- package/lib/module/core/mixins/OxyServices.language.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.location.js +51 -0
- package/lib/module/core/mixins/OxyServices.location.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.payment.js +62 -0
- package/lib/module/core/mixins/OxyServices.payment.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.privacy.js +170 -0
- package/lib/module/core/mixins/OxyServices.privacy.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.totp.js +49 -0
- package/lib/module/core/mixins/OxyServices.totp.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.user.js +385 -0
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.utility.js +156 -0
- package/lib/module/core/mixins/OxyServices.utility.js.map +1 -0
- package/lib/module/core/mixins/index.js +36 -0
- package/lib/module/core/mixins/index.js.map +1 -0
- package/lib/module/core/mixins/mixinHelpers.js +56 -0
- package/lib/module/core/mixins/mixinHelpers.js.map +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +26 -47
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/navigation/types.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +241 -3
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/utils/apiUtils.js +0 -13
- package/lib/module/utils/apiUtils.js.map +1 -1
- package/lib/module/utils/asyncUtils.js +0 -20
- package/lib/module/utils/asyncUtils.js.map +1 -1
- package/lib/typescript/core/OxyServices.base.d.ts +123 -0
- package/lib/typescript/core/OxyServices.base.d.ts.map +1 -0
- package/lib/typescript/core/OxyServices.d.ts +969 -682
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/OxyServices.errors.d.ts +12 -0
- package/lib/typescript/core/OxyServices.errors.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.analytics.d.ts +70 -0
- package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.assets.d.ts +159 -0
- package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts +168 -0
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.developer.d.ts +103 -0
- package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.devices.d.ts +93 -0
- package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.karma.d.ts +89 -0
- package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.language.d.ts +85 -0
- package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.location.d.ts +68 -0
- package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.payment.d.ts +74 -0
- package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.privacy.d.ts +126 -0
- package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.totp.d.ts +69 -0
- package/lib/typescript/core/mixins/OxyServices.totp.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +189 -0
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.utility.d.ts +97 -0
- package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -0
- package/lib/typescript/core/mixins/index.d.ts +898 -0
- package/lib/typescript/core/mixins/index.d.ts.map +1 -0
- package/lib/typescript/core/mixins/mixinHelpers.d.ts +32 -0
- package/lib/typescript/core/mixins/mixinHelpers.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +36 -0
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +2 -6
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/types.d.ts +0 -1
- package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/utils/apiUtils.d.ts +0 -7
- package/lib/typescript/utils/apiUtils.d.ts.map +1 -1
- package/lib/typescript/utils/asyncUtils.d.ts +0 -11
- package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/OxyServices.base.ts +311 -0
- package/src/core/OxyServices.errors.ts +26 -0
- package/src/core/OxyServices.ts +43 -2026
- package/src/core/mixins/OxyServices.analytics.ts +53 -0
- package/src/core/mixins/OxyServices.assets.ts +390 -0
- package/src/core/mixins/OxyServices.auth.ts +275 -0
- package/src/core/mixins/OxyServices.developer.ts +114 -0
- package/src/core/mixins/OxyServices.devices.ts +103 -0
- package/src/core/mixins/OxyServices.karma.ts +111 -0
- package/src/core/mixins/OxyServices.language.ts +127 -0
- package/src/core/mixins/OxyServices.location.ts +46 -0
- package/src/core/mixins/OxyServices.payment.ts +59 -0
- package/src/core/mixins/OxyServices.privacy.ts +182 -0
- package/src/core/mixins/OxyServices.totp.ts +36 -0
- package/src/core/mixins/OxyServices.user.ts +380 -0
- package/src/core/mixins/OxyServices.utility.ts +187 -0
- package/src/core/mixins/index.ts +58 -0
- package/src/core/mixins/mixinHelpers.ts +69 -0
- package/src/index.ts +4 -0
- package/src/models/interfaces.ts +40 -0
- package/src/ui/context/OxyContext.tsx +35 -53
- package/src/ui/navigation/types.ts +0 -1
- package/src/ui/screens/PrivacySettingsScreen.tsx +240 -2
- package/src/utils/apiUtils.ts +0 -14
- package/src/utils/asyncUtils.ts +0 -20
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +0 -192
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +0 -1
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +0 -142
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +0 -1
- package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +0 -113
- package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +0 -1
- package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +0 -132
- package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +0 -1
- package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js +0 -83
- package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js.map +0 -1
- package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js +0 -58
- package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js.map +0 -1
- package/lib/module/ui/screens/internal/SignInPasswordStep.js +0 -186
- package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +0 -1
- package/lib/module/ui/screens/internal/SignInUsernameStep.js +0 -136
- package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +0 -1
- package/lib/module/ui/screens/internal/SignUpIdentityStep.js +0 -108
- package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +0 -1
- package/lib/module/ui/screens/internal/SignUpSecurityStep.js +0 -127
- package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +0 -1
- package/lib/module/ui/screens/internal/SignUpSummaryStep.js +0 -78
- package/lib/module/ui/screens/internal/SignUpSummaryStep.js.map +0 -1
- package/lib/module/ui/screens/internal/SignUpWelcomeStep.js +0 -53
- package/lib/module/ui/screens/internal/SignUpWelcomeStep.js.map +0 -1
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +0 -28
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +0 -1
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +0 -25
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +0 -1
- package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts +0 -20
- package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +0 -1
- package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts +0 -24
- package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +0 -1
- package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts +0 -15
- package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts.map +0 -1
- package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts +0 -13
- package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts.map +0 -1
- package/src/ui/screens/internal/SignInPasswordStep.tsx +0 -184
- package/src/ui/screens/internal/SignInUsernameStep.tsx +0 -145
- package/src/ui/screens/internal/SignUpIdentityStep.tsx +0 -112
- package/src/ui/screens/internal/SignUpSecurityStep.tsx +0 -132
- package/src/ui/screens/internal/SignUpSummaryStep.tsx +0 -66
- package/src/ui/screens/internal/SignUpWelcomeStep.tsx +0 -52
package/src/models/interfaces.ts
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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);
|
package/src/utils/apiUtils.ts
CHANGED
|
@@ -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
|
package/src/utils/asyncUtils.ts
CHANGED
|
@@ -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
|
*/
|