@towsila/expo-ui-library 1.0.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.
Files changed (104) hide show
  1. package/README.md +142 -0
  2. package/package.json +55 -0
  3. package/src/assets/customIcons/arrow.svg +3 -0
  4. package/src/assets/customIcons/arrow_back.svg +3 -0
  5. package/src/assets/customIcons/ban.svg +4 -0
  6. package/src/assets/customIcons/checkmark.svg +4 -0
  7. package/src/assets/customIcons/close.svg +5 -0
  8. package/src/assets/customIcons/dark_mode.svg +3 -0
  9. package/src/assets/customIcons/dark_suspended.svg +67 -0
  10. package/src/assets/customIcons/document.svg +4 -0
  11. package/src/assets/customIcons/email.svg +3 -0
  12. package/src/assets/customIcons/eye.svg +5 -0
  13. package/src/assets/customIcons/eye_off.svg +7 -0
  14. package/src/assets/customIcons/humberger.svg +5 -0
  15. package/src/assets/customIcons/information.svg +6 -0
  16. package/src/assets/customIcons/light_mode.svg +18 -0
  17. package/src/assets/customIcons/light_suspended.svg +67 -0
  18. package/src/assets/customIcons/lock_closed.svg +4 -0
  19. package/src/assets/customIcons/logout.svg +4 -0
  20. package/src/assets/customIcons/person.svg +4 -0
  21. package/src/assets/customIcons/phone.svg +3 -0
  22. package/src/assets/customIcons/phone_dark.svg +36 -0
  23. package/src/assets/customIcons/phone_light.svg +36 -0
  24. package/src/assets/customIcons/shield.svg +4 -0
  25. package/src/assets/customIcons/trash.svg +9 -0
  26. package/src/assets/customIcons/warning.svg +5 -0
  27. package/src/assets/icons/google.svg +9 -0
  28. package/src/assets/images/bg_dark.svg +5 -0
  29. package/src/assets/images/bg_dark_v1.svg +5 -0
  30. package/src/assets/images/bg_light.svg +5 -0
  31. package/src/assets/images/bg_light_v1.svg +5 -0
  32. package/src/assets/images/en.jpg +0 -0
  33. package/src/assets/images/fr.jpg +0 -0
  34. package/src/components/icons/ban.tsx +77 -0
  35. package/src/components/icons/chatIcon.tsx +19 -0
  36. package/src/components/icons/homeIcon.tsx +25 -0
  37. package/src/components/icons/profileIcon.tsx +29 -0
  38. package/src/components/icons/settingsIcon.tsx +22 -0
  39. package/src/components/reusable/templatePage.tsx +94 -0
  40. package/src/components/reusable/templateSubPage.tsx +66 -0
  41. package/src/components/screens/AccountStatusScreen.tsx +102 -0
  42. package/src/components/screens/AddReportScreen.tsx +135 -0
  43. package/src/components/screens/LegalScreen.tsx +41 -0
  44. package/src/components/screens/VerificationCodeScreen.tsx +66 -0
  45. package/src/components/screens/aboutScreen.tsx +139 -0
  46. package/src/components/screens/forgotPasswordScreen.tsx +60 -0
  47. package/src/components/screens/loginScreen.tsx +165 -0
  48. package/src/components/screens/onBoarding.tsx +178 -0
  49. package/src/components/screens/resetPassword.tsx +115 -0
  50. package/src/components/screens/settingScreen.tsx +85 -0
  51. package/src/components/screens/signUpScreen.tsx +119 -0
  52. package/src/components/screens/splashScreen.tsx +170 -0
  53. package/src/components/ui/OTPInput.tsx +73 -0
  54. package/src/components/ui/PageTopBar.tsx +41 -0
  55. package/src/components/ui/asidebar.tsx +151 -0
  56. package/src/components/ui/badge.tsx +21 -0
  57. package/src/components/ui/button.tsx +58 -0
  58. package/src/components/ui/customPopup.tsx +176 -0
  59. package/src/components/ui/dynamicForm.tsx +144 -0
  60. package/src/components/ui/error.tsx +23 -0
  61. package/src/components/ui/input.tsx +121 -0
  62. package/src/components/ui/languageSelect.tsx +99 -0
  63. package/src/components/ui/pageIndicator.tsx +58 -0
  64. package/src/components/ui/passwordRequirmentCard.tsx +55 -0
  65. package/src/components/ui/sharpButton.tsx +67 -0
  66. package/src/components/ui/sharpContainer.tsx +180 -0
  67. package/src/components/ui/sharpDynamicForm.tsx +148 -0
  68. package/src/components/ui/sharpInput.tsx +148 -0
  69. package/src/components/ui/sharpOtpInput.tsx +228 -0
  70. package/src/components/ui/sharpSelect.tsx +155 -0
  71. package/src/components/ui/successDialog.tsx +97 -0
  72. package/src/components/ui/termsPopup.tsx +56 -0
  73. package/src/components/ui/themeToggle.tsx +98 -0
  74. package/src/components/ui/toast.tsx +163 -0
  75. package/src/components/ui/warningModel.tsx +132 -0
  76. package/src/constants/language.ts +15 -0
  77. package/src/context/LanguageContext.tsx +49 -0
  78. package/src/context/LocationPermissionProvider.tsx +106 -0
  79. package/src/context/ThemeContext.tsx +70 -0
  80. package/src/hooks/useCountdown.ts +21 -0
  81. package/src/hooks/useForgotPasswordForm.ts +46 -0
  82. package/src/hooks/useForm.ts +52 -0
  83. package/src/hooks/useLoginForm.ts +25 -0
  84. package/src/hooks/useResetPasswordForm.ts +75 -0
  85. package/src/hooks/useSignup.tsx +34 -0
  86. package/src/hooks/useSubmitReport.ts +47 -0
  87. package/src/hooks/useUpdateUser.tsx +81 -0
  88. package/src/icons/index.ts +36 -0
  89. package/src/index.ts +62 -0
  90. package/src/service/createApi.ts +57 -0
  91. package/src/service/sendEmail.ts +11 -0
  92. package/src/tokens/themes.ts +19 -0
  93. package/src/tokens/typography.ts +16 -0
  94. package/src/types/formFields.ts +13 -0
  95. package/src/types/icon.ts +6 -0
  96. package/src/types/language.ts +7 -0
  97. package/src/types/menu.ts +9 -0
  98. package/src/types/settingsFields.tsx +9 -0
  99. package/src/types/svg.d.ts +6 -0
  100. package/src/types/validateForm.ts +8 -0
  101. package/src/utils/helpers/groupSettings.ts +21 -0
  102. package/src/utils/inputValidator/customValidator.ts +36 -0
  103. package/src/utils/inputValidator/generalTerms.ts +19 -0
  104. package/src/utils/inputValidator/registerValidator.ts +31 -0
