@umituz/react-native-auth 1.11.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +0 -0
  2. package/lib/__tests__/services/AuthCoreService.test.d.ts +4 -0
  3. package/lib/__tests__/services/AuthCoreService.test.js +198 -0
  4. package/lib/__tests__/services/AuthPackage.test.d.ts +4 -0
  5. package/lib/__tests__/services/AuthPackage.test.js +177 -0
  6. package/lib/__tests__/services/GuestModeService.test.d.ts +4 -0
  7. package/lib/__tests__/services/GuestModeService.test.js +141 -0
  8. package/lib/__tests__/utils/AuthValidation.test.d.ts +4 -0
  9. package/lib/__tests__/utils/AuthValidation.test.js +222 -0
  10. package/lib/application/ports/IAuthProvider.d.ts +42 -0
  11. package/lib/application/ports/IAuthProvider.js +5 -0
  12. package/lib/application/ports/IAuthService.d.ts +48 -0
  13. package/lib/application/ports/IAuthService.js +5 -0
  14. package/lib/domain/entities/AuthUser.d.ts +12 -0
  15. package/lib/domain/entities/AuthUser.js +5 -0
  16. package/lib/domain/errors/AuthError.d.ts +36 -0
  17. package/lib/domain/errors/AuthError.js +76 -0
  18. package/lib/domain/value-objects/AuthConfig.d.ts +16 -0
  19. package/lib/domain/value-objects/AuthConfig.js +14 -0
  20. package/lib/index.d.ts +45 -0
  21. package/lib/index.js +59 -0
  22. package/lib/infrastructure/adapters/StorageProviderAdapter.d.ts +16 -0
  23. package/lib/infrastructure/adapters/StorageProviderAdapter.js +72 -0
  24. package/lib/infrastructure/adapters/UIProviderAdapter.d.ts +18 -0
  25. package/lib/infrastructure/adapters/UIProviderAdapter.js +28 -0
  26. package/lib/infrastructure/providers/FirebaseAuthProvider.d.ts +19 -0
  27. package/lib/infrastructure/providers/FirebaseAuthProvider.js +94 -0
  28. package/lib/infrastructure/services/AuthCoreService.d.ts +22 -0
  29. package/lib/infrastructure/services/AuthCoreService.js +102 -0
  30. package/lib/infrastructure/services/AuthEventService.d.ts +28 -0
  31. package/lib/infrastructure/services/AuthEventService.js +88 -0
  32. package/lib/infrastructure/services/AuthPackage.d.ts +62 -0
  33. package/lib/infrastructure/services/AuthPackage.js +91 -0
  34. package/lib/infrastructure/services/AuthService.d.ts +42 -0
  35. package/lib/infrastructure/services/AuthService.js +123 -0
  36. package/lib/infrastructure/services/GuestModeService.d.ts +23 -0
  37. package/lib/infrastructure/services/GuestModeService.js +69 -0
  38. package/lib/infrastructure/storage/GuestModeStorage.d.ts +16 -0
  39. package/lib/infrastructure/storage/GuestModeStorage.js +73 -0
  40. package/lib/infrastructure/utils/AuthErrorMapper.d.ts +8 -0
  41. package/lib/infrastructure/utils/AuthErrorMapper.js +51 -0
  42. package/lib/infrastructure/utils/AuthEventEmitter.d.ts +12 -0
  43. package/lib/infrastructure/utils/AuthEventEmitter.js +25 -0
  44. package/lib/infrastructure/utils/AuthValidation.d.ts +49 -0
  45. package/lib/infrastructure/utils/AuthValidation.js +133 -0
  46. package/lib/infrastructure/utils/UserMapper.d.ts +15 -0
  47. package/lib/infrastructure/utils/UserMapper.js +16 -0
  48. package/lib/presentation/components/AuthContainer.d.ts +10 -0
  49. package/lib/presentation/components/AuthContainer.js +27 -0
  50. package/lib/presentation/components/AuthDivider.d.ts +6 -0
  51. package/lib/presentation/components/AuthDivider.js +36 -0
  52. package/lib/presentation/components/AuthErrorDisplay.d.ts +10 -0
  53. package/lib/presentation/components/AuthErrorDisplay.js +24 -0
  54. package/lib/presentation/components/AuthFormCard.d.ts +10 -0
  55. package/lib/presentation/components/AuthFormCard.js +19 -0
  56. package/lib/presentation/components/AuthGradientBackground.d.ts +6 -0
  57. package/lib/presentation/components/AuthGradientBackground.js +8 -0
  58. package/lib/presentation/components/AuthHeader.d.ts +11 -0
  59. package/lib/presentation/components/AuthHeader.js +38 -0
  60. package/lib/presentation/components/AuthLegalLinks.d.ts +28 -0
  61. package/lib/presentation/components/AuthLegalLinks.js +54 -0
  62. package/lib/presentation/components/AuthLink.d.ts +13 -0
  63. package/lib/presentation/components/AuthLink.js +27 -0
  64. package/lib/presentation/components/LoginForm.d.ts +10 -0
  65. package/lib/presentation/components/LoginForm.js +27 -0
  66. package/lib/presentation/components/PasswordMatchIndicator.d.ts +9 -0
  67. package/lib/presentation/components/PasswordMatchIndicator.js +30 -0
  68. package/lib/presentation/components/PasswordStrengthIndicator.d.ts +11 -0
  69. package/lib/presentation/components/PasswordStrengthIndicator.js +60 -0
  70. package/lib/presentation/components/RegisterForm.d.ts +14 -0
  71. package/lib/presentation/components/RegisterForm.js +30 -0
  72. package/lib/presentation/hooks/useAuth.d.ts +44 -0
  73. package/lib/presentation/hooks/useAuth.js +38 -0
  74. package/lib/presentation/hooks/useAuthActions.d.ts +15 -0
  75. package/lib/presentation/hooks/useAuthActions.js +162 -0
  76. package/lib/presentation/hooks/useAuthState.d.ts +19 -0
  77. package/lib/presentation/hooks/useAuthState.js +79 -0
  78. package/lib/presentation/hooks/useLoginForm.d.ts +21 -0
  79. package/lib/presentation/hooks/useLoginForm.js +131 -0
  80. package/lib/presentation/hooks/useRegisterForm.d.ts +31 -0
  81. package/lib/presentation/hooks/useRegisterForm.js +136 -0
  82. package/lib/presentation/navigation/AuthNavigator.d.ts +28 -0
  83. package/lib/presentation/navigation/AuthNavigator.js +37 -0
  84. package/lib/presentation/screens/LoginScreen.d.ts +6 -0
  85. package/lib/presentation/screens/LoginScreen.js +15 -0
  86. package/lib/presentation/screens/RegisterScreen.d.ts +12 -0
  87. package/lib/presentation/screens/RegisterScreen.js +15 -0
  88. package/lib/presentation/utils/getAuthErrorMessage.d.ts +8 -0
  89. package/lib/presentation/utils/getAuthErrorMessage.js +69 -0
  90. package/package.json +12 -4
  91. package/src/__tests__/services/AuthCoreService.test.ts +247 -0
  92. package/src/__tests__/services/AuthPackage.test.ts +226 -0
  93. package/src/__tests__/services/GuestModeService.test.ts +194 -0
  94. package/src/__tests__/utils/AuthValidation.test.ts +270 -0
  95. package/src/application/ports/IAuthProvider.ts +0 -0
  96. package/src/application/ports/IAuthService.ts +0 -0
  97. package/src/domain/entities/AuthUser.ts +0 -0
  98. package/src/domain/errors/AuthError.ts +0 -0
  99. package/src/domain/value-objects/AuthConfig.ts +0 -0
  100. package/src/index.ts +4 -0
  101. package/src/infrastructure/adapters/StorageProviderAdapter.ts +73 -0
  102. package/src/infrastructure/adapters/UIProviderAdapter.ts +39 -0
  103. package/src/infrastructure/providers/FirebaseAuthProvider.ts +10 -2
  104. package/src/infrastructure/services/AuthCoreService.ts +138 -0
  105. package/src/infrastructure/services/AuthEventService.ts +115 -0
  106. package/src/infrastructure/services/AuthPackage.ts +148 -0
  107. package/src/infrastructure/services/AuthService.ts +62 -128
  108. package/src/infrastructure/services/GuestModeService.ts +86 -0
  109. package/src/infrastructure/storage/GuestModeStorage.ts +40 -14
  110. package/src/infrastructure/utils/AuthErrorMapper.ts +7 -3
  111. package/src/infrastructure/utils/AuthEventEmitter.ts +0 -0
  112. package/src/infrastructure/utils/AuthValidation.ts +47 -17
  113. package/src/infrastructure/utils/UserMapper.ts +0 -0
  114. package/src/presentation/components/AuthContainer.tsx +0 -0
  115. package/src/presentation/components/AuthDivider.tsx +0 -0
  116. package/src/presentation/components/AuthErrorDisplay.tsx +0 -0
  117. package/src/presentation/components/AuthFormCard.tsx +0 -0
  118. package/src/presentation/components/AuthGradientBackground.tsx +0 -0
  119. package/src/presentation/components/AuthHeader.tsx +0 -0
  120. package/src/presentation/components/AuthLegalLinks.tsx +0 -0
  121. package/src/presentation/components/AuthLink.tsx +0 -0
  122. package/src/presentation/components/LoginForm.tsx +0 -0
  123. package/src/presentation/components/PasswordMatchIndicator.tsx +50 -0
  124. package/src/presentation/components/PasswordStrengthIndicator.tsx +118 -0
  125. package/src/presentation/components/RegisterForm.tsx +10 -0
  126. package/src/presentation/hooks/useAuth.ts +0 -0
  127. package/src/presentation/hooks/useAuthActions.ts +8 -11
  128. package/src/presentation/hooks/useAuthState.ts +10 -0
  129. package/src/presentation/hooks/useLoginForm.ts +0 -0
  130. package/src/presentation/hooks/useRegisterForm.ts +40 -18
  131. package/src/presentation/navigation/AuthNavigator.tsx +2 -2
  132. package/src/presentation/screens/LoginScreen.tsx +3 -6
  133. package/src/presentation/screens/RegisterScreen.tsx +3 -6
  134. package/src/presentation/utils/getAuthErrorMessage.ts +0 -0
  135. package/src/types/external.d.ts +68 -0
  136. package/LICENSE +0 -22
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
4
+ import { useLocalization } from "@umituz/react-native-localization";
5
+ export const AuthDivider = () => {
6
+ const tokens = useAppDesignTokens();
7
+ const { t } = useLocalization();
8
+ return (_jsxs(View, { style: styles.divider, children: [_jsx(View, { style: [
9
+ styles.dividerLine,
10
+ { backgroundColor: tokens.colors.borderLight || "#E5E5E5" },
11
+ ] }), _jsx(Text, { style: [
12
+ styles.dividerText,
13
+ { color: tokens.colors.textSecondary || "#999999" },
14
+ ], children: t("general.or") }), _jsx(View, { style: [
15
+ styles.dividerLine,
16
+ { backgroundColor: tokens.colors.borderLight || "#E5E5E5" },
17
+ ] })] }));
18
+ };
19
+ const styles = StyleSheet.create({
20
+ divider: {
21
+ flexDirection: "row",
22
+ alignItems: "center",
23
+ marginVertical: 20,
24
+ },
25
+ dividerLine: {
26
+ flex: 1,
27
+ height: 1,
28
+ },
29
+ dividerText: {
30
+ marginHorizontal: 16,
31
+ fontSize: 13,
32
+ fontWeight: "500",
33
+ textTransform: "uppercase",
34
+ letterSpacing: 0.5,
35
+ },
36
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Auth Error Display Component
3
+ * Displays authentication errors
4
+ */
5
+ import React from "react";
6
+ interface AuthErrorDisplayProps {
7
+ error: string | null;
8
+ }
9
+ export declare const AuthErrorDisplay: React.FC<AuthErrorDisplayProps>;
10
+ export {};
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ export const AuthErrorDisplay = ({ error, }) => {
4
+ if (!error) {
5
+ return null;
6
+ }
7
+ return (_jsx(View, { style: styles.errorContainer, children: _jsx(Text, { style: styles.errorText, children: error }) }));
8
+ };
9
+ const styles = StyleSheet.create({
10
+ errorContainer: {
11
+ marginBottom: 16,
12
+ padding: 14,
13
+ borderRadius: 12,
14
+ backgroundColor: "rgba(255, 59, 48, 0.1)",
15
+ borderWidth: 1,
16
+ borderColor: "rgba(255, 59, 48, 0.2)",
17
+ },
18
+ errorText: {
19
+ color: "#FF3B30",
20
+ fontSize: 14,
21
+ textAlign: "center",
22
+ fontWeight: "500",
23
+ },
24
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Auth Form Card Component
3
+ * Reusable card container for auth forms
4
+ */
5
+ import React from "react";
6
+ interface AuthFormCardProps {
7
+ children: React.ReactNode;
8
+ }
9
+ export declare const AuthFormCard: React.FC<AuthFormCardProps>;
10
+ export {};
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { View, StyleSheet } from "react-native";
3
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
4
+ export const AuthFormCard = ({ children }) => {
5
+ const tokens = useAppDesignTokens();
6
+ return (_jsx(View, { style: [
7
+ styles.formCard,
8
+ { backgroundColor: tokens.colors.surface || "#FFFFFF" },
9
+ ], children: _jsx(View, { style: styles.form, children: children }) }));
10
+ };
11
+ const styles = StyleSheet.create({
12
+ formCard: {
13
+ borderRadius: 24,
14
+ padding: 24,
15
+ },
16
+ form: {
17
+ width: "100%",
18
+ },
19
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Auth Gradient Background Component
3
+ * Gradient background for auth screens
4
+ */
5
+ import React from "react";
6
+ export declare const AuthGradientBackground: React.FC;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { StyleSheet } from "react-native";
3
+ import { LinearGradient } from "expo-linear-gradient";
4
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
5
+ export const AuthGradientBackground = () => {
6
+ const tokens = useAppDesignTokens();
7
+ return (_jsx(LinearGradient, { colors: [tokens.colors.primary, tokens.colors.secondary], start: { x: 0, y: 0 }, end: { x: 1, y: 1 }, style: StyleSheet.absoluteFill }));
8
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Auth Header Component
3
+ * Reusable header for auth screens
4
+ */
5
+ import React from "react";
6
+ interface AuthHeaderProps {
7
+ title: string;
8
+ subtitle?: string;
9
+ }
10
+ export declare const AuthHeader: React.FC<AuthHeaderProps>;
11
+ export {};
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
4
+ import { useLocalization } from "@umituz/react-native-localization";
5
+ export const AuthHeader = ({ title, subtitle }) => {
6
+ const tokens = useAppDesignTokens();
7
+ const { t } = useLocalization();
8
+ return (_jsxs(View, { style: styles.header, children: [_jsx(Text, { style: [
9
+ styles.title,
10
+ { color: tokens.colors.onPrimary || "#FFFFFF" },
11
+ ], children: title }), (subtitle || t("auth.subtitle")) && (_jsx(Text, { style: [
12
+ styles.subtitle,
13
+ {
14
+ color: tokens.colors.textInverse || "rgba(255, 255, 255, 0.9)",
15
+ },
16
+ ], children: subtitle || t("auth.subtitle") }))] }));
17
+ };
18
+ const styles = StyleSheet.create({
19
+ header: {
20
+ marginBottom: 28,
21
+ alignItems: "center",
22
+ paddingHorizontal: 20,
23
+ },
24
+ title: {
25
+ fontSize: 36,
26
+ fontWeight: "700",
27
+ marginBottom: 8,
28
+ textAlign: "center",
29
+ letterSpacing: -0.5,
30
+ },
31
+ subtitle: {
32
+ fontSize: 16,
33
+ textAlign: "center",
34
+ lineHeight: 22,
35
+ fontWeight: "400",
36
+ marginTop: 4,
37
+ },
38
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Auth Legal Links Component
3
+ * Display Terms of Service and Privacy Policy links
4
+ */
5
+ import React from "react";
6
+ export interface AuthLegalLinksProps {
7
+ /**
8
+ * Terms of Service URL
9
+ */
10
+ termsUrl?: string;
11
+ /**
12
+ * Privacy Policy URL
13
+ */
14
+ privacyUrl?: string;
15
+ /**
16
+ * Callback when Terms of Service is pressed
17
+ */
18
+ onTermsPress?: () => void;
19
+ /**
20
+ * Callback when Privacy Policy is pressed
21
+ */
22
+ onPrivacyPress?: () => void;
23
+ /**
24
+ * Custom text before links
25
+ */
26
+ prefixText?: string;
27
+ }
28
+ export declare const AuthLegalLinks: React.FC<AuthLegalLinksProps>;
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, StyleSheet, Linking } from "react-native";
3
+ import { AtomicButton, AtomicText } from "@umituz/react-native-design-system-atoms";
4
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
5
+ import { useLocalization } from "@umituz/react-native-localization";
6
+ export const AuthLegalLinks = ({ termsUrl, privacyUrl, onTermsPress, onPrivacyPress, prefixText, }) => {
7
+ const tokens = useAppDesignTokens();
8
+ const { t } = useLocalization();
9
+ const handleTermsPress = async () => {
10
+ if (onTermsPress) {
11
+ onTermsPress();
12
+ }
13
+ else if (termsUrl) {
14
+ await Linking.openURL(termsUrl);
15
+ }
16
+ };
17
+ const handlePrivacyPress = async () => {
18
+ if (onPrivacyPress) {
19
+ onPrivacyPress();
20
+ }
21
+ else if (privacyUrl) {
22
+ await Linking.openURL(privacyUrl);
23
+ }
24
+ };
25
+ const hasTerms = termsUrl || onTermsPress;
26
+ const hasPrivacy = privacyUrl || onPrivacyPress;
27
+ if (!hasTerms && !hasPrivacy) {
28
+ return null;
29
+ }
30
+ return (_jsxs(View, { style: styles.container, children: [prefixText && (_jsx(AtomicText, { type: "bodySmall", color: "secondary", style: styles.prefixText, children: prefixText })), _jsxs(View, { style: styles.linksContainer, children: [hasTerms && (_jsx(AtomicButton, { variant: "text", onPress: handleTermsPress, style: styles.linkButton, children: _jsx(AtomicText, { type: "bodySmall", color: "primary", children: t("auth.termsOfService") || "Terms of Service" }) })), hasTerms && hasPrivacy && (_jsx(AtomicText, { type: "bodySmall", color: "secondary", style: styles.separator, children: " • " })), hasPrivacy && (_jsx(AtomicButton, { variant: "text", onPress: handlePrivacyPress, style: styles.linkButton, children: _jsx(AtomicText, { type: "bodySmall", color: "primary", children: t("auth.privacyPolicy") || "Privacy Policy" }) }))] })] }));
31
+ };
32
+ const styles = StyleSheet.create({
33
+ container: {
34
+ marginTop: 16,
35
+ alignItems: "center",
36
+ },
37
+ prefixText: {
38
+ marginBottom: 8,
39
+ textAlign: "center",
40
+ },
41
+ linksContainer: {
42
+ flexDirection: "row",
43
+ alignItems: "center",
44
+ justifyContent: "center",
45
+ flexWrap: "wrap",
46
+ },
47
+ linkButton: {
48
+ paddingHorizontal: 4,
49
+ paddingVertical: 4,
50
+ },
51
+ separator: {
52
+ marginHorizontal: 4,
53
+ },
54
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Auth Link Component
3
+ * Link text with button for navigation between auth screens
4
+ */
5
+ import React from "react";
6
+ interface AuthLinkProps {
7
+ text: string;
8
+ linkText: string;
9
+ onPress: () => void;
10
+ disabled?: boolean;
11
+ }
12
+ export declare const AuthLink: React.FC<AuthLinkProps>;
13
+ export {};
@@ -0,0 +1,27 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ import { AtomicButton } from "@umituz/react-native-design-system-atoms";
4
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
5
+ export const AuthLink = ({ text, linkText, onPress, disabled = false, }) => {
6
+ const tokens = useAppDesignTokens();
7
+ return (_jsxs(View, { style: styles.container, children: [_jsxs(Text, { style: [
8
+ styles.text,
9
+ { color: tokens.colors.textSecondary || "#666666" },
10
+ ], children: [text, " "] }), _jsx(AtomicButton, { variant: "text", onPress: onPress, disabled: disabled, style: styles.button, children: linkText })] }));
11
+ };
12
+ const styles = StyleSheet.create({
13
+ container: {
14
+ flexDirection: "row",
15
+ justifyContent: "center",
16
+ alignItems: "center",
17
+ marginTop: 8,
18
+ paddingTop: 8,
19
+ },
20
+ text: {
21
+ fontSize: 15,
22
+ fontWeight: "400",
23
+ },
24
+ button: {
25
+ paddingHorizontal: 4,
26
+ },
27
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Login Form Component
3
+ * Single Responsibility: Render login form UI
4
+ */
5
+ import React from "react";
6
+ interface LoginFormProps {
7
+ onNavigateToRegister: () => void;
8
+ }
9
+ export declare const LoginForm: React.FC<LoginFormProps>;
10
+ export {};
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, StyleSheet } from "react-native";
3
+ import { AtomicInput, AtomicButton } from "@umituz/react-native-design-system-atoms";
4
+ import { useLocalization } from "@umituz/react-native-localization";
5
+ import { useLoginForm } from "../hooks/useLoginForm";
6
+ import { AuthErrorDisplay } from "./AuthErrorDisplay";
7
+ import { AuthDivider } from "./AuthDivider";
8
+ import { AuthLink } from "./AuthLink";
9
+ export const LoginForm = ({ onNavigateToRegister, }) => {
10
+ const { t } = useLocalization();
11
+ const { email, password, emailError, passwordError, loading, handleEmailChange, handlePasswordChange, handleSignIn, handleContinueAsGuest, displayError, } = useLoginForm();
12
+ return (_jsxs(_Fragment, { children: [_jsx(View, { style: styles.inputContainer, children: _jsx(AtomicInput, { label: t("auth.email"), value: email, onChangeText: handleEmailChange, placeholder: t("auth.emailPlaceholder"), keyboardType: "email-address", autoCapitalize: "none", disabled: loading, state: emailError ? "error" : "default", helperText: emailError || undefined }) }), _jsx(View, { style: styles.inputContainer, children: _jsx(AtomicInput, { label: t("auth.password"), value: password, onChangeText: handlePasswordChange, placeholder: t("auth.passwordPlaceholder"), secureTextEntry: true, autoCapitalize: "none", disabled: loading, state: passwordError ? "error" : "default", helperText: passwordError || undefined }) }), _jsx(AuthErrorDisplay, { error: displayError }), _jsx(View, { style: styles.buttonContainer, children: _jsx(AtomicButton, { variant: "primary", onPress: handleSignIn, disabled: loading || !email.trim() || !password.trim(), fullWidth: true, style: styles.signInButton, children: t("auth.signIn") }) }), _jsx(AuthDivider, {}), _jsx(View, { style: styles.buttonContainer, children: _jsx(AtomicButton, { variant: "outline", onPress: handleContinueAsGuest, disabled: loading, fullWidth: true, style: styles.guestButton, testID: "continue-as-guest-button", children: t("auth.continueAsGuest") }) }), _jsx(AuthLink, { text: t("auth.dontHaveAccount"), linkText: t("auth.createAccount"), onPress: onNavigateToRegister, disabled: loading })] }));
13
+ };
14
+ const styles = StyleSheet.create({
15
+ inputContainer: {
16
+ marginBottom: 20,
17
+ },
18
+ buttonContainer: {
19
+ marginBottom: 16,
20
+ },
21
+ signInButton: {
22
+ minHeight: 52,
23
+ },
24
+ guestButton: {
25
+ minHeight: 52,
26
+ },
27
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Password Match Indicator Component
3
+ * Shows whether passwords match
4
+ */
5
+ import React from "react";
6
+ export interface PasswordMatchIndicatorProps {
7
+ isMatch: boolean;
8
+ }
9
+ export declare const PasswordMatchIndicator: React.FC<PasswordMatchIndicatorProps>;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
4
+ import { useLocalization } from "@umituz/react-native-localization";
5
+ export const PasswordMatchIndicator = ({ isMatch, }) => {
6
+ const tokens = useAppDesignTokens();
7
+ const { t } = useLocalization();
8
+ const color = isMatch ? tokens.colors.success : tokens.colors.error;
9
+ const text = isMatch
10
+ ? t("auth.passwordsMatch", { defaultValue: "Passwords match" })
11
+ : t("auth.passwordsDontMatch", { defaultValue: "Passwords don't match" });
12
+ return (_jsxs(View, { style: styles.container, children: [_jsx(View, { style: [styles.dot, { backgroundColor: color }] }), _jsx(Text, { style: [styles.text, { color }], children: text })] }));
13
+ };
14
+ const styles = StyleSheet.create({
15
+ container: {
16
+ flexDirection: "row",
17
+ alignItems: "center",
18
+ gap: 6,
19
+ marginTop: 8,
20
+ },
21
+ dot: {
22
+ width: 6,
23
+ height: 6,
24
+ borderRadius: 3,
25
+ },
26
+ text: {
27
+ fontSize: 12,
28
+ fontWeight: "500",
29
+ },
30
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Password Strength Indicator Component
3
+ * Shows password requirements with visual feedback
4
+ */
5
+ import React from "react";
6
+ import type { PasswordRequirements } from "../../infrastructure/utils/AuthValidation";
7
+ export interface PasswordStrengthIndicatorProps {
8
+ requirements: PasswordRequirements;
9
+ showLabels?: boolean;
10
+ }
11
+ export declare const PasswordStrengthIndicator: React.FC<PasswordStrengthIndicatorProps>;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
4
+ const RequirementDot = ({ label, isValid, successColor, pendingColor, }) => {
5
+ const color = isValid ? successColor : pendingColor;
6
+ return (_jsxs(View, { style: styles.requirement, children: [_jsx(View, { style: [styles.dot, { backgroundColor: color }] }), _jsx(Text, { style: [styles.label, { color }], children: label })] }));
7
+ };
8
+ export const PasswordStrengthIndicator = ({ requirements, showLabels = true }) => {
9
+ const tokens = useAppDesignTokens();
10
+ const successColor = tokens.colors.success;
11
+ const pendingColor = tokens.colors.textTertiary;
12
+ const items = [
13
+ { key: "minLength", label: "8+", isValid: requirements.hasMinLength },
14
+ { key: "uppercase", label: "A-Z", isValid: requirements.hasUppercase },
15
+ { key: "lowercase", label: "a-z", isValid: requirements.hasLowercase },
16
+ { key: "number", label: "0-9", isValid: requirements.hasNumber },
17
+ { key: "special", label: "!@#", isValid: requirements.hasSpecialChar },
18
+ ];
19
+ if (!showLabels) {
20
+ return (_jsx(View, { style: styles.dotsOnly, children: items.map((item) => (_jsx(View, { style: [
21
+ styles.dotOnly,
22
+ {
23
+ backgroundColor: item.isValid ? successColor : pendingColor,
24
+ },
25
+ ] }, item.key))) }));
26
+ }
27
+ return (_jsx(View, { style: styles.container, children: items.map((item) => (_jsx(RequirementDot, { label: item.label, isValid: item.isValid, successColor: successColor, pendingColor: pendingColor }, item.key))) }));
28
+ };
29
+ const styles = StyleSheet.create({
30
+ container: {
31
+ flexDirection: "row",
32
+ flexWrap: "wrap",
33
+ gap: 12,
34
+ marginTop: 8,
35
+ },
36
+ dotsOnly: {
37
+ flexDirection: "row",
38
+ gap: 6,
39
+ marginTop: 8,
40
+ },
41
+ requirement: {
42
+ flexDirection: "row",
43
+ alignItems: "center",
44
+ gap: 4,
45
+ },
46
+ dot: {
47
+ width: 6,
48
+ height: 6,
49
+ borderRadius: 3,
50
+ },
51
+ dotOnly: {
52
+ width: 8,
53
+ height: 8,
54
+ borderRadius: 4,
55
+ },
56
+ label: {
57
+ fontSize: 11,
58
+ fontWeight: "500",
59
+ },
60
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Register Form Component
3
+ * Single Responsibility: Render register form UI
4
+ */
5
+ import React from "react";
6
+ interface RegisterFormProps {
7
+ onNavigateToLogin: () => void;
8
+ termsUrl?: string;
9
+ privacyUrl?: string;
10
+ onTermsPress?: () => void;
11
+ onPrivacyPress?: () => void;
12
+ }
13
+ export declare const RegisterForm: React.FC<RegisterFormProps>;
14
+ export {};
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { View, StyleSheet } from "react-native";
3
+ import { AtomicInput, AtomicButton } from "@umituz/react-native-design-system-atoms";
4
+ import { useLocalization } from "@umituz/react-native-localization";
5
+ import { useRegisterForm } from "../hooks/useRegisterForm";
6
+ import { AuthErrorDisplay } from "./AuthErrorDisplay";
7
+ import { AuthLink } from "./AuthLink";
8
+ import { AuthLegalLinks } from "./AuthLegalLinks";
9
+ import { PasswordStrengthIndicator } from "./PasswordStrengthIndicator";
10
+ import { PasswordMatchIndicator } from "./PasswordMatchIndicator";
11
+ export const RegisterForm = ({ onNavigateToLogin, termsUrl, privacyUrl, onTermsPress, onPrivacyPress, }) => {
12
+ const { t } = useLocalization();
13
+ const { displayName, email, password, confirmPassword, fieldErrors, loading, passwordRequirements, passwordsMatch, handleDisplayNameChange, handleEmailChange, handlePasswordChange, handleConfirmPasswordChange, handleSignUp, displayError, } = useRegisterForm();
14
+ return (_jsxs(_Fragment, { children: [_jsx(View, { style: styles.inputContainer, children: _jsx(AtomicInput, { label: t("auth.displayName") || "Full Name", value: displayName, onChangeText: handleDisplayNameChange, placeholder: t("auth.displayNamePlaceholder") || "Enter your full name", autoCapitalize: "words", disabled: loading, state: fieldErrors.displayName ? "error" : "default", helperText: fieldErrors.displayName || undefined }) }), _jsx(View, { style: styles.inputContainer, children: _jsx(AtomicInput, { label: t("auth.email"), value: email, onChangeText: handleEmailChange, placeholder: t("auth.emailPlaceholder"), keyboardType: "email-address", autoCapitalize: "none", disabled: loading, state: fieldErrors.email ? "error" : "default", helperText: fieldErrors.email || undefined }) }), _jsxs(View, { style: styles.inputContainer, children: [_jsx(AtomicInput, { label: t("auth.password"), value: password, onChangeText: handlePasswordChange, placeholder: t("auth.passwordPlaceholder"), secureTextEntry: true, autoCapitalize: "none", disabled: loading, state: fieldErrors.password ? "error" : "default", helperText: fieldErrors.password || undefined }), password.length > 0 && (_jsx(PasswordStrengthIndicator, { requirements: passwordRequirements }))] }), _jsxs(View, { style: styles.inputContainer, children: [_jsx(AtomicInput, { label: t("auth.confirmPassword") || "Confirm Password", value: confirmPassword, onChangeText: handleConfirmPasswordChange, placeholder: t("auth.confirmPasswordPlaceholder") || "Confirm your password", secureTextEntry: true, autoCapitalize: "none", disabled: loading, state: fieldErrors.confirmPassword ? "error" : "default", helperText: fieldErrors.confirmPassword || undefined }), confirmPassword.length > 0 && (_jsx(PasswordMatchIndicator, { isMatch: passwordsMatch }))] }), _jsx(AuthErrorDisplay, { error: displayError }), _jsx(View, { style: styles.buttonContainer, children: _jsx(AtomicButton, { variant: "primary", onPress: handleSignUp, disabled: loading ||
15
+ !email.trim() ||
16
+ !password.trim() ||
17
+ !confirmPassword.trim(), fullWidth: true, style: styles.signUpButton, children: t("auth.signUp") }) }), _jsx(AuthLink, { text: t("auth.alreadyHaveAccount"), linkText: t("auth.signIn"), onPress: onNavigateToLogin, disabled: loading }), _jsx(AuthLegalLinks, { termsUrl: termsUrl, privacyUrl: privacyUrl, onTermsPress: onTermsPress, onPrivacyPress: onPrivacyPress, prefixText: t("auth.bySigningUp") || "By signing up, you agree to our" })] }));
18
+ };
19
+ const styles = StyleSheet.create({
20
+ inputContainer: {
21
+ marginBottom: 20,
22
+ },
23
+ buttonContainer: {
24
+ marginBottom: 16,
25
+ marginTop: 8,
26
+ },
27
+ signUpButton: {
28
+ minHeight: 52,
29
+ },
30
+ });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * useAuth Hook
3
+ * React hook for authentication state management
4
+ *
5
+ * Uses provider-agnostic AuthUser type.
6
+ * Adds app-specific state (guest mode, error handling) on top of provider auth.
7
+ */
8
+ import type { AuthUser } from "../../domain/entities/AuthUser";
9
+ export interface UseAuthResult {
10
+ /** Current authenticated user */
11
+ user: AuthUser | null;
12
+ /** Whether auth state is loading */
13
+ loading: boolean;
14
+ /** Whether user is in guest mode */
15
+ isGuest: boolean;
16
+ /** Whether user is authenticated */
17
+ isAuthenticated: boolean;
18
+ /** Current error message */
19
+ error: string | null;
20
+ /** Sign up function */
21
+ signUp: (email: string, password: string, displayName?: string) => Promise<void>;
22
+ /** Sign in function */
23
+ signIn: (email: string, password: string) => Promise<void>;
24
+ /** Sign out function */
25
+ signOut: () => Promise<void>;
26
+ /** Continue as guest function */
27
+ continueAsGuest: () => Promise<void>;
28
+ /** Set error manually (for form validation, etc.) */
29
+ setError: (error: string | null) => void;
30
+ }
31
+ /**
32
+ * Hook for authentication state management
33
+ *
34
+ * Uses Firebase Auth's built-in state management and adds app-specific features:
35
+ * - Guest mode support
36
+ * - Error handling
37
+ * - Loading states
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const { user, isAuthenticated, signIn, signUp, signOut } = useAuth();
42
+ * ```
43
+ */
44
+ export declare function useAuth(): UseAuthResult;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * useAuth Hook
3
+ * React hook for authentication state management
4
+ *
5
+ * Uses provider-agnostic AuthUser type.
6
+ * Adds app-specific state (guest mode, error handling) on top of provider auth.
7
+ */
8
+ import { useAuthState } from "./useAuthState";
9
+ import { useAuthActions } from "./useAuthActions";
10
+ /**
11
+ * Hook for authentication state management
12
+ *
13
+ * Uses Firebase Auth's built-in state management and adds app-specific features:
14
+ * - Guest mode support
15
+ * - Error handling
16
+ * - Loading states
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const { user, isAuthenticated, signIn, signUp, signOut } = useAuth();
21
+ * ```
22
+ */
23
+ export function useAuth() {
24
+ const state = useAuthState();
25
+ const actions = useAuthActions(state);
26
+ return {
27
+ user: state.user,
28
+ loading: state.loading,
29
+ isGuest: state.isGuest,
30
+ isAuthenticated: state.isAuthenticated,
31
+ error: state.error,
32
+ signUp: actions.signUp,
33
+ signIn: actions.signIn,
34
+ signOut: actions.signOut,
35
+ continueAsGuest: actions.continueAsGuest,
36
+ setError: state.setError,
37
+ };
38
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * useAuthActions Hook
3
+ * Single Responsibility: Handle authentication actions
4
+ */
5
+ import type { UseAuthStateResult } from "./useAuthState";
6
+ export interface UseAuthActionsResult {
7
+ signUp: (email: string, password: string, displayName?: string) => Promise<void>;
8
+ signIn: (email: string, password: string) => Promise<void>;
9
+ signOut: () => Promise<void>;
10
+ continueAsGuest: () => Promise<void>;
11
+ }
12
+ /**
13
+ * Hook for authentication actions
14
+ */
15
+ export declare function useAuthActions(state: UseAuthStateResult): UseAuthActionsResult;