@atzentis/auth-expo 0.1.0 → 0.2.0
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/CHANGELOG.md +21 -0
- package/dist/index.d.ts +967 -122
- package/dist/index.js +356 -443
- package/dist/index.js.map +1 -1
- package/package.json +26 -4
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { StyleSheet, View, Text, TextInput, KeyboardAvoidingView, ScrollView, Platform, ActivityIndicator, Alert, FlatList, RefreshControl, ActionSheetIOS,
|
|
1
|
+
import { StyleSheet, View, Text, TextInput, Pressable, KeyboardAvoidingView, ScrollView, Platform, ActivityIndicator, Alert, FlatList, RefreshControl, ActionSheetIOS, Image } from 'react-native';
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { authKeys, forgotPasswordSchema, credentialLoginSchema, magicLinkSchema, phoneLoginSchema, otpVerifySchema, resetPasswordSchema, signupSchema, totpSchema, backupCodeSchema, changeEmailSchema, changePasswordSchema, updateNameSchema, updateUsernameSchema } from '@atzentis/auth-sdk';
|
|
3
|
+
import { InvalidConfigError, authKeys, RequestFailedError, forgotPasswordSchema, credentialLoginSchema, magicLinkSchema, NotImplementedError, phoneLoginSchema, otpVerifySchema, resetPasswordSchema, signupSchema, totpSchema, backupCodeSchema, changeEmailSchema, changePasswordSchema, updateNameSchema, updateUsernameSchema } from '@atzentis/auth-sdk';
|
|
4
4
|
import { QueryClient, QueryClientProvider, useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
5
|
-
import React, { useState, useMemo, useCallback, useEffect
|
|
5
|
+
import React, { forwardRef, useState, useMemo, use, useCallback, useEffect } from 'react';
|
|
6
6
|
import { en } from '@atzentis/auth-locales';
|
|
7
7
|
import { expoClient } from '@better-auth/expo/client';
|
|
8
8
|
import { createAuthClient } from 'better-auth/react';
|
|
9
9
|
import { getLocales } from 'expo-localization';
|
|
10
10
|
import * as SecureStore from 'expo-secure-store';
|
|
11
|
-
import { EyeOff, Eye, AlertCircle, CheckCircle2
|
|
11
|
+
import { EyeOff, Eye, AlertCircle, CheckCircle2 } from 'lucide-react-native';
|
|
12
12
|
import { Host, Button } from '@expo/ui/swift-ui';
|
|
13
13
|
import { useRouter } from 'expo-router';
|
|
14
14
|
import * as Haptics from 'expo-haptics';
|
|
@@ -59,7 +59,9 @@ function AuthProvider({
|
|
|
59
59
|
children,
|
|
60
60
|
locales,
|
|
61
61
|
localization: localizationOverride,
|
|
62
|
-
queryClient: externalQueryClient
|
|
62
|
+
queryClient: externalQueryClient,
|
|
63
|
+
scheme,
|
|
64
|
+
storagePrefix
|
|
63
65
|
}) {
|
|
64
66
|
const queryClient = useMemo(
|
|
65
67
|
() => externalQueryClient ?? new QueryClient(),
|
|
@@ -70,13 +72,13 @@ function AuthProvider({
|
|
|
70
72
|
baseURL,
|
|
71
73
|
plugins: [
|
|
72
74
|
expoClient({
|
|
73
|
-
scheme
|
|
74
|
-
storagePrefix:
|
|
75
|
+
scheme,
|
|
76
|
+
storagePrefix: storagePrefix ?? scheme,
|
|
75
77
|
storage: SecureStore
|
|
76
78
|
})
|
|
77
79
|
]
|
|
78
80
|
}),
|
|
79
|
-
[baseURL]
|
|
81
|
+
[baseURL, scheme, storagePrefix]
|
|
80
82
|
);
|
|
81
83
|
const resolvedLocalization = useMemo(() => {
|
|
82
84
|
const deviceLocales = getLocales();
|
|
@@ -92,9 +94,11 @@ function AuthProvider({
|
|
|
92
94
|
|
|
93
95
|
// src/hooks/use-auth-client.ts
|
|
94
96
|
function useAuthClient() {
|
|
95
|
-
const client =
|
|
97
|
+
const client = use(AuthClientContext);
|
|
96
98
|
if (!client) {
|
|
97
|
-
throw new
|
|
99
|
+
throw new InvalidConfigError({
|
|
100
|
+
message: "useAuthClient must be used within an AuthProvider"
|
|
101
|
+
});
|
|
98
102
|
}
|
|
99
103
|
return client;
|
|
100
104
|
}
|
|
@@ -118,7 +122,7 @@ function AuthGate({ children, fallback = null }) {
|
|
|
118
122
|
}
|
|
119
123
|
function ErrorFeedback({ message }) {
|
|
120
124
|
return /* @__PURE__ */ jsxs(View, { style: styles2.container, accessibilityRole: "alert", accessibilityLiveRegion: "polite", children: [
|
|
121
|
-
/* @__PURE__ */ jsx(AlertCircle, { size: 16
|
|
125
|
+
/* @__PURE__ */ jsx(AlertCircle, { size: 16 }),
|
|
122
126
|
/* @__PURE__ */ jsx(Text, { style: styles2.text, children: message })
|
|
123
127
|
] });
|
|
124
128
|
}
|
|
@@ -133,7 +137,7 @@ var styles2 = StyleSheet.create({
|
|
|
133
137
|
},
|
|
134
138
|
text: { fontSize: 14, color: "#ef4444", flex: 1 }
|
|
135
139
|
});
|
|
136
|
-
var FormInput =
|
|
140
|
+
var FormInput = forwardRef(
|
|
137
141
|
({ error, label, style, ...props }, ref) => {
|
|
138
142
|
return /* @__PURE__ */ jsxs(View, { style: { gap: 4 }, children: [
|
|
139
143
|
label && /* @__PURE__ */ jsx(Text, { style: { fontSize: 14, fontWeight: "500", color: "#1f2937" }, children: label }),
|
|
@@ -164,7 +168,7 @@ var FormInput = React.forwardRef(
|
|
|
164
168
|
}
|
|
165
169
|
);
|
|
166
170
|
FormInput.displayName = "FormInput";
|
|
167
|
-
var FormPasswordInput =
|
|
171
|
+
var FormPasswordInput = forwardRef(
|
|
168
172
|
({ error, label, style, ...props }, ref) => {
|
|
169
173
|
const [visible, setVisible] = useState(false);
|
|
170
174
|
return /* @__PURE__ */ jsxs(View, { style: { gap: 4 }, children: [
|
|
@@ -195,8 +199,11 @@ var FormPasswordInput = React.forwardRef(
|
|
|
195
199
|
}
|
|
196
200
|
),
|
|
197
201
|
/* @__PURE__ */ jsx(
|
|
198
|
-
|
|
202
|
+
Pressable,
|
|
199
203
|
{
|
|
204
|
+
onPress: () => setVisible((v) => !v),
|
|
205
|
+
accessibilityRole: "button",
|
|
206
|
+
accessibilityLabel: visible ? "Hide password" : "Show password",
|
|
200
207
|
style: {
|
|
201
208
|
position: "absolute",
|
|
202
209
|
right: 8,
|
|
@@ -206,9 +213,7 @@ var FormPasswordInput = React.forwardRef(
|
|
|
206
213
|
alignItems: "center",
|
|
207
214
|
width: 32
|
|
208
215
|
},
|
|
209
|
-
|
|
210
|
-
onPress: () => setVisible((v) => !v),
|
|
211
|
-
children: visible ? /* @__PURE__ */ jsx(EyeOff, { size: 20, color: "#6b7280" }) : /* @__PURE__ */ jsx(Eye, { size: 20, color: "#6b7280" })
|
|
216
|
+
children: visible ? /* @__PURE__ */ jsx(EyeOff, { size: 20 }) : /* @__PURE__ */ jsx(Eye, { size: 20 })
|
|
212
217
|
}
|
|
213
218
|
)
|
|
214
219
|
] }),
|
|
@@ -218,9 +223,9 @@ var FormPasswordInput = React.forwardRef(
|
|
|
218
223
|
);
|
|
219
224
|
FormPasswordInput.displayName = "FormPasswordInput";
|
|
220
225
|
function useAuthLocalization() {
|
|
221
|
-
return
|
|
226
|
+
return use(LocalizationContext);
|
|
222
227
|
}
|
|
223
|
-
function OAuthButton({
|
|
228
|
+
function OAuthButton({ onError, onSuccess, provider }) {
|
|
224
229
|
const { localization: L } = useAuthLocalization();
|
|
225
230
|
const router = useRouter();
|
|
226
231
|
const client = useAuthClient();
|
|
@@ -229,16 +234,16 @@ function OAuthButton({ disabled = false, onError, onSuccess, provider }) {
|
|
|
229
234
|
mutationFn: async () => {
|
|
230
235
|
const response = await client.signIn.social({ provider });
|
|
231
236
|
if (response.error) {
|
|
232
|
-
throw new
|
|
237
|
+
throw new RequestFailedError({ message: response.error.message ?? "OAuth sign-in failed" });
|
|
233
238
|
}
|
|
234
239
|
return response.data;
|
|
235
240
|
},
|
|
236
241
|
onSuccess: (data) => {
|
|
237
|
-
if (data
|
|
238
|
-
queryClient.
|
|
242
|
+
if ("url" in data && data.url) {
|
|
243
|
+
queryClient.invalidateQueries({ queryKey: authKeys.session() });
|
|
239
244
|
}
|
|
240
|
-
if (data
|
|
241
|
-
queryClient.setQueryData(authKeys.
|
|
245
|
+
if ("user" in data && data.user) {
|
|
246
|
+
queryClient.setQueryData(authKeys.user(), data.user);
|
|
242
247
|
}
|
|
243
248
|
}
|
|
244
249
|
});
|
|
@@ -260,15 +265,7 @@ function OAuthButton({ disabled = false, onError, onSuccess, provider }) {
|
|
|
260
265
|
}, [oauthMutation, router, onSuccess, onError]);
|
|
261
266
|
const label = `${L.CONTINUE_WITH} ${provider.charAt(0).toUpperCase()}${provider.slice(1)}`;
|
|
262
267
|
const loadingLabel = L.SIGNING_IN;
|
|
263
|
-
return /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
264
|
-
Button,
|
|
265
|
-
{
|
|
266
|
-
onPress: handlePress,
|
|
267
|
-
disabled: disabled || oauthMutation.isPending,
|
|
268
|
-
variant: "bordered",
|
|
269
|
-
children: oauthMutation.isPending ? loadingLabel : label
|
|
270
|
-
}
|
|
271
|
-
) });
|
|
268
|
+
return /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handlePress, label: oauthMutation.isPending ? loadingLabel : label }) });
|
|
272
269
|
}
|
|
273
270
|
function ProtectedScreen({ children, fallback = null }) {
|
|
274
271
|
const { data: session, isLoading } = useSession();
|
|
@@ -284,7 +281,7 @@ function ProtectedScreen({ children, fallback = null }) {
|
|
|
284
281
|
}
|
|
285
282
|
function SuccessFeedback({ message }) {
|
|
286
283
|
return /* @__PURE__ */ jsxs(View, { style: styles3.container, accessibilityRole: "alert", accessibilityLiveRegion: "polite", children: [
|
|
287
|
-
/* @__PURE__ */ jsx(CheckCircle2, { size: 16
|
|
284
|
+
/* @__PURE__ */ jsx(CheckCircle2, { size: 16 }),
|
|
288
285
|
/* @__PURE__ */ jsx(Text, { style: styles3.text, children: message })
|
|
289
286
|
] });
|
|
290
287
|
}
|
|
@@ -300,19 +297,21 @@ var styles3 = StyleSheet.create({
|
|
|
300
297
|
text: { fontSize: 14, color: "#22c55e", flex: 1 }
|
|
301
298
|
});
|
|
302
299
|
function SubmitButton({
|
|
303
|
-
disabled,
|
|
304
300
|
label,
|
|
305
301
|
loading,
|
|
306
302
|
loadingLabel,
|
|
307
|
-
onPress
|
|
303
|
+
onPress,
|
|
304
|
+
disabled = false
|
|
308
305
|
}) {
|
|
306
|
+
const isDisabled = disabled || loading;
|
|
309
307
|
const handlePress = () => {
|
|
308
|
+
if (isDisabled) return;
|
|
310
309
|
if (process.env.EXPO_OS === "ios") {
|
|
311
310
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
312
311
|
}
|
|
313
312
|
onPress();
|
|
314
313
|
};
|
|
315
|
-
return /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, {
|
|
314
|
+
return /* @__PURE__ */ jsx(Host, { matchContents: true, style: { opacity: isDisabled ? 0.5 : 1 }, children: /* @__PURE__ */ jsx(Button, { onPress: handlePress, label: loading ? loadingLabel : label }) });
|
|
316
315
|
}
|
|
317
316
|
function EmailVerificationScreen({ onSuccess, token }) {
|
|
318
317
|
const { localization: L } = useAuthLocalization();
|
|
@@ -321,9 +320,9 @@ function EmailVerificationScreen({ onSuccess, token }) {
|
|
|
321
320
|
const [status, setStatus] = useState(token ? "loading" : "no_token");
|
|
322
321
|
const verifyMutation = useMutation({
|
|
323
322
|
mutationFn: async (verifyToken) => {
|
|
324
|
-
const response = await client.verifyEmail?.({ token: verifyToken });
|
|
323
|
+
const response = await client.verifyEmail?.({ query: { token: verifyToken } });
|
|
325
324
|
if (response?.error) {
|
|
326
|
-
throw new
|
|
325
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
327
326
|
}
|
|
328
327
|
return response?.data;
|
|
329
328
|
}
|
|
@@ -378,12 +377,12 @@ function EmailVerificationScreen({ onSuccess, token }) {
|
|
|
378
377
|
] }),
|
|
379
378
|
status === "error" && /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
380
379
|
/* @__PURE__ */ jsx(ErrorFeedback, { message: verifyMutation.error?.message ?? L.REQUEST_FAILED }),
|
|
381
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleRetry,
|
|
382
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
380
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleRetry, label: L.VERIFY }) }),
|
|
381
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
383
382
|
] }),
|
|
384
383
|
status === "no_token" && /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
385
384
|
/* @__PURE__ */ jsx(ErrorFeedback, { message: L.REQUEST_FAILED }),
|
|
386
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
385
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
387
386
|
] })
|
|
388
387
|
] }) });
|
|
389
388
|
}
|
|
@@ -391,11 +390,16 @@ function useRequestPasswordReset() {
|
|
|
391
390
|
const client = useAuthClient();
|
|
392
391
|
return useMutation({
|
|
393
392
|
mutationFn: async (data) => {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
393
|
+
try {
|
|
394
|
+
await client.requestPasswordReset({
|
|
395
|
+
email: data.email,
|
|
396
|
+
redirectTo: data.redirectTo
|
|
397
|
+
});
|
|
398
|
+
return true;
|
|
399
|
+
} catch (error) {
|
|
400
|
+
const message = error instanceof Error && error.message ? error.message : "Password reset request failed";
|
|
401
|
+
throw new RequestFailedError({ message });
|
|
397
402
|
}
|
|
398
|
-
return true;
|
|
399
403
|
}
|
|
400
404
|
});
|
|
401
405
|
}
|
|
@@ -471,15 +475,20 @@ function ForgotPasswordScreen({ onSuccess }) {
|
|
|
471
475
|
label: L.FORGOT_PASSWORD_ACTION,
|
|
472
476
|
loading: forgotMutation.isPending,
|
|
473
477
|
loadingLabel: L.SENDING,
|
|
474
|
-
onPress: form.handleSubmit(handleSubmit)
|
|
475
|
-
disabled: !form.formState.isValid
|
|
478
|
+
onPress: form.handleSubmit(handleSubmit)
|
|
476
479
|
}
|
|
477
480
|
),
|
|
478
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
481
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
479
482
|
] }) : /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
480
483
|
/* @__PURE__ */ jsx(SuccessFeedback, { message: `${L.FORGOT_PASSWORD_EMAIL} ${form.getValues("email")}` }),
|
|
481
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
482
|
-
|
|
484
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
485
|
+
Button,
|
|
486
|
+
{
|
|
487
|
+
onPress: handleResend,
|
|
488
|
+
label: cooldown > 0 ? `${L.RESEND} (${cooldown}s)` : L.RESEND
|
|
489
|
+
}
|
|
490
|
+
) }),
|
|
491
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
483
492
|
] })
|
|
484
493
|
] }) });
|
|
485
494
|
}
|
|
@@ -490,16 +499,14 @@ function useLogin() {
|
|
|
490
499
|
mutationFn: async (data) => {
|
|
491
500
|
const response = await client.signIn.email(data);
|
|
492
501
|
if (response.error) {
|
|
493
|
-
throw new
|
|
502
|
+
throw new RequestFailedError({ message: response.error.message ?? "Login failed" });
|
|
494
503
|
}
|
|
495
504
|
return response.data;
|
|
496
505
|
},
|
|
497
506
|
onSuccess: (data) => {
|
|
498
507
|
if (data?.user) {
|
|
499
508
|
queryClient.setQueryData(authKeys.user(), data.user);
|
|
500
|
-
|
|
501
|
-
if (data?.session) {
|
|
502
|
-
queryClient.setQueryData(authKeys.session(), data.session);
|
|
509
|
+
queryClient.invalidateQueries({ queryKey: authKeys.session() });
|
|
503
510
|
}
|
|
504
511
|
}
|
|
505
512
|
});
|
|
@@ -588,24 +595,26 @@ function LoginScreen({
|
|
|
588
595
|
label: L.SIGN_IN_ACTION,
|
|
589
596
|
loading: loginMutation.isPending,
|
|
590
597
|
loadingLabel: L.SIGNING_IN,
|
|
591
|
-
onPress: form.handleSubmit(onSubmit)
|
|
592
|
-
disabled: !form.formState.isValid
|
|
598
|
+
onPress: form.handleSubmit(onSubmit)
|
|
593
599
|
}
|
|
594
600
|
),
|
|
595
|
-
biometricAvailable && process.env.EXPO_OS === "ios" && onBiometricPress && /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */
|
|
596
|
-
/* @__PURE__ */ jsx(Fingerprint, { size: 20, color: "#6b7280" }),
|
|
597
|
-
L.SIGN_IN
|
|
598
|
-
] }) }),
|
|
601
|
+
biometricAvailable && process.env.EXPO_OS === "ios" && onBiometricPress && /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: onBiometricPress }) }),
|
|
599
602
|
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", justifyContent: "space-between", marginTop: 8 }, children: [
|
|
600
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.push("/signup"),
|
|
601
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
603
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.push("/signup"), label: L.DONT_HAVE_AN_ACCOUNT }) }),
|
|
604
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
605
|
+
Button,
|
|
606
|
+
{
|
|
607
|
+
onPress: () => router.push("/forgot-password"),
|
|
608
|
+
label: L.FORGOT_PASSWORD_LINK
|
|
609
|
+
}
|
|
610
|
+
) })
|
|
602
611
|
] })
|
|
603
612
|
] }) });
|
|
604
613
|
}
|
|
605
614
|
function MagicLinkScreen({ onSuccess, token }) {
|
|
606
615
|
const { localization: L } = useAuthLocalization();
|
|
607
616
|
const router = useRouter();
|
|
608
|
-
|
|
617
|
+
useAuthClient();
|
|
609
618
|
const [state, setState] = useState(
|
|
610
619
|
token ? { step: "verifying", token } : { step: "email" }
|
|
611
620
|
);
|
|
@@ -616,21 +625,13 @@ function MagicLinkScreen({ onSuccess, token }) {
|
|
|
616
625
|
mode: "onChange"
|
|
617
626
|
});
|
|
618
627
|
const sendMutation = useMutation({
|
|
619
|
-
mutationFn: async (
|
|
620
|
-
|
|
621
|
-
if (response?.error) {
|
|
622
|
-
throw new Error(response.error.message ?? "Failed to send magic link");
|
|
623
|
-
}
|
|
624
|
-
return response?.data;
|
|
628
|
+
mutationFn: async (_data) => {
|
|
629
|
+
throw new NotImplementedError({ feature: "Magic link authentication" });
|
|
625
630
|
}
|
|
626
631
|
});
|
|
627
632
|
const verifyMutation = useMutation({
|
|
628
|
-
mutationFn: async (
|
|
629
|
-
|
|
630
|
-
if (response?.error) {
|
|
631
|
-
throw new Error(response.error.message ?? "Invalid or expired link");
|
|
632
|
-
}
|
|
633
|
-
return response?.data;
|
|
633
|
+
mutationFn: async (_verifyToken) => {
|
|
634
|
+
throw new NotImplementedError({ feature: "Magic link verification" });
|
|
634
635
|
}
|
|
635
636
|
});
|
|
636
637
|
const handleSend = useCallback(
|
|
@@ -702,8 +703,7 @@ function MagicLinkScreen({ onSuccess, token }) {
|
|
|
702
703
|
label: L.MAGIC_LINK_ACTION,
|
|
703
704
|
loading: sendMutation.isPending,
|
|
704
705
|
loadingLabel: L.SENDING,
|
|
705
|
-
onPress: form.handleSubmit(handleSend)
|
|
706
|
-
disabled: !form.formState.isValid
|
|
706
|
+
onPress: form.handleSubmit(handleSend)
|
|
707
707
|
}
|
|
708
708
|
)
|
|
709
709
|
] }),
|
|
@@ -721,9 +721,7 @@ function MagicLinkScreen({ onSuccess, token }) {
|
|
|
721
721
|
);
|
|
722
722
|
}
|
|
723
723
|
},
|
|
724
|
-
|
|
725
|
-
variant: "borderless",
|
|
726
|
-
children: cooldown > 0 ? `${L.RESEND} (${cooldown}s)` : L.RESEND
|
|
724
|
+
label: cooldown > 0 ? `${L.RESEND} (${cooldown}s)` : L.RESEND
|
|
727
725
|
}
|
|
728
726
|
) })
|
|
729
727
|
] }),
|
|
@@ -731,14 +729,14 @@ function MagicLinkScreen({ onSuccess, token }) {
|
|
|
731
729
|
state.step === "success" && /* @__PURE__ */ jsx(SuccessFeedback, { message: L.REDIRECTING }),
|
|
732
730
|
state.step === "error" && /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
733
731
|
/* @__PURE__ */ jsx(ErrorFeedback, { message: state.message }),
|
|
734
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setState({ step: "email" }),
|
|
732
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setState({ step: "email" }), label: L.GO_BACK }) })
|
|
735
733
|
] })
|
|
736
734
|
] }) });
|
|
737
735
|
}
|
|
738
736
|
function PhoneLoginScreen({ onSuccess }) {
|
|
739
737
|
const { localization: L } = useAuthLocalization();
|
|
740
738
|
const router = useRouter();
|
|
741
|
-
|
|
739
|
+
useAuthClient();
|
|
742
740
|
const [state, setState] = useState({ step: 1 });
|
|
743
741
|
const [cooldown, setCooldown] = useState(0);
|
|
744
742
|
const phoneForm = useForm({
|
|
@@ -752,23 +750,13 @@ function PhoneLoginScreen({ onSuccess }) {
|
|
|
752
750
|
mode: "onChange"
|
|
753
751
|
});
|
|
754
752
|
const sendOtpMutation = useMutation({
|
|
755
|
-
mutationFn: async (
|
|
756
|
-
|
|
757
|
-
phoneNumber: `${data.countryCode}${data.phoneNumber}`
|
|
758
|
-
});
|
|
759
|
-
if (response?.error) {
|
|
760
|
-
throw new Error(response.error.message ?? "Failed to send code");
|
|
761
|
-
}
|
|
762
|
-
return response?.data;
|
|
753
|
+
mutationFn: async (_data) => {
|
|
754
|
+
throw new NotImplementedError({ feature: "Phone number authentication" });
|
|
763
755
|
}
|
|
764
756
|
});
|
|
765
757
|
const verifyOtpMutation = useMutation({
|
|
766
|
-
mutationFn: async (
|
|
767
|
-
|
|
768
|
-
if (response?.error) {
|
|
769
|
-
throw new Error(response.error.message ?? "Invalid code");
|
|
770
|
-
}
|
|
771
|
-
return response?.data;
|
|
758
|
+
mutationFn: async (_data) => {
|
|
759
|
+
throw new NotImplementedError({ feature: "Phone number verification" });
|
|
772
760
|
}
|
|
773
761
|
});
|
|
774
762
|
const handleSendOtp = useCallback(
|
|
@@ -869,8 +857,7 @@ function PhoneLoginScreen({ onSuccess }) {
|
|
|
869
857
|
label: L.SEND_VERIFICATION_CODE,
|
|
870
858
|
loading: sendOtpMutation.isPending,
|
|
871
859
|
loadingLabel: L.SENDING_VERIFICATION_CODE,
|
|
872
|
-
onPress: phoneForm.handleSubmit(handleSendOtp)
|
|
873
|
-
disabled: !phoneForm.formState.isValid
|
|
860
|
+
onPress: phoneForm.handleSubmit(handleSendOtp)
|
|
874
861
|
}
|
|
875
862
|
)
|
|
876
863
|
] }) : /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
@@ -905,13 +892,18 @@ function PhoneLoginScreen({ onSuccess }) {
|
|
|
905
892
|
label: L.VERIFY,
|
|
906
893
|
loading: verifyOtpMutation.isPending,
|
|
907
894
|
loadingLabel: L.VERIFYING,
|
|
908
|
-
onPress: otpForm.handleSubmit(handleVerifyOtp)
|
|
909
|
-
disabled: !otpForm.formState.isValid
|
|
895
|
+
onPress: otpForm.handleSubmit(handleVerifyOtp)
|
|
910
896
|
}
|
|
911
897
|
),
|
|
912
898
|
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", justifyContent: "space-between" }, children: [
|
|
913
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setState({ step: 1 }),
|
|
914
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
899
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setState({ step: 1 }), label: L.GO_BACK }) }),
|
|
900
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
901
|
+
Button,
|
|
902
|
+
{
|
|
903
|
+
onPress: handleResend,
|
|
904
|
+
label: cooldown > 0 ? `${L.RESEND} (${cooldown}s)` : L.RESEND
|
|
905
|
+
}
|
|
906
|
+
) })
|
|
915
907
|
] })
|
|
916
908
|
] })
|
|
917
909
|
] }) });
|
|
@@ -922,7 +914,9 @@ function useResetPassword() {
|
|
|
922
914
|
mutationFn: async (data) => {
|
|
923
915
|
const response = await client.resetPassword(data);
|
|
924
916
|
if (response.error) {
|
|
925
|
-
throw new
|
|
917
|
+
throw new RequestFailedError({
|
|
918
|
+
message: response.error.message ?? "Password reset failed"
|
|
919
|
+
});
|
|
926
920
|
}
|
|
927
921
|
return true;
|
|
928
922
|
}
|
|
@@ -959,13 +953,13 @@ function ResetPasswordScreen({ onSuccess, token }) {
|
|
|
959
953
|
return /* @__PURE__ */ jsx(AuthContainer, { children: /* @__PURE__ */ jsxs(View, { style: { gap: 24 }, children: [
|
|
960
954
|
/* @__PURE__ */ jsx(View, { style: { gap: 4, alignItems: "center" }, children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 24, fontWeight: "700", color: "#1f2937" }, children: L.RESET_PASSWORD }) }),
|
|
961
955
|
/* @__PURE__ */ jsx(ErrorFeedback, { message: L.REQUEST_FAILED }),
|
|
962
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
956
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
963
957
|
] }) });
|
|
964
958
|
}
|
|
965
959
|
if (resetMutation.isSuccess) {
|
|
966
960
|
return /* @__PURE__ */ jsx(AuthContainer, { children: /* @__PURE__ */ jsxs(View, { style: { gap: 24 }, children: [
|
|
967
961
|
/* @__PURE__ */ jsx(SuccessFeedback, { message: L.RESET_PASSWORD_SUCCESS }),
|
|
968
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.replace("/login"),
|
|
962
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.replace("/login"), label: L.SIGN_IN }) }) })
|
|
969
963
|
] }) });
|
|
970
964
|
}
|
|
971
965
|
return /* @__PURE__ */ jsx(AuthContainer, { children: /* @__PURE__ */ jsxs(View, { style: { gap: 24 }, children: [
|
|
@@ -1017,11 +1011,10 @@ function ResetPasswordScreen({ onSuccess, token }) {
|
|
|
1017
1011
|
label: L.RESET_PASSWORD_ACTION,
|
|
1018
1012
|
loading: resetMutation.isPending,
|
|
1019
1013
|
loadingLabel: L.LOADING,
|
|
1020
|
-
onPress: form.handleSubmit(handleSubmit)
|
|
1021
|
-
disabled: !form.formState.isValid
|
|
1014
|
+
onPress: form.handleSubmit(handleSubmit)
|
|
1022
1015
|
}
|
|
1023
1016
|
),
|
|
1024
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
1017
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
1025
1018
|
] }) });
|
|
1026
1019
|
}
|
|
1027
1020
|
function useSignup() {
|
|
@@ -1031,16 +1024,14 @@ function useSignup() {
|
|
|
1031
1024
|
mutationFn: async (data) => {
|
|
1032
1025
|
const response = await client.signUp.email(data);
|
|
1033
1026
|
if (response.error) {
|
|
1034
|
-
throw new
|
|
1027
|
+
throw new RequestFailedError({ message: response.error.message ?? "Signup failed" });
|
|
1035
1028
|
}
|
|
1036
1029
|
return response.data;
|
|
1037
1030
|
},
|
|
1038
1031
|
onSuccess: (data) => {
|
|
1039
1032
|
if (data?.user) {
|
|
1040
1033
|
queryClient.setQueryData(authKeys.user(), data.user);
|
|
1041
|
-
|
|
1042
|
-
if (data?.session) {
|
|
1043
|
-
queryClient.setQueryData(authKeys.session(), data.session);
|
|
1034
|
+
queryClient.invalidateQueries({ queryKey: authKeys.session() });
|
|
1044
1035
|
}
|
|
1045
1036
|
}
|
|
1046
1037
|
});
|
|
@@ -1147,17 +1138,16 @@ function SignupScreen({ onSuccess }) {
|
|
|
1147
1138
|
label: L.SIGN_UP_ACTION,
|
|
1148
1139
|
loading: signupMutation.isPending,
|
|
1149
1140
|
loadingLabel: L.SIGNING_UP,
|
|
1150
|
-
onPress: form.handleSubmit(onSubmit)
|
|
1151
|
-
disabled: !form.formState.isValid
|
|
1141
|
+
onPress: form.handleSubmit(onSubmit)
|
|
1152
1142
|
}
|
|
1153
1143
|
),
|
|
1154
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.push("/login"),
|
|
1144
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.push("/login"), label: L.ALREADY_HAVE_AN_ACCOUNT }) }) })
|
|
1155
1145
|
] }) });
|
|
1156
1146
|
}
|
|
1157
1147
|
function TwoFactorScreen({ onSuccess }) {
|
|
1158
1148
|
const { localization: L } = useAuthLocalization();
|
|
1159
1149
|
const router = useRouter();
|
|
1160
|
-
|
|
1150
|
+
useAuthClient();
|
|
1161
1151
|
const [mode, setMode] = useState("totp");
|
|
1162
1152
|
const totpForm = useForm({
|
|
1163
1153
|
resolver: zodResolver(totpSchema),
|
|
@@ -1170,12 +1160,8 @@ function TwoFactorScreen({ onSuccess }) {
|
|
|
1170
1160
|
mode: "onChange"
|
|
1171
1161
|
});
|
|
1172
1162
|
const verifyMutation = useMutation({
|
|
1173
|
-
mutationFn: async (
|
|
1174
|
-
|
|
1175
|
-
if (response?.error) {
|
|
1176
|
-
throw new Error(response.error.message ?? "Invalid code");
|
|
1177
|
-
}
|
|
1178
|
-
return response?.data;
|
|
1163
|
+
mutationFn: async (_data) => {
|
|
1164
|
+
throw new NotImplementedError({ feature: "Two-factor verification" });
|
|
1179
1165
|
}
|
|
1180
1166
|
});
|
|
1181
1167
|
const handleTotpSubmit = useCallback(
|
|
@@ -1218,22 +1204,8 @@ function TwoFactorScreen({ onSuccess }) {
|
|
|
1218
1204
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#6b7280" }, children: L.TWO_FACTOR_DESCRIPTION })
|
|
1219
1205
|
] }),
|
|
1220
1206
|
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", gap: 8 }, children: [
|
|
1221
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
1222
|
-
|
|
1223
|
-
{
|
|
1224
|
-
onPress: () => setMode("totp"),
|
|
1225
|
-
variant: mode === "totp" ? "borderedProminent" : "bordered",
|
|
1226
|
-
children: L.USE_AUTHENTICATOR
|
|
1227
|
-
}
|
|
1228
|
-
) }),
|
|
1229
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
1230
|
-
Button,
|
|
1231
|
-
{
|
|
1232
|
-
onPress: () => setMode("backup"),
|
|
1233
|
-
variant: mode === "backup" ? "borderedProminent" : "bordered",
|
|
1234
|
-
children: L.USE_BACKUP_CODE
|
|
1235
|
-
}
|
|
1236
|
-
) })
|
|
1207
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setMode("totp"), label: L.USE_AUTHENTICATOR }) }),
|
|
1208
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setMode("backup"), label: L.USE_BACKUP_CODE }) })
|
|
1237
1209
|
] }),
|
|
1238
1210
|
mode === "totp" ? /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
1239
1211
|
/* @__PURE__ */ jsx(
|
|
@@ -1264,8 +1236,7 @@ function TwoFactorScreen({ onSuccess }) {
|
|
|
1264
1236
|
label: L.TWO_FACTOR_ACTION,
|
|
1265
1237
|
loading: verifyMutation.isPending,
|
|
1266
1238
|
loadingLabel: L.VERIFYING,
|
|
1267
|
-
onPress: totpForm.handleSubmit(handleTotpSubmit)
|
|
1268
|
-
disabled: !totpForm.formState.isValid
|
|
1239
|
+
onPress: totpForm.handleSubmit(handleTotpSubmit)
|
|
1269
1240
|
}
|
|
1270
1241
|
)
|
|
1271
1242
|
] }) : /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
@@ -1296,8 +1267,7 @@ function TwoFactorScreen({ onSuccess }) {
|
|
|
1296
1267
|
label: L.TWO_FACTOR_ACTION,
|
|
1297
1268
|
loading: verifyMutation.isPending,
|
|
1298
1269
|
loadingLabel: L.VERIFYING,
|
|
1299
|
-
onPress: backupForm.handleSubmit(handleBackupSubmit)
|
|
1300
|
-
disabled: !backupForm.formState.isValid
|
|
1270
|
+
onPress: backupForm.handleSubmit(handleBackupSubmit)
|
|
1301
1271
|
}
|
|
1302
1272
|
)
|
|
1303
1273
|
] })
|
|
@@ -1370,7 +1340,7 @@ function ChangeEmailScreen({ onSuccess, redirectUrl }) {
|
|
|
1370
1340
|
callbackURL: redirectUrl
|
|
1371
1341
|
});
|
|
1372
1342
|
if (response.error) {
|
|
1373
|
-
throw new
|
|
1343
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
1374
1344
|
}
|
|
1375
1345
|
return response.data;
|
|
1376
1346
|
}
|
|
@@ -1435,15 +1405,20 @@ function ChangeEmailScreen({ onSuccess, redirectUrl }) {
|
|
|
1435
1405
|
label: L.CHANGE_EMAIL,
|
|
1436
1406
|
loading: sendMutation.isPending,
|
|
1437
1407
|
loadingLabel: L.SENDING,
|
|
1438
|
-
onPress: form.handleSubmit(handleSubmit)
|
|
1439
|
-
disabled: !form.formState.isValid
|
|
1408
|
+
onPress: form.handleSubmit(handleSubmit)
|
|
1440
1409
|
}
|
|
1441
1410
|
),
|
|
1442
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
1411
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
1443
1412
|
] }) : /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
1444
1413
|
/* @__PURE__ */ jsx(SuccessFeedback, { message: L.CHANGE_EMAIL_SENT }),
|
|
1445
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
1446
|
-
|
|
1414
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
1415
|
+
Button,
|
|
1416
|
+
{
|
|
1417
|
+
onPress: handleResend,
|
|
1418
|
+
label: cooldown > 0 ? `${L.RESEND} (${cooldown}s)` : L.RESEND
|
|
1419
|
+
}
|
|
1420
|
+
) }),
|
|
1421
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center" }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
1447
1422
|
] })
|
|
1448
1423
|
] }) });
|
|
1449
1424
|
}
|
|
@@ -1464,7 +1439,7 @@ function ChangePasswordScreen({ onSuccess }) {
|
|
|
1464
1439
|
newPassword: data.newPassword
|
|
1465
1440
|
});
|
|
1466
1441
|
if (response.error) {
|
|
1467
|
-
throw new
|
|
1442
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
1468
1443
|
}
|
|
1469
1444
|
return response.data;
|
|
1470
1445
|
}
|
|
@@ -1473,9 +1448,8 @@ function ChangePasswordScreen({ onSuccess }) {
|
|
|
1473
1448
|
(data) => {
|
|
1474
1449
|
changeMutation.mutate(data, {
|
|
1475
1450
|
onSuccess: () => {
|
|
1451
|
+
form.reset();
|
|
1476
1452
|
setShowSuccess(true);
|
|
1477
|
-
form.handleSubmit(() => {
|
|
1478
|
-
})();
|
|
1479
1453
|
setTimeout(() => setShowSuccess(false), 3e3);
|
|
1480
1454
|
onSuccess?.();
|
|
1481
1455
|
}
|
|
@@ -1553,11 +1527,10 @@ function ChangePasswordScreen({ onSuccess }) {
|
|
|
1553
1527
|
label: L.CHANGE_PASSWORD,
|
|
1554
1528
|
loading: changeMutation.isPending,
|
|
1555
1529
|
loadingLabel: L.CHANGING_PASSWORD,
|
|
1556
|
-
onPress: form.handleSubmit(handleSubmit)
|
|
1557
|
-
disabled: !form.formState.isValid
|
|
1530
|
+
onPress: form.handleSubmit(handleSubmit)
|
|
1558
1531
|
}
|
|
1559
1532
|
),
|
|
1560
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
1533
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
1561
1534
|
] })
|
|
1562
1535
|
] }) });
|
|
1563
1536
|
}
|
|
@@ -1575,7 +1548,7 @@ function DeleteAccountScreen({
|
|
|
1575
1548
|
mutationFn: async (pw) => {
|
|
1576
1549
|
const response = await client.deleteUser({ password: pw });
|
|
1577
1550
|
if (response.error) {
|
|
1578
|
-
throw new
|
|
1551
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
1579
1552
|
}
|
|
1580
1553
|
return response.data;
|
|
1581
1554
|
}
|
|
@@ -1643,27 +1616,25 @@ function DeleteAccountScreen({
|
|
|
1643
1616
|
Button,
|
|
1644
1617
|
{
|
|
1645
1618
|
onPress: handleDelete,
|
|
1646
|
-
|
|
1647
|
-
variant: "borderedProminent",
|
|
1648
|
-
children: deleteMutation.isPending ? L.DELETING_ACCOUNT : L.DELETE
|
|
1619
|
+
label: deleteMutation.isPending ? L.DELETING_ACCOUNT : L.DELETE
|
|
1649
1620
|
}
|
|
1650
1621
|
) }),
|
|
1651
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
1622
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
1652
1623
|
] })
|
|
1653
1624
|
] }) });
|
|
1654
1625
|
}
|
|
1655
|
-
function formatLastActive(dateStr) {
|
|
1626
|
+
function formatLastActive(dateStr, L) {
|
|
1656
1627
|
if (!dateStr) return "";
|
|
1657
1628
|
const date = new Date(dateStr);
|
|
1658
1629
|
const now = /* @__PURE__ */ new Date();
|
|
1659
1630
|
const diffMs = now.getTime() - date.getTime();
|
|
1660
1631
|
const diffMin = Math.floor(diffMs / 6e4);
|
|
1661
|
-
if (diffMin < 1) return
|
|
1662
|
-
if (diffMin < 60) return
|
|
1632
|
+
if (diffMin < 1) return L.JUST_NOW;
|
|
1633
|
+
if (diffMin < 60) return L.MINUTES_AGO.replace("{{n}}", String(diffMin));
|
|
1663
1634
|
const diffHrs = Math.floor(diffMin / 60);
|
|
1664
|
-
if (diffHrs < 24) return
|
|
1635
|
+
if (diffHrs < 24) return L.HOURS_AGO.replace("{{n}}", String(diffHrs));
|
|
1665
1636
|
const diffDays = Math.floor(diffHrs / 24);
|
|
1666
|
-
return
|
|
1637
|
+
return L.DAYS_AGO.replace("{{n}}", String(diffDays));
|
|
1667
1638
|
}
|
|
1668
1639
|
function SessionsScreen({ onSuccess }) {
|
|
1669
1640
|
const { localization: L } = useAuthLocalization();
|
|
@@ -1675,7 +1646,7 @@ function SessionsScreen({ onSuccess }) {
|
|
|
1675
1646
|
queryFn: async () => {
|
|
1676
1647
|
const response = await client.listSessions();
|
|
1677
1648
|
if (response.error) {
|
|
1678
|
-
throw new
|
|
1649
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
1679
1650
|
}
|
|
1680
1651
|
return response.data ?? [];
|
|
1681
1652
|
}
|
|
@@ -1684,7 +1655,7 @@ function SessionsScreen({ onSuccess }) {
|
|
|
1684
1655
|
mutationFn: async (sessionToken) => {
|
|
1685
1656
|
const response = await client.revokeSession({ token: sessionToken });
|
|
1686
1657
|
if (response.error) {
|
|
1687
|
-
throw new
|
|
1658
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
1688
1659
|
}
|
|
1689
1660
|
return response.data;
|
|
1690
1661
|
},
|
|
@@ -1696,7 +1667,7 @@ function SessionsScreen({ onSuccess }) {
|
|
|
1696
1667
|
mutationFn: async () => {
|
|
1697
1668
|
const response = await client.revokeOtherSessions();
|
|
1698
1669
|
if (response.error) {
|
|
1699
|
-
throw new
|
|
1670
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
1700
1671
|
}
|
|
1701
1672
|
return response.data;
|
|
1702
1673
|
},
|
|
@@ -1729,7 +1700,7 @@ function SessionsScreen({ onSuccess }) {
|
|
|
1729
1700
|
]);
|
|
1730
1701
|
}, [L, revokeAllMutation]);
|
|
1731
1702
|
const sessions = sessionsQuery.data ?? [];
|
|
1732
|
-
|
|
1703
|
+
revokeMutation.isPending || revokeAllMutation.isPending;
|
|
1733
1704
|
return /* @__PURE__ */ jsx(AuthContainer, { children: /* @__PURE__ */ jsxs(View, { style: { gap: 24, flex: 1 }, children: [
|
|
1734
1705
|
/* @__PURE__ */ jsxs(View, { style: { gap: 4, alignItems: "center" }, children: [
|
|
1735
1706
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 24, fontWeight: "700", color: "#1f2937" }, children: L.SESSIONS }),
|
|
@@ -1766,69 +1737,54 @@ function SessionsScreen({ onSuccess }) {
|
|
|
1766
1737
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 16, fontWeight: "600", color: "#1f2937" }, children: item.userAgent ?? L.UNKNOWN }),
|
|
1767
1738
|
/* @__PURE__ */ jsxs(Text, { style: { fontSize: 12, color: "#9ca3af" }, children: [
|
|
1768
1739
|
item.ipAddress ?? "",
|
|
1769
|
-
item.updatedAt ? ` \xB7 ${formatLastActive(item.updatedAt)}` : ""
|
|
1740
|
+
item.updatedAt ? ` \xB7 ${formatLastActive(item.updatedAt, L)}` : ""
|
|
1770
1741
|
] }),
|
|
1771
1742
|
item.current && /* @__PURE__ */ jsx(Text, { style: { fontSize: 11, fontWeight: "600", color: "#16a34a" }, children: L.CURRENT_DEVICE })
|
|
1772
1743
|
] }),
|
|
1773
1744
|
!item.current && /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
1774
1745
|
Button,
|
|
1775
1746
|
{
|
|
1776
|
-
onPress: () => handleRevoke(item.token),
|
|
1777
|
-
|
|
1778
|
-
variant: "bordered",
|
|
1779
|
-
children: L.REVOKE_SESSION
|
|
1747
|
+
onPress: () => handleRevoke(item.token ?? item.id),
|
|
1748
|
+
label: L.REVOKE_SESSION
|
|
1780
1749
|
}
|
|
1781
1750
|
) })
|
|
1782
1751
|
]
|
|
1783
1752
|
}
|
|
1784
1753
|
),
|
|
1785
|
-
ListFooterComponent: sessions.length > 1 ? /* @__PURE__ */ jsx(View, { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleRevokeAll,
|
|
1754
|
+
ListFooterComponent: sessions.length > 1 ? /* @__PURE__ */ jsx(View, { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleRevokeAll, label: L.REVOKE_ALL_OTHER_SESSIONS }) }) }) : null
|
|
1786
1755
|
}
|
|
1787
1756
|
),
|
|
1788
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
1757
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
1789
1758
|
] }) });
|
|
1790
1759
|
}
|
|
1791
1760
|
function TwoFactorSettingsScreen({ onSuccess }) {
|
|
1792
1761
|
const { localization: L } = useAuthLocalization();
|
|
1793
1762
|
const router = useRouter();
|
|
1794
|
-
const client = useAuthClient();
|
|
1795
1763
|
const { data: user } = useUser();
|
|
1796
1764
|
const [state, setState] = useState({ step: "idle" });
|
|
1797
1765
|
const [totpCode, setTotpCode] = useState("");
|
|
1798
1766
|
const [password, setPassword] = useState("");
|
|
1799
1767
|
const [copied, setCopied] = useState(false);
|
|
1800
1768
|
const setupMutation = useMutation({
|
|
1801
|
-
mutationFn: async () => {
|
|
1802
|
-
|
|
1803
|
-
password: "setup"
|
|
1804
|
-
});
|
|
1805
|
-
if (response.error) {
|
|
1806
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
1807
|
-
}
|
|
1808
|
-
return response.data;
|
|
1769
|
+
mutationFn: async (_pw) => {
|
|
1770
|
+
throw new NotImplementedError({ feature: "Two-factor setup" });
|
|
1809
1771
|
}
|
|
1810
1772
|
});
|
|
1811
1773
|
const verifyMutation = useMutation({
|
|
1812
|
-
mutationFn: async (
|
|
1813
|
-
|
|
1814
|
-
if (response.error) {
|
|
1815
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
1816
|
-
}
|
|
1817
|
-
return response.data;
|
|
1774
|
+
mutationFn: async (_code) => {
|
|
1775
|
+
throw new NotImplementedError({ feature: "Two-factor verification" });
|
|
1818
1776
|
}
|
|
1819
1777
|
});
|
|
1820
1778
|
const disableMutation = useMutation({
|
|
1821
|
-
mutationFn: async (
|
|
1822
|
-
|
|
1823
|
-
if (response.error) {
|
|
1824
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
1825
|
-
}
|
|
1826
|
-
return response.data;
|
|
1779
|
+
mutationFn: async (_pw) => {
|
|
1780
|
+
throw new NotImplementedError({ feature: "Two-factor disable" });
|
|
1827
1781
|
}
|
|
1828
1782
|
});
|
|
1829
1783
|
const handleEnable = useCallback(() => {
|
|
1830
|
-
|
|
1784
|
+
if (!password) return;
|
|
1785
|
+
setupMutation.mutate(password, {
|
|
1831
1786
|
onSuccess: (data) => {
|
|
1787
|
+
setPassword("");
|
|
1832
1788
|
setState({
|
|
1833
1789
|
step: "setup",
|
|
1834
1790
|
totpURI: data?.totpURI ?? "",
|
|
@@ -1836,7 +1792,7 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
1836
1792
|
});
|
|
1837
1793
|
}
|
|
1838
1794
|
});
|
|
1839
|
-
}, [setupMutation]);
|
|
1795
|
+
}, [password, setupMutation]);
|
|
1840
1796
|
const handleVerify = useCallback(() => {
|
|
1841
1797
|
if (totpCode.length !== 6) return;
|
|
1842
1798
|
verifyMutation.mutate(totpCode, {
|
|
@@ -1876,7 +1832,7 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
1876
1832
|
}
|
|
1877
1833
|
});
|
|
1878
1834
|
}, [password, disableMutation, onSuccess]);
|
|
1879
|
-
const isEnabled = user?.twoFactorEnabled
|
|
1835
|
+
const isEnabled = user?.twoFactorEnabled === true;
|
|
1880
1836
|
const isPending = setupMutation.isPending || verifyMutation.isPending || disableMutation.isPending;
|
|
1881
1837
|
const error = setupMutation.error ?? verifyMutation.error ?? disableMutation.error;
|
|
1882
1838
|
return /* @__PURE__ */ jsx(AuthContainer, { children: /* @__PURE__ */ jsxs(ScrollView, { contentContainerStyle: { gap: 24 }, children: [
|
|
@@ -1890,11 +1846,61 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
1890
1846
|
": ",
|
|
1891
1847
|
L.DISABLED
|
|
1892
1848
|
] }),
|
|
1893
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true,
|
|
1849
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, style: { opacity: isPending ? 0.5 : 1 }, children: /* @__PURE__ */ jsx(
|
|
1850
|
+
Button,
|
|
1851
|
+
{
|
|
1852
|
+
onPress: () => {
|
|
1853
|
+
if (isPending) return;
|
|
1854
|
+
setState({ step: "confirm-password" });
|
|
1855
|
+
},
|
|
1856
|
+
label: L.ENABLE_TWO_FACTOR
|
|
1857
|
+
}
|
|
1858
|
+
) })
|
|
1859
|
+
] }),
|
|
1860
|
+
state.step === "confirm-password" && /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
1861
|
+
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#374151", textAlign: "center" }, children: L.PASSWORD }),
|
|
1862
|
+
/* @__PURE__ */ jsx(
|
|
1863
|
+
TextInput,
|
|
1864
|
+
{
|
|
1865
|
+
value: password,
|
|
1866
|
+
onChangeText: setPassword,
|
|
1867
|
+
placeholder: L.PASSWORD,
|
|
1868
|
+
secureTextEntry: true,
|
|
1869
|
+
editable: !setupMutation.isPending,
|
|
1870
|
+
style: {
|
|
1871
|
+
borderWidth: 1,
|
|
1872
|
+
borderColor: "#d1d5db",
|
|
1873
|
+
borderRadius: 8,
|
|
1874
|
+
padding: 12,
|
|
1875
|
+
fontSize: 16
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
),
|
|
1879
|
+
setupMutation.isPending && /* @__PURE__ */ jsx(ActivityIndicator, { size: "small" }),
|
|
1880
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, style: { opacity: setupMutation.isPending ? 0.5 : 1 }, children: /* @__PURE__ */ jsx(
|
|
1881
|
+
Button,
|
|
1882
|
+
{
|
|
1883
|
+
onPress: () => {
|
|
1884
|
+
if (setupMutation.isPending) return;
|
|
1885
|
+
handleEnable();
|
|
1886
|
+
},
|
|
1887
|
+
label: setupMutation.isPending ? L.LOADING : L.ENABLE_TWO_FACTOR
|
|
1888
|
+
}
|
|
1889
|
+
) }),
|
|
1890
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
1891
|
+
Button,
|
|
1892
|
+
{
|
|
1893
|
+
onPress: () => {
|
|
1894
|
+
setState({ step: "idle" });
|
|
1895
|
+
setPassword("");
|
|
1896
|
+
},
|
|
1897
|
+
label: L.CANCEL
|
|
1898
|
+
}
|
|
1899
|
+
) })
|
|
1894
1900
|
] }),
|
|
1895
1901
|
state.step === "idle" && isEnabled && /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
1896
1902
|
/* @__PURE__ */ jsx(SuccessFeedback, { message: `${L.STATUS}: ${L.ENABLED}` }),
|
|
1897
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleDisable,
|
|
1903
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleDisable, label: L.DISABLE_TWO_FACTOR }) })
|
|
1898
1904
|
] }),
|
|
1899
1905
|
state.step === "setup" && /* @__PURE__ */ jsxs(View, { style: { gap: 16, alignItems: "center" }, children: [
|
|
1900
1906
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#374151", textAlign: "center" }, children: L.SCAN_QR_CODE }),
|
|
@@ -1909,7 +1915,7 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
1909
1915
|
alignItems: "center",
|
|
1910
1916
|
borderRadius: 8
|
|
1911
1917
|
},
|
|
1912
|
-
children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: "#9ca3af" }, children:
|
|
1918
|
+
children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: "#9ca3af" }, children: L.SCAN_QR_CODE })
|
|
1913
1919
|
}
|
|
1914
1920
|
),
|
|
1915
1921
|
/* @__PURE__ */ jsxs(View, { style: { gap: 4, alignItems: "center" }, children: [
|
|
@@ -1936,8 +1942,7 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
1936
1942
|
totpURI: state.totpURI,
|
|
1937
1943
|
secret: state.secret
|
|
1938
1944
|
}),
|
|
1939
|
-
|
|
1940
|
-
children: L.NEXT
|
|
1945
|
+
label: L.NEXT
|
|
1941
1946
|
}
|
|
1942
1947
|
) })
|
|
1943
1948
|
] }),
|
|
@@ -1965,15 +1970,7 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
1965
1970
|
}
|
|
1966
1971
|
),
|
|
1967
1972
|
verifyMutation.isPending && /* @__PURE__ */ jsx(ActivityIndicator, { size: "small" }),
|
|
1968
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
1969
|
-
Button,
|
|
1970
|
-
{
|
|
1971
|
-
onPress: handleVerify,
|
|
1972
|
-
disabled: totpCode.length !== 6 || verifyMutation.isPending,
|
|
1973
|
-
variant: "borderedProminent",
|
|
1974
|
-
children: L.TWO_FACTOR_ACTION
|
|
1975
|
-
}
|
|
1976
|
-
) })
|
|
1973
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleVerify, label: L.TWO_FACTOR_ACTION }) })
|
|
1977
1974
|
] }),
|
|
1978
1975
|
state.step === "backup" && /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
1979
1976
|
/* @__PURE__ */ jsx(SuccessFeedback, { message: L.TWO_FACTOR_SETUP_SUCCESS }),
|
|
@@ -2002,8 +1999,14 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
2002
1999
|
))
|
|
2003
2000
|
}
|
|
2004
2001
|
),
|
|
2005
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
2006
|
-
|
|
2002
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
2003
|
+
Button,
|
|
2004
|
+
{
|
|
2005
|
+
onPress: handleCopyBackupCodes,
|
|
2006
|
+
label: copied ? L.BACKUP_CODES_COPIED : L.COPY_BACKUP_CODES
|
|
2007
|
+
}
|
|
2008
|
+
) }),
|
|
2009
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setState({ step: "idle" }), label: L.FINISH }) })
|
|
2007
2010
|
] }),
|
|
2008
2011
|
state.step === "disabling" && /* @__PURE__ */ jsxs(View, { style: { gap: 16 }, children: [
|
|
2009
2012
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#374151", textAlign: "center" }, children: L.CONFIRM_DISABLE_2FA }),
|
|
@@ -2025,19 +2028,11 @@ function TwoFactorSettingsScreen({ onSuccess }) {
|
|
|
2025
2028
|
}
|
|
2026
2029
|
),
|
|
2027
2030
|
disableMutation.isPending && /* @__PURE__ */ jsx(ActivityIndicator, { size: "small" }),
|
|
2028
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
2029
|
-
|
|
2030
|
-
{
|
|
2031
|
-
onPress: handleConfirmDisable,
|
|
2032
|
-
disabled: !password || disableMutation.isPending,
|
|
2033
|
-
variant: "borderedProminent",
|
|
2034
|
-
children: L.DISABLE_TWO_FACTOR
|
|
2035
|
-
}
|
|
2036
|
-
) }),
|
|
2037
|
-
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setState({ step: "idle" }), variant: "borderless", children: L.CANCEL }) })
|
|
2031
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleConfirmDisable, label: L.DISABLE_TWO_FACTOR }) }),
|
|
2032
|
+
/* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => setState({ step: "idle" }), label: L.CANCEL }) })
|
|
2038
2033
|
] }),
|
|
2039
2034
|
error && /* @__PURE__ */ jsx(ErrorFeedback, { message: error.message }),
|
|
2040
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
2035
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
2041
2036
|
] }) });
|
|
2042
2037
|
}
|
|
2043
2038
|
var AVATAR_SIZE = 120;
|
|
@@ -2056,13 +2051,12 @@ function UpdateAvatarScreen({ onSuccess }) {
|
|
|
2056
2051
|
const queryClient = useQueryClient();
|
|
2057
2052
|
const { data: user } = useUser();
|
|
2058
2053
|
const [selectedUri, setSelectedUri] = useState(null);
|
|
2059
|
-
const [
|
|
2060
|
-
const [successMessage, setSuccessMessage] = useState("");
|
|
2054
|
+
const [successMessage, setSuccessMessage] = useState(null);
|
|
2061
2055
|
const uploadMutation = useMutation({
|
|
2062
2056
|
mutationFn: async (imageUri) => {
|
|
2063
2057
|
const response = await client.updateUser({ image: imageUri });
|
|
2064
2058
|
if (response.error) {
|
|
2065
|
-
throw new
|
|
2059
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
2066
2060
|
}
|
|
2067
2061
|
return response.data;
|
|
2068
2062
|
}
|
|
@@ -2071,43 +2065,57 @@ function UpdateAvatarScreen({ onSuccess }) {
|
|
|
2071
2065
|
mutationFn: async () => {
|
|
2072
2066
|
const response = await client.updateUser({ image: "" });
|
|
2073
2067
|
if (response.error) {
|
|
2074
|
-
throw new
|
|
2068
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
2075
2069
|
}
|
|
2076
2070
|
return response.data;
|
|
2077
2071
|
}
|
|
2078
2072
|
});
|
|
2073
|
+
const handlePickImage = useCallback(async () => {
|
|
2074
|
+
const result = await ImagePicker.launchImageLibraryAsync({
|
|
2075
|
+
mediaTypes: "images",
|
|
2076
|
+
allowsEditing: true,
|
|
2077
|
+
aspect: [1, 1],
|
|
2078
|
+
quality: 0.8
|
|
2079
|
+
});
|
|
2080
|
+
if (!result.canceled && result.assets[0]) {
|
|
2081
|
+
setSelectedUri(result.assets[0].uri);
|
|
2082
|
+
}
|
|
2083
|
+
}, []);
|
|
2084
|
+
const handleDelete = useCallback(() => {
|
|
2085
|
+
deleteMutation.mutate(void 0, {
|
|
2086
|
+
onSuccess: () => {
|
|
2087
|
+
queryClient.invalidateQueries({ queryKey: authKeys.user() });
|
|
2088
|
+
setSuccessMessage(L.AVATAR_DELETED);
|
|
2089
|
+
setTimeout(() => setSuccessMessage(null), 3e3);
|
|
2090
|
+
onSuccess?.();
|
|
2091
|
+
}
|
|
2092
|
+
});
|
|
2093
|
+
}, [deleteMutation, queryClient, L, onSuccess]);
|
|
2079
2094
|
const showActionSheet = useCallback(() => {
|
|
2080
|
-
|
|
2081
|
-
{
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
},
|
|
2086
|
-
async (buttonIndex) => {
|
|
2087
|
-
if (buttonIndex === 1) {
|
|
2088
|
-
const result = await ImagePicker.launchImageLibraryAsync({
|
|
2089
|
-
mediaTypes: "images",
|
|
2090
|
-
allowsEditing: true,
|
|
2091
|
-
aspect: [1, 1],
|
|
2092
|
-
quality: 0.8
|
|
2093
|
-
});
|
|
2094
|
-
if (!result.canceled && result.assets[0]) {
|
|
2095
|
-
setSelectedUri(result.assets[0].uri);
|
|
2096
|
-
}
|
|
2097
|
-
} else if (buttonIndex === 2) {
|
|
2098
|
-
deleteMutation.mutate(void 0, {
|
|
2099
|
-
onSuccess: () => {
|
|
2100
|
-
queryClient.invalidateQueries({ queryKey: authKeys.user() });
|
|
2101
|
-
setSuccessMessage(L.AVATAR_DELETED);
|
|
2102
|
-
setShowSuccess(true);
|
|
2103
|
-
setTimeout(() => setShowSuccess(false), 3e3);
|
|
2104
|
-
onSuccess?.();
|
|
2105
|
-
}
|
|
2106
|
-
});
|
|
2107
|
-
}
|
|
2095
|
+
const handleAction = (buttonIndex) => {
|
|
2096
|
+
if (buttonIndex === 1) {
|
|
2097
|
+
handlePickImage();
|
|
2098
|
+
} else if (buttonIndex === 2) {
|
|
2099
|
+
handleDelete();
|
|
2108
2100
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2101
|
+
};
|
|
2102
|
+
if (Platform.OS === "ios") {
|
|
2103
|
+
ActionSheetIOS.showActionSheetWithOptions(
|
|
2104
|
+
{
|
|
2105
|
+
options: [L.CANCEL, L.CLICK_TO_UPLOAD, L.DELETE_AVATAR],
|
|
2106
|
+
cancelButtonIndex: 0,
|
|
2107
|
+
destructiveButtonIndex: 2
|
|
2108
|
+
},
|
|
2109
|
+
handleAction
|
|
2110
|
+
);
|
|
2111
|
+
} else {
|
|
2112
|
+
Alert.alert(L.AVATAR, void 0, [
|
|
2113
|
+
{ text: L.CLICK_TO_UPLOAD, onPress: () => handleAction(1) },
|
|
2114
|
+
{ text: L.DELETE_AVATAR, style: "destructive", onPress: () => handleAction(2) },
|
|
2115
|
+
{ text: L.CANCEL, style: "cancel" }
|
|
2116
|
+
]);
|
|
2117
|
+
}
|
|
2118
|
+
}, [L, handlePickImage, handleDelete]);
|
|
2111
2119
|
const handleUpload = useCallback(() => {
|
|
2112
2120
|
if (!selectedUri) return;
|
|
2113
2121
|
uploadMutation.mutate(selectedUri, {
|
|
@@ -2115,8 +2123,7 @@ function UpdateAvatarScreen({ onSuccess }) {
|
|
|
2115
2123
|
queryClient.invalidateQueries({ queryKey: authKeys.user() });
|
|
2116
2124
|
setSelectedUri(null);
|
|
2117
2125
|
setSuccessMessage(L.AVATAR_UPDATED);
|
|
2118
|
-
|
|
2119
|
-
setTimeout(() => setShowSuccess(false), 3e3);
|
|
2126
|
+
setTimeout(() => setSuccessMessage(null), 3e3);
|
|
2120
2127
|
onSuccess?.();
|
|
2121
2128
|
}
|
|
2122
2129
|
});
|
|
@@ -2129,7 +2136,7 @@ function UpdateAvatarScreen({ onSuccess }) {
|
|
|
2129
2136
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 24, fontWeight: "700", color: "#1f2937" }, children: L.AVATAR }),
|
|
2130
2137
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#6b7280" }, children: L.AVATAR_DESCRIPTION })
|
|
2131
2138
|
] }),
|
|
2132
|
-
|
|
2139
|
+
successMessage ? /* @__PURE__ */ jsx(SuccessFeedback, { message: successMessage }) : /* @__PURE__ */ jsxs(View, { style: { gap: 16, alignItems: "center" }, children: [
|
|
2133
2140
|
/* @__PURE__ */ jsx(Pressable, { onPress: showActionSheet, disabled: isPending, children: displayUri ? /* @__PURE__ */ jsx(
|
|
2134
2141
|
Image,
|
|
2135
2142
|
{
|
|
@@ -2155,9 +2162,9 @@ function UpdateAvatarScreen({ onSuccess }) {
|
|
|
2155
2162
|
}
|
|
2156
2163
|
) }),
|
|
2157
2164
|
isPending && /* @__PURE__ */ jsx(ActivityIndicator, { size: "small" }),
|
|
2158
|
-
selectedUri && /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleUpload,
|
|
2165
|
+
selectedUri && /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: handleUpload, label: L.UPLOAD_AVATAR }) }),
|
|
2159
2166
|
error && /* @__PURE__ */ jsx(ErrorFeedback, { message: error.message }),
|
|
2160
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
2167
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
2161
2168
|
] })
|
|
2162
2169
|
] }) });
|
|
2163
2170
|
}
|
|
@@ -2177,7 +2184,7 @@ function UpdateNameScreen({ onSuccess }) {
|
|
|
2177
2184
|
mutationFn: async (data) => {
|
|
2178
2185
|
const response = await client.updateUser({ name: data.name });
|
|
2179
2186
|
if (response.error) {
|
|
2180
|
-
throw new
|
|
2187
|
+
throw new RequestFailedError({ message: response.error.message ?? L.REQUEST_FAILED });
|
|
2181
2188
|
}
|
|
2182
2189
|
return response.data;
|
|
2183
2190
|
}
|
|
@@ -2227,82 +2234,31 @@ function UpdateNameScreen({ onSuccess }) {
|
|
|
2227
2234
|
label: L.SAVE,
|
|
2228
2235
|
loading: updateMutation.isPending,
|
|
2229
2236
|
loadingLabel: L.LOADING,
|
|
2230
|
-
onPress: form.handleSubmit(handleSubmit)
|
|
2231
|
-
disabled: !form.formState.isValid
|
|
2237
|
+
onPress: form.handleSubmit(handleSubmit)
|
|
2232
2238
|
}
|
|
2233
2239
|
),
|
|
2234
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
2240
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
2235
2241
|
] })
|
|
2236
2242
|
] }) });
|
|
2237
2243
|
}
|
|
2238
2244
|
function UpdateUsernameScreen({ onSuccess }) {
|
|
2239
2245
|
const { localization: L } = useAuthLocalization();
|
|
2240
2246
|
const router = useRouter();
|
|
2241
|
-
|
|
2247
|
+
useAuthClient();
|
|
2242
2248
|
const queryClient = useQueryClient();
|
|
2243
|
-
const { data:
|
|
2249
|
+
const { data: _user } = useUser();
|
|
2244
2250
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
2245
|
-
const [availability, setAvailability] = useState("idle");
|
|
2246
|
-
const debounceRef = useRef(null);
|
|
2247
2251
|
const form = useForm({
|
|
2248
2252
|
resolver: zodResolver(updateUsernameSchema),
|
|
2249
|
-
|
|
2253
|
+
// BA 1.5: username not available on user object
|
|
2254
|
+
defaultValues: { username: "" },
|
|
2250
2255
|
mode: "onChange"
|
|
2251
2256
|
});
|
|
2252
2257
|
const updateMutation = useMutation({
|
|
2253
|
-
mutationFn: async (
|
|
2254
|
-
|
|
2255
|
-
username: data.username
|
|
2256
|
-
});
|
|
2257
|
-
if (response.error) {
|
|
2258
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
2259
|
-
}
|
|
2260
|
-
return response.data;
|
|
2258
|
+
mutationFn: async (_data) => {
|
|
2259
|
+
throw new NotImplementedError({ feature: "Username updates" });
|
|
2261
2260
|
}
|
|
2262
2261
|
});
|
|
2263
|
-
const checkAvailability = useCallback(
|
|
2264
|
-
async (username) => {
|
|
2265
|
-
if (!username || username === user?.username) {
|
|
2266
|
-
setAvailability("idle");
|
|
2267
|
-
return;
|
|
2268
|
-
}
|
|
2269
|
-
setAvailability("checking");
|
|
2270
|
-
try {
|
|
2271
|
-
const response = await client.getSession();
|
|
2272
|
-
if (response.error) {
|
|
2273
|
-
setAvailability("idle");
|
|
2274
|
-
} else {
|
|
2275
|
-
setAvailability("available");
|
|
2276
|
-
}
|
|
2277
|
-
} catch {
|
|
2278
|
-
setAvailability("idle");
|
|
2279
|
-
}
|
|
2280
|
-
},
|
|
2281
|
-
[client, user?.username]
|
|
2282
|
-
);
|
|
2283
|
-
const handleUsernameChange = useCallback(
|
|
2284
|
-
(value) => {
|
|
2285
|
-
if (debounceRef.current) {
|
|
2286
|
-
clearTimeout(debounceRef.current);
|
|
2287
|
-
}
|
|
2288
|
-
if (!value || value === user?.username) {
|
|
2289
|
-
setAvailability("idle");
|
|
2290
|
-
return;
|
|
2291
|
-
}
|
|
2292
|
-
setAvailability("checking");
|
|
2293
|
-
debounceRef.current = setTimeout(() => {
|
|
2294
|
-
checkAvailability(value);
|
|
2295
|
-
}, 300);
|
|
2296
|
-
},
|
|
2297
|
-
[checkAvailability, user?.username]
|
|
2298
|
-
);
|
|
2299
|
-
useEffect(() => {
|
|
2300
|
-
return () => {
|
|
2301
|
-
if (debounceRef.current) {
|
|
2302
|
-
clearTimeout(debounceRef.current);
|
|
2303
|
-
}
|
|
2304
|
-
};
|
|
2305
|
-
}, []);
|
|
2306
2262
|
const handleSubmit = useCallback(
|
|
2307
2263
|
(data) => {
|
|
2308
2264
|
updateMutation.mutate(data, {
|
|
@@ -2327,30 +2283,19 @@ function UpdateUsernameScreen({ onSuccess }) {
|
|
|
2327
2283
|
{
|
|
2328
2284
|
control: form.control,
|
|
2329
2285
|
name: "username",
|
|
2330
|
-
render: ({ field: { onChange, onBlur, value }, fieldState: { error } }) => /* @__PURE__ */
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
value,
|
|
2344
|
-
error: error?.message
|
|
2345
|
-
}
|
|
2346
|
-
),
|
|
2347
|
-
availability === "checking" && /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 4 }, children: [
|
|
2348
|
-
/* @__PURE__ */ jsx(ActivityIndicator, { size: "small" }),
|
|
2349
|
-
/* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: "#6b7280" }, children: L.CHECKING_AVAILABILITY })
|
|
2350
|
-
] }),
|
|
2351
|
-
availability === "available" && /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: "#16a34a" }, children: L.USERNAME_AVAILABLE_STATUS }),
|
|
2352
|
-
availability === "taken" && /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: "#dc2626" }, children: L.USERNAME_TAKEN_STATUS })
|
|
2353
|
-
] })
|
|
2286
|
+
render: ({ field: { onChange, onBlur, value }, fieldState: { error } }) => /* @__PURE__ */ jsx(
|
|
2287
|
+
FormInput,
|
|
2288
|
+
{
|
|
2289
|
+
label: L.USERNAME,
|
|
2290
|
+
placeholder: L.USERNAME_PLACEHOLDER,
|
|
2291
|
+
autoCapitalize: "none",
|
|
2292
|
+
editable: !updateMutation.isPending,
|
|
2293
|
+
onChangeText: onChange,
|
|
2294
|
+
onBlur,
|
|
2295
|
+
value,
|
|
2296
|
+
error: error?.message
|
|
2297
|
+
}
|
|
2298
|
+
)
|
|
2354
2299
|
}
|
|
2355
2300
|
),
|
|
2356
2301
|
updateMutation.error && /* @__PURE__ */ jsx(ErrorFeedback, { message: updateMutation.error.message }),
|
|
@@ -2360,11 +2305,10 @@ function UpdateUsernameScreen({ onSuccess }) {
|
|
|
2360
2305
|
label: L.SAVE,
|
|
2361
2306
|
loading: updateMutation.isPending,
|
|
2362
2307
|
loadingLabel: L.LOADING,
|
|
2363
|
-
onPress: form.handleSubmit(handleSubmit)
|
|
2364
|
-
disabled: !form.formState.isValid || availability === "taken" || availability === "checking"
|
|
2308
|
+
onPress: form.handleSubmit(handleSubmit)
|
|
2365
2309
|
}
|
|
2366
2310
|
),
|
|
2367
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
2311
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
2368
2312
|
] })
|
|
2369
2313
|
] }) });
|
|
2370
2314
|
}
|
|
@@ -2375,32 +2319,18 @@ function OrganizationMembersScreen({
|
|
|
2375
2319
|
}) {
|
|
2376
2320
|
const { localization: L } = useAuthLocalization();
|
|
2377
2321
|
const router = useRouter();
|
|
2378
|
-
|
|
2322
|
+
useAuthClient();
|
|
2379
2323
|
const queryClient = useQueryClient();
|
|
2380
2324
|
const [searchQuery, setSearchQuery] = useState("");
|
|
2381
2325
|
const membersQuery = useQuery({
|
|
2382
2326
|
queryKey: ["organizations", organizationId, "members"],
|
|
2383
2327
|
queryFn: async () => {
|
|
2384
|
-
|
|
2385
|
-
query: { organizationId }
|
|
2386
|
-
});
|
|
2387
|
-
if (response.error) {
|
|
2388
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
2389
|
-
}
|
|
2390
|
-
return response.data?.members ?? [];
|
|
2328
|
+
throw new NotImplementedError({ feature: "Organization member management" });
|
|
2391
2329
|
}
|
|
2392
2330
|
});
|
|
2393
2331
|
const updateRoleMutation = useMutation({
|
|
2394
|
-
mutationFn: async (
|
|
2395
|
-
|
|
2396
|
-
organizationId,
|
|
2397
|
-
memberId,
|
|
2398
|
-
role
|
|
2399
|
-
});
|
|
2400
|
-
if (response.error) {
|
|
2401
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
2402
|
-
}
|
|
2403
|
-
return response.data;
|
|
2332
|
+
mutationFn: async (_memberId_role) => {
|
|
2333
|
+
throw new NotImplementedError({ feature: "Organization role updates" });
|
|
2404
2334
|
},
|
|
2405
2335
|
onSuccess: () => {
|
|
2406
2336
|
queryClient.invalidateQueries({
|
|
@@ -2409,15 +2339,8 @@ function OrganizationMembersScreen({
|
|
|
2409
2339
|
}
|
|
2410
2340
|
});
|
|
2411
2341
|
const removeMutation = useMutation({
|
|
2412
|
-
mutationFn: async (
|
|
2413
|
-
|
|
2414
|
-
organizationId,
|
|
2415
|
-
memberIdOrEmail: memberId
|
|
2416
|
-
});
|
|
2417
|
-
if (response.error) {
|
|
2418
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
2419
|
-
}
|
|
2420
|
-
return response.data;
|
|
2342
|
+
mutationFn: async (_memberId) => {
|
|
2343
|
+
throw new NotImplementedError({ feature: "Organization member removal" });
|
|
2421
2344
|
},
|
|
2422
2345
|
onSuccess: () => {
|
|
2423
2346
|
queryClient.invalidateQueries({
|
|
@@ -2426,49 +2349,63 @@ function OrganizationMembersScreen({
|
|
|
2426
2349
|
onSuccess?.();
|
|
2427
2350
|
}
|
|
2428
2351
|
});
|
|
2352
|
+
const showMemberActionSheet = useCallback(
|
|
2353
|
+
(callback) => {
|
|
2354
|
+
const options = [L.ADMIN, L.MEMBER, L.REMOVE_MEMBER, L.CANCEL];
|
|
2355
|
+
if (Platform.OS === "ios") {
|
|
2356
|
+
ActionSheetIOS.showActionSheetWithOptions(
|
|
2357
|
+
{
|
|
2358
|
+
options,
|
|
2359
|
+
cancelButtonIndex: 3,
|
|
2360
|
+
destructiveButtonIndex: 2
|
|
2361
|
+
},
|
|
2362
|
+
callback
|
|
2363
|
+
);
|
|
2364
|
+
} else {
|
|
2365
|
+
Alert.alert(L.MANAGE_ROLE, void 0, [
|
|
2366
|
+
{ text: L.ADMIN, onPress: () => callback(0) },
|
|
2367
|
+
{ text: L.MEMBER, onPress: () => callback(1) },
|
|
2368
|
+
{ text: L.REMOVE_MEMBER, style: "destructive", onPress: () => callback(2) },
|
|
2369
|
+
{ text: L.CANCEL, style: "cancel" }
|
|
2370
|
+
]);
|
|
2371
|
+
}
|
|
2372
|
+
},
|
|
2373
|
+
[L]
|
|
2374
|
+
);
|
|
2429
2375
|
const handleMemberAction = useCallback(
|
|
2430
|
-
// biome-ignore lint/suspicious/noExplicitAny: member type from Better Auth
|
|
2431
2376
|
(member) => {
|
|
2432
2377
|
if (!isAdmin) return;
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
{
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
}
|
|
2450
|
-
]);
|
|
2451
|
-
} else if (roleMap[buttonIndex]) {
|
|
2452
|
-
updateRoleMutation.mutate({
|
|
2453
|
-
memberId: member.id,
|
|
2454
|
-
role: roleMap[buttonIndex]
|
|
2455
|
-
});
|
|
2456
|
-
}
|
|
2378
|
+
showMemberActionSheet((buttonIndex) => {
|
|
2379
|
+
const roleMap = { 0: "admin", 1: "member" };
|
|
2380
|
+
if (buttonIndex === 2) {
|
|
2381
|
+
Alert.alert(L.REMOVE_MEMBER, L.CONFIRM_REMOVE_MEMBER, [
|
|
2382
|
+
{ text: L.CANCEL, style: "cancel" },
|
|
2383
|
+
{
|
|
2384
|
+
text: L.REMOVE,
|
|
2385
|
+
style: "destructive",
|
|
2386
|
+
onPress: () => removeMutation.mutate(member.id)
|
|
2387
|
+
}
|
|
2388
|
+
]);
|
|
2389
|
+
} else if (roleMap[buttonIndex]) {
|
|
2390
|
+
updateRoleMutation.mutate({
|
|
2391
|
+
memberId: member.id,
|
|
2392
|
+
role: roleMap[buttonIndex]
|
|
2393
|
+
});
|
|
2457
2394
|
}
|
|
2458
|
-
);
|
|
2395
|
+
});
|
|
2459
2396
|
},
|
|
2460
|
-
[isAdmin, L, removeMutation, updateRoleMutation]
|
|
2397
|
+
[isAdmin, L, removeMutation, updateRoleMutation, showMemberActionSheet]
|
|
2461
2398
|
);
|
|
2462
2399
|
const allMembers = membersQuery.data ?? [];
|
|
2463
2400
|
const filteredMembers = searchQuery ? allMembers.filter(
|
|
2464
2401
|
(m) => (m.user?.name ?? "").toLowerCase().includes(searchQuery.toLowerCase()) || (m.user?.email ?? "").toLowerCase().includes(searchQuery.toLowerCase())
|
|
2465
2402
|
) : allMembers;
|
|
2466
|
-
|
|
2403
|
+
updateRoleMutation.isPending || removeMutation.isPending;
|
|
2467
2404
|
const error = updateRoleMutation.error ?? removeMutation.error;
|
|
2468
2405
|
return /* @__PURE__ */ jsx(AuthContainer, { children: /* @__PURE__ */ jsxs(View, { style: { gap: 24, flex: 1 }, children: [
|
|
2469
2406
|
/* @__PURE__ */ jsxs(View, { style: { gap: 4, alignItems: "center" }, children: [
|
|
2470
2407
|
/* @__PURE__ */ jsx(Text, { style: { fontSize: 24, fontWeight: "700", color: "#1f2937" }, children: L.MEMBERS }),
|
|
2471
|
-
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#6b7280" }, children: L.
|
|
2408
|
+
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#6b7280" }, children: L.MEMBERS_SUBTITLE })
|
|
2472
2409
|
] }),
|
|
2473
2410
|
allMembers.length > 10 && /* @__PURE__ */ jsx(
|
|
2474
2411
|
TextInput,
|
|
@@ -2537,15 +2474,7 @@ function OrganizationMembersScreen({
|
|
|
2537
2474
|
item.role
|
|
2538
2475
|
] })
|
|
2539
2476
|
] }),
|
|
2540
|
-
isAdmin && !isOwner && /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
2541
|
-
Button,
|
|
2542
|
-
{
|
|
2543
|
-
onPress: () => handleMemberAction(item),
|
|
2544
|
-
disabled: isPending,
|
|
2545
|
-
variant: "bordered",
|
|
2546
|
-
children: L.ROLE
|
|
2547
|
-
}
|
|
2548
|
-
) })
|
|
2477
|
+
isAdmin && !isOwner && /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => handleMemberAction(item), label: L.MANAGE_ROLE }) })
|
|
2549
2478
|
]
|
|
2550
2479
|
}
|
|
2551
2480
|
);
|
|
@@ -2553,7 +2482,7 @@ function OrganizationMembersScreen({
|
|
|
2553
2482
|
}
|
|
2554
2483
|
),
|
|
2555
2484
|
error && /* @__PURE__ */ jsx(ErrorFeedback, { message: error.message }),
|
|
2556
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
2485
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
2557
2486
|
] }) });
|
|
2558
2487
|
}
|
|
2559
2488
|
function OrganizationSwitcherScreen({
|
|
@@ -2562,25 +2491,17 @@ function OrganizationSwitcherScreen({
|
|
|
2562
2491
|
}) {
|
|
2563
2492
|
const { localization: L } = useAuthLocalization();
|
|
2564
2493
|
const router = useRouter();
|
|
2565
|
-
|
|
2494
|
+
useAuthClient();
|
|
2566
2495
|
const [searchQuery, setSearchQuery] = useState("");
|
|
2567
2496
|
const orgsQuery = useQuery({
|
|
2568
2497
|
queryKey: ["organizations", "list"],
|
|
2569
2498
|
queryFn: async () => {
|
|
2570
|
-
|
|
2571
|
-
if (response.error) {
|
|
2572
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
2573
|
-
}
|
|
2574
|
-
return response.data ?? [];
|
|
2499
|
+
throw new NotImplementedError({ feature: "Organization listing" });
|
|
2575
2500
|
}
|
|
2576
2501
|
});
|
|
2577
2502
|
const setActiveMutation = useMutation({
|
|
2578
|
-
mutationFn: async (
|
|
2579
|
-
|
|
2580
|
-
if (response.error) {
|
|
2581
|
-
throw new Error(response.error.message ?? L.REQUEST_FAILED);
|
|
2582
|
-
}
|
|
2583
|
-
return response.data;
|
|
2503
|
+
mutationFn: async (_orgId) => {
|
|
2504
|
+
throw new NotImplementedError({ feature: "Organization switching" });
|
|
2584
2505
|
}
|
|
2585
2506
|
});
|
|
2586
2507
|
const handleSelect = useCallback(
|
|
@@ -2660,22 +2581,14 @@ function OrganizationSwitcherScreen({
|
|
|
2660
2581
|
),
|
|
2661
2582
|
item.members && /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: "#9ca3af" }, children: L.MEMBER_COUNT.replace("{{count}}", String(item.members.length ?? 0)) })
|
|
2662
2583
|
] }),
|
|
2663
|
-
isCurrent ? /* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#16a34a", fontWeight: "600" }, children: "\u2713" }) : /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(
|
|
2664
|
-
Button,
|
|
2665
|
-
{
|
|
2666
|
-
onPress: () => handleSelect(item.id),
|
|
2667
|
-
disabled: setActiveMutation.isPending,
|
|
2668
|
-
variant: "bordered",
|
|
2669
|
-
children: L.SELECT_ORGANIZATION
|
|
2670
|
-
}
|
|
2671
|
-
) })
|
|
2584
|
+
isCurrent ? /* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: "#16a34a", fontWeight: "600" }, children: "\u2713" }) : /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => handleSelect(item.id), label: L.SELECT_ORGANIZATION }) })
|
|
2672
2585
|
]
|
|
2673
2586
|
}
|
|
2674
2587
|
);
|
|
2675
2588
|
}
|
|
2676
2589
|
}
|
|
2677
2590
|
),
|
|
2678
|
-
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(),
|
|
2591
|
+
/* @__PURE__ */ jsx(View, { style: { alignItems: "center", marginTop: 8 }, children: /* @__PURE__ */ jsx(Host, { matchContents: true, children: /* @__PURE__ */ jsx(Button, { onPress: () => router.back(), label: L.GO_BACK }) }) })
|
|
2679
2592
|
] }) });
|
|
2680
2593
|
}
|
|
2681
2594
|
function useAuthRedirect() {
|
|
@@ -2717,7 +2630,7 @@ function useBiometric() {
|
|
|
2717
2630
|
cancelLabel: "Cancel"
|
|
2718
2631
|
});
|
|
2719
2632
|
if (!result.success) {
|
|
2720
|
-
throw new
|
|
2633
|
+
throw new RequestFailedError({ message: result.error ?? "BIOMETRIC_FAILED_ERROR" });
|
|
2721
2634
|
}
|
|
2722
2635
|
return result;
|
|
2723
2636
|
}
|
|
@@ -2731,7 +2644,7 @@ function useLogout() {
|
|
|
2731
2644
|
mutationFn: async () => {
|
|
2732
2645
|
const response = await client.signOut();
|
|
2733
2646
|
if (response.error) {
|
|
2734
|
-
throw new
|
|
2647
|
+
throw new RequestFailedError({ message: response.error.message ?? "Logout failed" });
|
|
2735
2648
|
}
|
|
2736
2649
|
return response.data;
|
|
2737
2650
|
},
|