Browse Source

Worked on forgot password confirm screen, navigation and its structure

Khubaib 4 months ago
parent
commit
7f894ce05f

+ 19 - 0
app/src/main/java/com/fastest/pass/forgotpassword/di/ForgotPasswordConfirmModule.kt

@@ -0,0 +1,19 @@
+package com.fastest.pass.forgotpassword.di
+
+import com.fastest.pass.forgotpassword.utils.ForgotPasswordConfirmNavigation
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object ForgotPasswordConfirmModule {
+
+    @Provides
+    @Singleton
+    fun provideNavigation(): ForgotPasswordConfirmNavigation {
+        return ForgotPasswordConfirmNavigation()
+    }
+}

+ 381 - 0
app/src/main/java/com/fastest/pass/forgotpassword/presentation/components/ForgotPasswordConfirmScreen.kt

@@ -0,0 +1,381 @@
+package com.fastest.pass.forgotpassword.presentation.components
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.focus.FocusManager
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.platform.SoftwareKeyboardController
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.fastest.pass.R
+import com.fastest.pass.login.presentation.components.ClickType
+
+enum class ClickTypeForgotConfirm {
+    GO_BACK,
+    OPEN_LOGIN_SCREEN
+}
+
+@Composable
+fun ForgotPasswordConfirmScreen(clickType: (ClickTypeForgotConfirm) -> Unit) {
+    val keyboardController = LocalSoftwareKeyboardController.current
+    val focusManager = LocalFocusManager.current
+
+    Box(
+        modifier = Modifier
+            .background(colorResource(id = R.color.blue_login))
+            .fillMaxSize()
+            .statusBarsPadding()
+    ) {
+        Column(
+            modifier = Modifier
+                .fillMaxSize()
+                .padding(top = 0.dp)
+                .statusBarsPadding()
+        ) {
+            ShowHeaderFPC(text = stringResource(id = R.string.forgot_password)) { clickTypeForgotConfirm ->
+                clickType(clickTypeForgotConfirm)
+            }
+
+            Column(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .padding(top = 25.dp)
+                    .clip(RoundedCornerShape(topStart = 35.dp, topEnd = 35.dp))
+                    .background(
+                        colorResource(id = R.color.light_gray_login),
+                    )
+            ) {
+                ForgotConfirmText(R.string.create_new_password_continue)
+                Spacer(modifier = Modifier.height(50.dp))
+                PasswordTextFieldFPC(keyboardController = keyboardController, focusManager = focusManager)
+                Spacer(modifier = Modifier.height(20.dp))
+                PasswordConfirmTextFieldFPC(keyboardController, focusManager)
+                Spacer(modifier = Modifier.height(20.dp))
+                ContinueButton(buttonText = R.string.continue_text) { clickTypeForgotConfirm ->
+                    clickType(clickTypeForgotConfirm)
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun ColumnScope.ShowHeaderFPC(text: String, clickType: (ClickTypeForgotConfirm) -> Unit) {
+    Row(
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(top = 30.dp)
+            .fillMaxWidth(),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        IconButton(
+            onClick = {
+                clickType.invoke(ClickTypeForgotConfirm.GO_BACK)
+            },
+            modifier = Modifier
+                .padding(start = 30.dp)
+                .size(24.dp, 24.dp)
+        ) {
+            Image(
+                painter = painterResource(id = R.drawable.arrow_left),
+                contentDescription = "Arrow-Back",
+                modifier = Modifier.size(24.dp, 24.dp)
+            )
+        }
+        Surface(
+            modifier = Modifier
+                .padding(start = 15.dp),
+            color = colorResource(id = R.color.transparent)
+        ) {
+            Text(
+                text = text,
+                color = colorResource(id = R.color.white),
+                style = MaterialTheme.typography.displayLarge.copy(
+                    fontSize = 24.sp
+                ),
+            )
+        }
+    }
+}
+
+@Composable
+fun ColumnScope.ForgotConfirmText(
+    forgotText: Int,
+) {
+    Text(
+        text = stringResource(id = R.string.create_new_password),
+        color = colorResource(id = R.color.gray_splash),
+        style = MaterialTheme.typography.headlineLarge.copy(
+            fontSize = 26.sp
+        ),
+        lineHeight = 20.sp,
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(start = 30.dp, top = 35.dp)
+    )
+    Text(
+        text = stringResource(id = forgotText),
+        color = colorResource(id = R.color.gray_splash),
+        style = MaterialTheme.typography.displayLarge.copy(
+            fontSize = 18.sp
+        ),
+        lineHeight = 25.sp,
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(start = 30.dp, top = 5.dp, end = 50.dp)
+
+    )
+}
+
+@Composable
+fun ColumnScope.PasswordTextFieldFPC(
+    keyboardController: SoftwareKeyboardController?,
+    focusManager: FocusManager
+) {
+    var passwordText by remember { mutableStateOf("") }
+    var passwordVisible by remember { mutableStateOf(false) }
+
+    TextField(
+        value = passwordText,
+        onValueChange = {
+            passwordText = it
+        },
+        textStyle = MaterialTheme.typography.displaySmall,
+        modifier = Modifier
+            .padding(start = 30.dp, end = 30.dp)
+            .align(Alignment.Start)
+            .fillMaxWidth()
+            .height(60.dp)
+            .border(
+                1.dp,
+                color = colorResource(id = R.color.gray_border_textfield),
+                shape = RoundedCornerShape(16.dp)
+            )
+            .background(color = colorResource(id = R.color.transparent)),
+        shape = RoundedCornerShape(16.dp),
+        placeholder = {
+            Text(text = stringResource(id = R.string.enter_new_password),
+                color = colorResource(id = R.color.gray_splash))
+        },
+//        label = {
+//            Text(text = context.getString(R.string.password),
+//                style = MaterialTheme.typography.customTypography.bodyLarge
+//            )
+//        },
+        maxLines = 1,
+        colors = TextFieldDefaults.colors(
+            focusedLabelColor = colorResource(id = R.color.gray_splash),
+            unfocusedContainerColor = colorResource(id = R.color.transparent),
+            focusedContainerColor = colorResource(id = R.color.transparent),
+            focusedIndicatorColor = colorResource(id = R.color.transparent),
+            disabledIndicatorColor = colorResource(id = R.color.transparent),
+            unfocusedIndicatorColor = colorResource(id = R.color.transparent),
+            cursorColor = colorResource(id = R.color.gray_splash),
+        ),
+        keyboardOptions = KeyboardOptions(
+            keyboardType = KeyboardType.Password,
+            imeAction = ImeAction.Done
+        ),
+        keyboardActions = KeyboardActions(
+            onDone = {
+                focusManager.clearFocus()
+                keyboardController?.hide()
+            }
+        ),
+        visualTransformation =
+        if (passwordVisible) VisualTransformation.Companion.None
+        else PasswordVisualTransformation(),
+
+        trailingIcon = {
+            val description = if (passwordVisible) "Hide Password"
+            else "Show Password"
+
+            IconButton(onClick = {
+                passwordVisible = !passwordVisible
+            })
+            {
+                if (passwordVisible) {
+                    Image(
+                        painter = painterResource(id = R.drawable.eye_open),
+                        contentDescription = description,
+                        modifier = Modifier.size(24.dp)
+                    )
+                }
+                else {
+                    Image(
+                        painter = painterResource(id = R.drawable.eye_slash3x),
+                        contentDescription = description,
+                        modifier = Modifier
+                            .size(24.dp),
+                        colorFilter = ColorFilter.tint(
+                            colorResource(id = R.color.gray_splash)
+                        )
+                    )
+                }
+            }
+        }
+    )
+}
+
+@Composable
+fun ColumnScope.PasswordConfirmTextFieldFPC(
+    keyboardController: SoftwareKeyboardController?,
+    focusManager: FocusManager
+) {
+    var passwordText by remember { mutableStateOf("") }
+    var passwordVisible by remember { mutableStateOf(false) }
+
+    TextField(
+        value = passwordText,
+        onValueChange = {
+            passwordText = it
+        },
+        textStyle = MaterialTheme.typography.displaySmall,
+        modifier = Modifier
+            .padding(start = 30.dp, end = 30.dp)
+            .align(Alignment.Start)
+            .fillMaxWidth()
+            .height(60.dp)
+            .border(
+                1.dp,
+                color = colorResource(id = R.color.gray_border_textfield),
+                shape = RoundedCornerShape(16.dp)
+            )
+            .background(color = colorResource(id = R.color.transparent)),
+        shape = RoundedCornerShape(16.dp),
+        placeholder = {
+            Text(text = stringResource(id = R.string.confirm_new_password),
+                color = colorResource(id = R.color.gray_splash))
+        },
+//        label = {
+//            Text(text = context.getString(R.string.password),
+//                style = MaterialTheme.typography.customTypography.bodyLarge
+//            )
+//        },
+        maxLines = 1,
+        colors = TextFieldDefaults.colors(
+            focusedLabelColor = colorResource(id = R.color.gray_splash),
+            unfocusedContainerColor = colorResource(id = R.color.transparent),
+            focusedContainerColor = colorResource(id = R.color.transparent),
+            focusedIndicatorColor = colorResource(id = R.color.transparent),
+            disabledIndicatorColor = colorResource(id = R.color.transparent),
+            unfocusedIndicatorColor = colorResource(id = R.color.transparent),
+            cursorColor = colorResource(id = R.color.gray_splash),
+        ),
+        keyboardOptions = KeyboardOptions(
+            keyboardType = KeyboardType.Password,
+            imeAction = ImeAction.Done
+        ),
+        keyboardActions = KeyboardActions(
+            onDone = {
+                focusManager.clearFocus()
+                keyboardController?.hide()
+            }
+        ),
+        visualTransformation =
+        if (passwordVisible) VisualTransformation.Companion.None
+        else PasswordVisualTransformation(),
+
+        trailingIcon = {
+            val description = if (passwordVisible) "Hide Password"
+            else "Show Password"
+
+            IconButton(onClick = {
+                passwordVisible = !passwordVisible
+            })
+            {
+                if (passwordVisible) {
+                    Image(
+                        painter = painterResource(id = R.drawable.eye_open),
+                        contentDescription = description,
+                        modifier = Modifier.size(24.dp)
+                    )
+                }
+                else {
+                    Image(
+                        painter = painterResource(id = R.drawable.eye_slash3x),
+                        contentDescription = description,
+                        modifier = Modifier
+                            .size(24.dp),
+                        colorFilter = ColorFilter.tint(
+                            colorResource(id = R.color.gray_splash)
+                        )
+                    )
+                }
+            }
+        }
+    )
+}
+
+@Composable
+fun ColumnScope.ContinueButton(buttonText: Int, clickType: (ClickTypeForgotConfirm) -> Unit) {
+    Button(
+        modifier = Modifier
+            .padding(start = 30.dp, end = 30.dp,)
+            .background(colorResource(id = R.color.transparent))
+            .fillMaxWidth()
+            .height(60.dp)
+            .clickable() { },
+        onClick = {
+            clickType.invoke(ClickTypeForgotConfirm.OPEN_LOGIN_SCREEN)
+        },
+        shape = RoundedCornerShape(15.dp),
+//            border = BorderStroke(25.dp, colorResource(id = R.color.black)),
+        colors = ButtonDefaults.buttonColors(
+            contentColor = colorResource(id = R.color.white),
+            containerColor = colorResource(id = R.color.red_login_button),
+        ),
+    )
+    {
+        Text(
+            text = stringResource(id = buttonText),
+            style = MaterialTheme.typography.bodyMedium.copy(
+                fontSize = 20.sp,
+                color = colorResource(id = R.color.white)
+            ),
+            textAlign = TextAlign.Center
+        )
+    }
+}

+ 0 - 1
app/src/main/java/com/fastest/pass/forgotpassword/presentation/components/ForgotPasswordVerifyScreen.kt

@@ -100,7 +100,6 @@ fun ForgotPasswordVerifyScreen(clickType: (ClickTypeForgotVerify) -> Unit) {
                 Spacer(modifier = Modifier.height(20.dp))
                 ResendOTPCode()
             }
-
         }
     }
 }

+ 54 - 0
app/src/main/java/com/fastest/pass/forgotpassword/presentation/ui/ForgotPasswordConfirmFragment.kt

@@ -0,0 +1,54 @@
+package com.fastest.pass.forgotpassword.presentation.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import com.fastest.pass.forgotpassword.presentation.components.ClickTypeForgotConfirm
+import com.fastest.pass.forgotpassword.presentation.components.ForgotPasswordConfirmScreen
+import com.fastest.pass.forgotpassword.presentation.viewmodels.ForgotPasswordConfirmViewModel
+import com.fastest.pass.forgotpassword.utils.ForgotPasswordConfirmNavigation
+import com.fastest.pass.forgotpassword.utils.ForgotPasswordConfirmRoute
+import com.fastest.pass.ui.theme.FastestPassTheme
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class ForgotPasswordConfirmFragment : Fragment() {
+
+    val viewmodel: ForgotPasswordConfirmViewModel by viewModels()
+
+    @Inject
+    lateinit var navigation: ForgotPasswordConfirmNavigation
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        navigation.navigate(this)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return ComposeView(requireActivity()).apply {
+            setContent {
+                FastestPassTheme {
+                    ForgotPasswordConfirmScreen { clickTypeForgotConfirm ->
+                        when (clickTypeForgotConfirm) {
+                            ClickTypeForgotConfirm.GO_BACK -> {
+                                viewmodel.navigateTo(ForgotPasswordConfirmRoute.GoBackToForgotVerify)
+                            }
+                            ClickTypeForgotConfirm.OPEN_LOGIN_SCREEN -> {
+                                viewmodel.navigateTo(ForgotPasswordConfirmRoute.OpenLoginScreen)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 1 - 1
app/src/main/java/com/fastest/pass/forgotpassword/presentation/ui/ForgotPasswordVerifyFragment.kt

@@ -43,7 +43,7 @@ class ForgotPasswordVerifyFragment : Fragment() {
                                 viewmodel.navigateTo(ForgotPasswordVerifyRoute.GoBackToForgotPassword)
                             }
                             ClickTypeForgotVerify.GO_TO_CONFIRM_PASSWORD -> {
-
+                                viewmodel.navigateTo(ForgotPasswordVerifyRoute.OpenForgotConfirmPasswordScreen)
                             }
                         }
                     }

+ 15 - 0
app/src/main/java/com/fastest/pass/forgotpassword/presentation/viewmodels/ForgotPasswordConfirmViewModel.kt

@@ -0,0 +1,15 @@
+package com.fastest.pass.forgotpassword.presentation.viewmodels
+
+import androidx.lifecycle.ViewModel
+import com.fastest.pass.forgotpassword.utils.ForgotPasswordConfirmRoute
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class ForgotPasswordConfirmViewModel : ViewModel() {
+
+    private val _router = MutableStateFlow<ForgotPasswordConfirmRoute>(ForgotPasswordConfirmRoute.OpenNoneScreen)
+    val router: MutableStateFlow<ForgotPasswordConfirmRoute> = _router
+
+    fun navigateTo(forgotPasswordConfirmRoute: ForgotPasswordConfirmRoute) {
+        _router.value = forgotPasswordConfirmRoute
+    }
+}

+ 34 - 0
app/src/main/java/com/fastest/pass/forgotpassword/utils/ForgotPasswordConfirmNavigation.kt

@@ -0,0 +1,34 @@
+package com.fastest.pass.forgotpassword.utils
+
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.NavOptions
+import androidx.navigation.fragment.findNavController
+import com.fastest.pass.R
+import com.fastest.pass.forgotpassword.presentation.ui.ForgotPasswordConfirmFragment
+
+class ForgotPasswordConfirmNavigation {
+
+    fun navigate(forgotPasswordConfirmFragment: ForgotPasswordConfirmFragment) {
+        forgotPasswordConfirmFragment.lifecycleScope.launchWhenStarted {
+            forgotPasswordConfirmFragment.viewmodel.router.collect { router ->
+                when (router) {
+                    ForgotPasswordConfirmRoute.GoBackToForgotVerify -> {
+                        forgotPasswordConfirmFragment.findNavController().popBackStack()
+                    }
+                    ForgotPasswordConfirmRoute.OpenNoneScreen -> {}
+                    ForgotPasswordConfirmRoute.OpenLoginScreen -> {
+                        forgotPasswordConfirmFragment.findNavController().navigate(
+                            R.id.loginFragment,
+                            null,
+                            NavOptions.Builder()
+                                .setPopUpTo(R.id.welcomeFragment, false)
+                                .build()
+                        )
+                    }
+                }
+
+                forgotPasswordConfirmFragment.viewmodel.navigateTo(ForgotPasswordConfirmRoute.OpenNoneScreen)
+            }
+        }
+    }
+}

+ 7 - 0
app/src/main/java/com/fastest/pass/forgotpassword/utils/ForgotPasswordConfirmRoute.kt

@@ -0,0 +1,7 @@
+package com.fastest.pass.forgotpassword.utils
+
+sealed class ForgotPasswordConfirmRoute {
+    data object GoBackToForgotVerify : ForgotPasswordConfirmRoute()
+    data object OpenNoneScreen : ForgotPasswordConfirmRoute()
+    data object OpenLoginScreen : ForgotPasswordConfirmRoute()
+}

+ 2 - 1
app/src/main/java/com/fastest/pass/forgotpassword/utils/ForgotPasswordVerifyNavigation.kt

@@ -2,6 +2,7 @@ package com.fastest.pass.forgotpassword.utils
 
 import androidx.lifecycle.lifecycleScope
 import androidx.navigation.fragment.findNavController
+import com.fastest.pass.R
 import com.fastest.pass.forgotpassword.presentation.ui.ForgotPasswordVerifyFragment
 
 class ForgotPasswordVerifyNavigation {
@@ -15,7 +16,7 @@ class ForgotPasswordVerifyNavigation {
                         forgotPasswordVerifyFragment.findNavController().popBackStack()
                     }
                     ForgotPasswordVerifyRoute.OpenForgotConfirmPasswordScreen -> {
-
+                        forgotPasswordVerifyFragment.findNavController().navigate(R.id.forgotPasswordConfirmFragment)
                     }
                 }
 

+ 5 - 0
app/src/main/res/navigation/nav_graph.xml

@@ -42,4 +42,9 @@
         android:name="com.fastest.pass.forgotpassword.presentation.ui.ForgotPasswordVerifyFragment"
         android:label="ForgotPasswordVerifyFragment" />
 
+    <fragment
+        android:id="@+id/forgotPasswordConfirmFragment"
+        android:name="com.fastest.pass.forgotpassword.presentation.ui.ForgotPasswordConfirmFragment"
+        android:label="ForgotPasswordConfirmFragment" />
+
 </navigation>

+ 5 - 0
app/src/main/res/values/strings.xml

@@ -28,5 +28,10 @@
     <string name="enter_otp">Please enter the OTP sent to</string>
     <string name="need_new_code">Need a new Code?</string>
     <string name="resend">Resend</string>
+    <string name="create_new_password">Create new password</string>
+    <string name="create_new_password_continue">Hello! Please create your new password to continue.</string>
+    <string name="enter_new_password">Enter new password</string>
+    <string name="confirm_new_password">Confirm new password</string>
+    <string name="continue_text">Continue</string>
 
 </resources>