@@ -0,0 +1,135 @@
1
+ import { useState } from "react";
2
+ import { Text, View, TouchableOpacity } from "react-native";
3
+ import TemplateSubPage from "../reusable/templateSubPage";
4
+ import { useSubmitReport } from "../../hooks/useSubmitReport";
5
+ import SharpInput from "../ui/sharpInput";
6
+ import { useAppTheme } from "../../context/ThemeContext";
7
+ import SharpButton from "../ui/sharpButton";
8
+ import { Fonts, textSize } from "../../tokens/typography";
9
+ import ErrorMessage from "../ui/error";
10
+ import { SharpContainer } from "../..";
11
+
12
+ interface ReportProps {
13
+ title: string;
14
+ subtitle: string;
15
+ onBack: () => void;
16
+ categories: { value: string; label: string }[];
17
+ detailsPlaceholder?: string;
18
+ submitLabel: string;
19
+ onSubmit: (target: string, reason: string) => Promise<void>;
20
+ error?: string;
21
+ textLabel?: string;
22
+ selectLabel?:string;
23
+ }
24
+
25
+ export default function AddReport({
26
+ title,
27
+ subtitle,
28
+ onBack,
29
+ categories,
30
+ detailsPlaceholder,
31
+ submitLabel,
32
+ onSubmit,
33
+ error,
34
+ textLabel,
35
+ selectLabel,
36
+ }: ReportProps) {
37
+ const form = useSubmitReport({ onSubmit });
38
+ const { colors } = useAppTheme();
39
+ const [charCount, setCharCount] = useState(0);
40
+ const MAX_CHARS = 500;
41
+
42
+ return (
43
+ <TemplateSubPage
44
+ title={title}
45
+ text={subtitle}
46
+ error={error}
47
+ onBack={onBack}
48
+ >
49
+ {/* Category chips */}
50
+ <View style={{ marginBottom: 16 }}>
51
+ {selectLabel && (
52
+ <Text style={{
53
+ fontSize: textSize.xs,
54
+ fontFamily: Fonts.montserrat.semiBold,
55
+ color: colors.text.primary,
56
+ letterSpacing: 0.8,
57
+ marginBottom: 10,
58
+ }}>
59
+ {selectLabel}
60
+ </Text>
61
+ )}
62
+ <View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
63
+ {categories.map((cat, index) => {
64
+ const isSelected = form.values.target === cat.value;
65
+ return (
66
+ <View style={{ alignSelf: "flex-start", minWidth: 100}} key={index}>
67
+ <SharpContainer
68
+ height={50}
69
+ bgColor={colors.background.main}
70
+ borderColor={isSelected ? colors.primary : colors.semantic.border}
71
+ >
72
+ <TouchableOpacity
73
+ key={cat.value}
74
+ onPress={() => {
75
+ form.setValue("target", cat.value);
76
+ form.clearError("target");
77
+ }}
78
+ style={{ flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 14 }}
79
+ >
80
+ <Text style={{
81
+ fontSize: textSize.xs,
82
+ fontFamily: Fonts.montserrat.regular,
83
+ color: colors.text.secondary,
84
+ }}>
85
+ {cat.label}
86
+ </Text>
87
+ </TouchableOpacity>
88
+ </SharpContainer>
89
+ </View>
90
+ );
91
+ })}
92
+ </View>
93
+ {form.errors.target && <ErrorMessage message={form.errors.target} />}
94
+ </View>
95
+
96
+ {/* Details textarea */}
97
+ <View style={{ marginBottom: 16 }}>
98
+ <SharpInput
99
+ value={form.values.reason}
100
+ onChangeText={(text) => {
101
+ if (text.length <= MAX_CHARS) {
102
+ form.setValue("reason", text);
103
+ setCharCount(text.length);
104
+ }
105
+ }}
106
+ placeholder={detailsPlaceholder}
107
+ label={textLabel}
108
+ multiline
109
+ numberOfLines={4}
110
+ textAlignVertical="top"
111
+ height={100}
112
+ error={form.errors.reason}
113
+ onClearError={() => form.clearError("reason")}
114
+ bgColor={colors.background.main}
115
+ />
116
+ {/* Character counter */}
117
+ <Text style={{
118
+ fontSize: textSize.xs,
119
+ fontFamily: Fonts.montserrat.regular,
120
+ color: charCount >= MAX_CHARS ? colors.semantic.error : colors.text.placeholder,
121
+ textAlign: "right",
122
+ }}>
123
+ {charCount} / {MAX_CHARS}
124
+ </Text>
125
+ </View>
126
+
127
+ <SharpButton
128
+ text={submitLabel}
129
+ bgColor={colors.primary}
130
+ onPress={form.handleSubmit}
131
+ loading={form.loading}
132
+ />
133
+ </TemplateSubPage>
134
+ );
135
+ }
@@ -0,0 +1,41 @@
1
+ import { ScrollView, Text, StyleSheet, View } from "react-native";
2
+ import { useState } from "react";
3
+ import { useAppTheme } from "../../context/ThemeContext";
4
+ import { Fonts, textSize } from "../../tokens/typography";
5
+ import TemplateSubPage from "../reusable/templateSubPage";
6
+ interface PrivacyProps{
7
+ title:string;
8
+ subtitle:string;
9
+ content:string[];
10
+ onBack: ()=>void;
11
+ }
12
+ export default function LegalScreen({title, subtitle, content, onBack}:PrivacyProps){
13
+ const {colors} = useAppTheme();
14
+
15
+ return(
16
+ <TemplateSubPage
17
+ title={title}
18
+ text={subtitle}
19
+ onBack={onBack}
20
+ >
21
+
22
+ {content.map((item, index)=>{
23
+ return (
24
+ <View key={index}>
25
+ <Text style={{
26
+ fontSize: textSize.sm,
27
+ lineHeight: 24,
28
+ fontFamily: Fonts.montserrat.regular,
29
+ marginBottom: 14,
30
+ textAlign:"center",
31
+ color:colors.text.primary
32
+ }}
33
+ >
34
+ {item}
35
+ </Text>
36
+ </View>
37
+ )
38
+ })}
39
+ </TemplateSubPage>
40
+ )
41
+ }
@@ -0,0 +1,66 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Text, TouchableOpacity, View } from "react-native";
3
+ import { useAppTheme } from "../../context/ThemeContext";
4
+ import { textSize, Fonts} from "../../tokens/typography";
5
+ import TemplateSubPage from "../reusable/templateSubPage";
6
+ import OtpInput from "../ui/OTPInput";
7
+ import { useCountdown } from "../../hooks/useCountdown";
8
+ import SharpOtpInput from "../ui/sharpOtpInput";
9
+
10
+ interface VerificationCodeScreenProps {
11
+ onVerify: (code: string) => void;
12
+ title?: string;
13
+ subtitle?: string;
14
+ buttonText?: string;
15
+ error?:string;
16
+ onResend: () => void
17
+ resendText?:string;
18
+ resendBtn?:string;
19
+ onBack: ()=>void;
20
+ loading?: boolean;
21
+ }
22
+
23
+ export default function VerificationCodeScreen({
24
+ onVerify,
25
+ title = "Verification code",
26
+ subtitle = "Enter the 6-digit code sent to your email or phone number",
27
+ buttonText = "Verify and continue",
28
+ error,
29
+ onResend,
30
+ resendText= "Resend in",
31
+ onBack,
32
+ loading,
33
+ resendBtn="Resend Code",
34
+ }: VerificationCodeScreenProps) {
35
+ const { colors } = useAppTheme();
36
+ const [code, setCode] = useState("");
37
+ const { count, reset } = useCountdown(30);
38
+ const handleResend = () => {
39
+ onResend();
40
+ reset();
41
+ };
42
+ return (
43
+ <TemplateSubPage
44
+ title={title}
45
+ text={subtitle}
46
+ buttonText={buttonText}
47
+ handleFunction={() => onVerify(code)}
48
+ error={error}
49
+ onBack={onBack}
50
+ loading={loading}
51
+ >
52
+ <SharpOtpInput length={4} onChange={setCode} />
53
+ {count !== 0 ? (
54
+ <Text style={{ textAlign: "center", color: colors.text.placeholder, fontSize: textSize.xs, fontFamily: Fonts.montserrat.regular}}>
55
+ {resendText}: {count} seconds
56
+ </Text>
57
+ ) : (
58
+ <TouchableOpacity onPress={handleResend}>
59
+ <Text style={{ textAlign: "center", color: colors.primary, fontSize: textSize.sm, fontFamily: Fonts.montserrat.regular }}>
60
+ {resendBtn}
61
+ </Text>
62
+ </TouchableOpacity>
63
+ )}
64
+ </TemplateSubPage>
65
+ );
66
+ }
@@ -0,0 +1,139 @@
1
+ import { StyleSheet, Text, View } from "react-native";
2
+ import TemplateSubPage from "../reusable/templateSubPage";
3
+ import { useAppTheme } from "../../context/ThemeContext";
4
+ import { Fonts, textSize } from "../../tokens/typography";
5
+ import Badge from "../ui/badge";
6
+
7
+ interface AboutProps {
8
+ title: string;
9
+ subtitle: string;
10
+ logo: React.ReactNode;
11
+ appName: string;
12
+ slogan: string;
13
+ sections: { title: string; text: string }[];
14
+ onBack: () => void;
15
+ }
16
+
17
+ export default function AboutScreen({
18
+ title,
19
+ subtitle,
20
+ logo,
21
+ appName,
22
+ slogan,
23
+ sections,
24
+ onBack,
25
+ }: AboutProps) {
26
+ const { colors } = useAppTheme();
27
+ const styles = createStyles(colors);
28
+
29
+ return (
30
+ <TemplateSubPage
31
+ title={title}
32
+ text={subtitle}
33
+ onBack={onBack}
34
+ >
35
+ <View style={styles.container}>
36
+ {/* Header */}
37
+ <View style={styles.header}>
38
+ <View style={{marginBottom:20, flexDirection:"row", justifyContent:"space-between", alignItems:"center"}}>
39
+ {logo}
40
+ <Badge
41
+ text="v1.0.0"
42
+ bgColor={colors.text.secondary+"40"}
43
+ textColor={colors.text.secondary}
44
+ />
45
+ </View>
46
+
47
+ <Text style={styles.appName}>{appName}</Text>
48
+
49
+ <Text style={styles.slogan}>
50
+ {slogan}
51
+ </Text>
52
+ </View>
53
+
54
+ {/* Sections */}
55
+ <View style={styles.sections}>
56
+ {sections.map((section, index) => (
57
+ <View key={index} style={styles.card}>
58
+ <Text style={styles.sectionTitle}>
59
+ {section.title}
60
+ </Text>
61
+
62
+ <Text style={styles.sectionText}>
63
+ {section.text}
64
+ </Text>
65
+ </View>
66
+ ))}
67
+ </View>
68
+
69
+ {/* Footer */}
70
+ <View style={styles.footer}>
71
+ <Text style={styles.copyright}>
72
+ © 2026 {appName}
73
+ </Text>
74
+ </View>
75
+ </View>
76
+ </TemplateSubPage>
77
+ );
78
+ }
79
+
80
+ const createStyles = (colors: any) =>
81
+ StyleSheet.create({
82
+ container: {
83
+ gap: 28,
84
+ paddingBottom: 24,
85
+ },
86
+
87
+ header: {
88
+ gap: 12,
89
+ },
90
+
91
+
92
+ appName: {
93
+ fontSize: textSize.lg,
94
+ fontFamily: Fonts.montserrat.bold,
95
+ color: colors.text.primary,
96
+ },
97
+
98
+ slogan: {
99
+ fontSize: textSize.md,
100
+ fontFamily: Fonts.montserrat.regular,
101
+ color: colors.text.secondary,
102
+ lineHeight: 22,
103
+ },
104
+
105
+ sections: {
106
+ gap: 16,
107
+ },
108
+
109
+ card: {
110
+ backgroundColor: colors.background.card,
111
+ borderRadius: 12,
112
+ padding: 18,
113
+ gap: 10,
114
+ },
115
+
116
+ sectionTitle: {
117
+ fontSize: textSize.lg,
118
+ fontFamily: Fonts.montserrat.semiBold,
119
+ color: colors.text.primary,
120
+ },
121
+
122
+ sectionText: {
123
+ fontSize: textSize.md,
124
+ fontFamily: Fonts.montserrat.regular,
125
+ color: colors.text.secondary,
126
+ lineHeight: 24,
127
+ },
128
+
129
+ footer: {
130
+ alignItems: "center",
131
+ marginTop: 8,
132
+ },
133
+
134
+ copyright: {
135
+ fontSize: textSize.sm,
136
+ fontFamily: Fonts.montserrat.regular,
137
+ color: colors.text.placeholder,
138
+ },
139
+ });
@@ -0,0 +1,60 @@
1
+ import TemplateSubPage from "../reusable/templateSubPage";
2
+ import Input from "../ui/input";
3
+ import "../../utils/inputValidator/customValidator";
4
+ import { useForgotPasswordForm } from "../../hooks/useForgotPasswordForm";
5
+ import SharpInput from "../ui/sharpInput";
6
+ import MailIcon from "../../assets/customIcons/email.svg";
7
+
8
+
9
+
10
+ interface ForgetPwScreenProps {
11
+ onSubmit: (input: string) => Promise<void>;
12
+ title?: string;
13
+ subtitle?: string;
14
+ buttonText?: string;
15
+ errorMessage?: string;
16
+ inputLabel?:string;
17
+ inputPlaceholder: string;
18
+ onBack: ()=>void;
19
+ }
20
+
21
+ export default function ForgotPwScreen({
22
+ onSubmit,
23
+ title = "Forgot password",
24
+ subtitle = "Enter your email address below, and we’ll send you an opt code to reset your password.",
25
+ buttonText = "Send OTP",
26
+ errorMessage,
27
+ inputLabel,
28
+ inputPlaceholder,
29
+ onBack,
30
+ }: ForgetPwScreenProps) {
31
+
32
+ const form = useForgotPasswordForm({onSubmit});
33
+
34
+ return (
35
+ <TemplateSubPage
36
+ title={title}
37
+ text={subtitle}
38
+ buttonText={buttonText}
39
+ handleFunction={form.handleSubmit}
40
+ error={errorMessage}
41
+ onBack={onBack}
42
+ loading={form.loading}
43
+ >
44
+ <SharpInput
45
+ value={form.values.input}
46
+ onChangeText={(text) => form.setValue("input", text)}
47
+ placeholder={inputPlaceholder}
48
+ label={inputLabel}
49
+ keyboardType="email-address"
50
+ iconLeft={(color) => <MailIcon width={18} height={18} color={color}/>}
51
+ error={form.errors.input}
52
+ autoCapitalize="none"
53
+ autoComplete="email"
54
+ textContentType="emailAddress"
55
+ returnKeyType="next"
56
+ onClearError={() => form.clearError("input")}
57
+ />
58
+ </TemplateSubPage>
59
+ );
60
+ }
@@ -0,0 +1,165 @@
1
+ import ErrorMessage from "../ui/error";
2
+ import { textSize, Fonts } from "../../tokens/typography";
3
+ import { useAppTheme } from "../../context/ThemeContext";
4
+ import { useLoginForm } from "../../hooks/useLoginForm";
5
+ import { FontAwesome } from "@expo/vector-icons";
6
+ import {
7
+ Image,
8
+ StyleSheet,
9
+ Text, TouchableOpacity, View,
10
+ } from "react-native";
11
+ import TemplatePage from "../reusable/templatePage";
12
+ import SharpButton from "../ui/sharpButton";
13
+ import SharpInput from "../ui/sharpInput";
14
+ import Google from "../../assets/icons/google.svg"
15
+ import SharpContainer from "../ui/sharpContainer";
16
+ import MailIcon from "../../assets/customIcons/email.svg";
17
+ import EyeIcon from "../../assets/customIcons/eye.svg";
18
+ import EyeOffIcon from "../../assets/customIcons/eye_off.svg";
19
+ import LockIcon from "../../assets/customIcons/lock_closed.svg"
20
+ interface LoginScreenProps {
21
+ title?: string;
22
+ subtitle?: string;
23
+ onLogin: (email: string, password: string) => Promise<void>;
24
+ onForgotPassword: () => void;
25
+ onSignUp: () => void;
26
+ onGoogleLogin?: () => void;
27
+ errorMessage?: string | null;
28
+ emailLabel?: string;
29
+ passwordLabel?: string;
30
+ emailPlaceholder?: string;
31
+ passwordPlaceholder?: string;
32
+ forgotPasswordLabel?: string;
33
+ loginButtonLabel?: string;
34
+ dividerLabel?: string;
35
+ noAccountLabel?: string;
36
+ signUpLabel?: string;
37
+ googleLabel?: string;
38
+ }
39
+
40
+ export default function LoginScreen({
41
+ title = "Login here",
42
+ subtitle = "Welcome back you've been missed!",
43
+ onLogin,
44
+ onForgotPassword,
45
+ onSignUp,
46
+ onGoogleLogin,
47
+ errorMessage = null,
48
+ emailLabel = "Email address",
49
+ passwordLabel = "Password",
50
+ emailPlaceholder = "Enter you email...",
51
+ passwordPlaceholder = "Enter you password...",
52
+ forgotPasswordLabel = "Forgot your password?",
53
+ loginButtonLabel = "Sign in",
54
+ dividerLabel = "Or continue with",
55
+ noAccountLabel = "Don't have an account?",
56
+ signUpLabel = "Create one",
57
+ googleLabel,
58
+ }: LoginScreenProps) {
59
+ const { colors, isDark } = useAppTheme();
60
+ const form = useLoginForm({ onLogin });
61
+
62
+ return (
63
+ <TemplatePage title={title}
64
+ subtitle={subtitle}
65
+ body={
66
+ <>
67
+ {errorMessage && <ErrorMessage message={errorMessage} position="center" fontSize={textSize.md} />}
68
+ <View style={{ marginBottom: 15 }}>
69
+ <SharpInput
70
+ label={emailLabel}
71
+ value={form.values.email}
72
+ onChangeText={(text)=>form.setValue("email", text)}
73
+ placeholder={emailPlaceholder}
74
+ keyboardType="email-address"
75
+ error={form.errors.email}
76
+ autoCapitalize="none"
77
+ autoComplete="email"
78
+ textContentType="emailAddress"
79
+ returnKeyType="next"
80
+ onClearError={() => form.clearError("email")}
81
+ iconLeft={(color)=><MailIcon width={18} height={18} color={color}/>}
82
+ />
83
+ </View>
84
+
85
+ <View style={{ marginBottom: 20 }}>
86
+ <SharpInput
87
+ label={passwordLabel}
88
+ value={form.values.password}
89
+ onChangeText={(value) => form.setValue("password", value)}
90
+ placeholder={passwordPlaceholder}
91
+ iconRight={form.showPassword ? (color)=><EyeIcon width={18} height={18} color={color}/> : (color)=><EyeOffIcon width={18} height={18} color={color}/>}
92
+ onPressIconRight={() => form.setShowPassword(!form.showPassword)}
93
+ secureTextEntry={!form.showPassword}
94
+ textContentType="password"
95
+ returnKeyType="done"
96
+ error={form.errors.password}
97
+ onClearError={() => form.clearError("password")}
98
+ iconLeft={(color)=><LockIcon width={18} height={18} color={color}/>}
99
+ />
100
+ </View>
101
+
102
+ <SharpButton text={loginButtonLabel} bgColor={colors.primary} onPress={form.handleSubmit} height={50} loading={form.loading}/>
103
+
104
+ <TouchableOpacity onPress={onForgotPassword} activeOpacity={0.7} style={{ alignSelf: "flex-end", marginTop: 14 }}>
105
+ <Text style={{ fontSize: textSize.xs, color: colors.primary, fontFamily: Fonts.montserrat.semiBold }}>
106
+ {forgotPasswordLabel}
107
+ </Text>
108
+ </TouchableOpacity>
109
+ </>
110
+ }
111
+ footer={
112
+ <View style={{flex: 1, justifyContent: "space-between"}}>
113
+ {(onGoogleLogin) && (
114
+ <View>
115
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 12, marginVertical: 20, marginHorizontal: 20 }}>
116
+ <View style={{ flex: 1, height: 1, backgroundColor: colors.text.secondary, opacity: 0.5 }} />
117
+ <Text style={{ fontSize: textSize.xs, color: colors.text.secondary, fontFamily: Fonts.montserrat.regular }}>
118
+ {dividerLabel}
119
+ </Text>
120
+ <View style={{ flex: 1, height: 1, backgroundColor: colors.text.secondary, opacity: 0.5 }} />
121
+ </View>
122
+ <View style={{marginHorizontal: 28}}>
123
+ <SharpContainer
124
+ bgColor={colors.background.main}
125
+ borderColor={colors.text.primary}
126
+ height={50}
127
+ >
128
+ <TouchableOpacity style={{ flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 10 }} onPress={onGoogleLogin}>
129
+ <Google width={20} height={20} />
130
+ <Text style={{
131
+ fontSize: textSize.sm,
132
+ fontFamily: Fonts.montserrat.semiBold,
133
+ color: colors.text.primary,
134
+ }}>
135
+ {googleLabel}
136
+ </Text>
137
+ </TouchableOpacity>
138
+ </SharpContainer>
139
+ </View>
140
+ </View>
141
+ )}
142
+
143
+ <View style={{ flexDirection: "row", justifyContent: "center", gap: 4, marginBottom: 40 }}>
144
+ <Text style={{ fontSize: textSize.xs, color: colors.text.secondary, fontFamily: Fonts.montserrat.regular }}>
145
+ {noAccountLabel}
146
+ </Text>
147
+ <TouchableOpacity onPress={onSignUp} activeOpacity={0.7}>
148
+ <Text style={{ fontSize: textSize.xs, color: colors.primary, fontFamily: Fonts.montserrat.semiBold }}>
149
+ {signUpLabel}
150
+ </Text>
151
+ </TouchableOpacity>
152
+ </View>
153
+ </View>
154
+ }
155
+ />
156
+ );
157
+ }
158
+
159
+
160
+ const styles = StyleSheet.create({
161
+ socialIcons:{
162
+ alignItems: "center",
163
+ justifyContent: "center"
164
+ }
165
+ })