Pārlūkot izejas kodu

Worked on new item form api structure, clean architecture, DI and password form data and api integration

Khubaib 1 nedēļu atpakaļ
vecāks
revīzija
e6d586ce0e

+ 6 - 0
app/src/main/java/com/fastest/pass/home/data/model/NewItemFormField.kt

@@ -0,0 +1,6 @@
+package com.fastest.pass.home.data.model
+
+data class NewItemFormField(
+    val key: String,
+    val value: String
+)

+ 5 - 0
app/src/main/java/com/fastest/pass/home/data/model/NewItemFormRequest.kt

@@ -0,0 +1,5 @@
+package com.fastest.pass.home.data.model
+
+data class NewItemFormRequest(
+    val fields: List<NewItemFormField>
+)

+ 8 - 0
app/src/main/java/com/fastest/pass/home/data/model/NewItemFormResponse.kt

@@ -0,0 +1,8 @@
+package com.fastest.pass.home.data.model
+
+data class NewItemFormResponse(
+    val item: String,
+    val vault: String,
+    val folder: String,
+    val user_id: Int
+)

+ 16 - 0
app/src/main/java/com/fastest/pass/home/data/remote/NewItemFormApiService.kt

@@ -0,0 +1,16 @@
+package com.fastest.pass.home.data.remote
+
+import com.fastest.pass.app.WebResponse
+import com.fastest.pass.home.data.model.NewItemFormRequest
+import com.fastest.pass.home.data.model.NewItemFormResponse
+import retrofit2.http.Body
+import retrofit2.http.POST
+import retrofit2.http.Path
+
+interface NewItemFormApiService {
+    @POST("vaults/{vault}/items")
+    suspend fun newItemForm(
+        @Path("vault") vault: String,
+        @Body newItemFormRequest: NewItemFormRequest
+    ) : WebResponse<NewItemFormResponse>
+}

+ 18 - 0
app/src/main/java/com/fastest/pass/home/data/repository/NewItemFormRepositoryImpl.kt

@@ -0,0 +1,18 @@
+package com.fastest.pass.home.data.repository
+
+import com.fastest.pass.app.WebResponse
+import com.fastest.pass.home.data.model.NewItemFormRequest
+import com.fastest.pass.home.data.model.NewItemFormResponse
+import com.fastest.pass.home.data.remote.NewItemFormApiService
+import com.fastest.pass.home.domain.repository.NewItemFormRepository
+
+class NewItemFormRepositoryImpl(
+    private val newItemFormApiService: NewItemFormApiService
+) : NewItemFormRepository {
+    override suspend fun newItemFormRepository(
+        vault: String,
+        newItemFormRequest: NewItemFormRequest
+    ): WebResponse<NewItemFormResponse> {
+        return newItemFormApiService.newItemForm(vault, newItemFormRequest)
+    }
+}

+ 16 - 0
app/src/main/java/com/fastest/pass/home/di/NewItemFormModule.kt

@@ -1,10 +1,14 @@
 package com.fastest.pass.home.di
 
+import com.fastest.pass.home.data.remote.NewItemFormApiService
+import com.fastest.pass.home.data.repository.NewItemFormRepositoryImpl
+import com.fastest.pass.home.domain.repository.NewItemFormRepository
 import com.fastest.pass.home.utils.NewItemFormNavigation
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn
 import dagger.hilt.components.SingletonComponent
+import retrofit2.Retrofit
 import javax.inject.Singleton
 
 @Module
@@ -16,4 +20,16 @@ object NewItemFormModule {
     fun provideNavigation() : NewItemFormNavigation {
         return NewItemFormNavigation()
     }
+
+    @Provides
+    @Singleton
+    fun provideNewItemFormApiService(retrofit: Retrofit) : NewItemFormApiService {
+        return retrofit.create(NewItemFormApiService::class.java)
+    }
+
+    @Provides
+    @Singleton
+    fun provideNewItemFormRepository(newItemFormApiService: NewItemFormApiService) : NewItemFormRepository {
+        return NewItemFormRepositoryImpl(newItemFormApiService)
+    }
 }

+ 9 - 0
app/src/main/java/com/fastest/pass/home/domain/repository/NewItemFormRepository.kt

@@ -0,0 +1,9 @@
+package com.fastest.pass.home.domain.repository
+
+import com.fastest.pass.app.WebResponse
+import com.fastest.pass.home.data.model.NewItemFormRequest
+import com.fastest.pass.home.data.model.NewItemFormResponse
+
+interface NewItemFormRepository {
+    suspend fun newItemFormRepository(vault: String, newItemFormRequest: NewItemFormRequest) : WebResponse<NewItemFormResponse>
+}

+ 16 - 0
app/src/main/java/com/fastest/pass/home/domain/usecase/NewItemFormUseCase.kt

@@ -0,0 +1,16 @@
+package com.fastest.pass.home.domain.usecase
+
+import com.fastest.pass.app.BaseUseCase
+import com.fastest.pass.app.WebResponse
+import com.fastest.pass.home.data.model.NewItemFormRequest
+import com.fastest.pass.home.data.model.NewItemFormResponse
+import com.fastest.pass.home.domain.repository.NewItemFormRepository
+import javax.inject.Inject
+
+class NewItemFormUseCase @Inject constructor(
+    private val newItemFormRepository: NewItemFormRepository
+) : BaseUseCase<Pair<String, NewItemFormRequest>, WebResponse<NewItemFormResponse>>() {
+    override suspend fun execute(params: Pair<String, NewItemFormRequest>): WebResponse<NewItemFormResponse> {
+        return newItemFormRepository.newItemFormRepository(params.first, params.second)
+    }
+}

+ 30 - 20
app/src/main/java/com/fastest/pass/home/presentation/ui/components/AddPasswordFormScreen.kt

@@ -64,12 +64,13 @@ import androidx.compose.ui.unit.sp
 import com.fastest.pass.R
 import com.fastest.pass.app.Utils
 import com.fastest.pass.helpers.BasePreferenceHelper
+import com.fastest.pass.home.data.model.NewItemFormField
 import com.fastest.pass.home.domain.model.PasswordFormData
 import javax.inject.Inject
 
 @Composable
 fun AddPasswordFormScreen(
-    onPasswordsFormList: (ArrayList<PasswordFormData>) -> Unit,
+    onPasswordsFormList: (ArrayList<NewItemFormField>, Boolean) -> Unit,
     basePreferenceHelper: BasePreferenceHelper
 ) {
     val keyboardController = LocalSoftwareKeyboardController.current
@@ -91,29 +92,38 @@ fun AddPasswordFormScreen(
         SaveButtonAPWFS(
             buttonText = R.string.save,
             onSaveButtonCLick = {
-                val passwordsFormList = ArrayList<PasswordFormData>()
+                val passwordsFormList = ArrayList<NewItemFormField>()
                 val saltKey = basePreferenceHelper.getCustomSaltKey()
-                val userPassword = basePreferenceHelper.getPassword()
+                val secretKey = basePreferenceHelper.getCustomSecretKey()
 
-                Log.d("saltKey", "saltKey = $saltKey, password = $userPassword")
-//                val passwords_title_enc = Utils.encryptData(
-//                    passwords_title, userPassword ?: "", saltKey ?: "")
-//                passwords_url = Utils.encryptData(
-//                    passwords_url, userPassword ?: "", saltKey ?: "")
-//                passwords_username = Utils.encryptData(
-//                    passwords_username, userPassword ?: "", saltKey ?: "")
-//                passwords_password = Utils.encryptData(
-//                    passwords_password, userPassword ?: "", saltKey ?: "")
-//                passwords_notes = Utils.encryptData(
-//                    passwords_notes, userPassword ?: "", saltKey ?: "")
+                val isTitleEmpty = passwords_title.isEmpty()
 
-                passwordsFormList.add(PasswordFormData("passwords_title", passwords_title))
-                passwordsFormList.add(PasswordFormData("passwords_url", passwords_url))
-                passwordsFormList.add(PasswordFormData("passwords_username", passwords_username))
-                passwordsFormList.add(PasswordFormData("passwords_password", passwords_password))
-                passwordsFormList.add(PasswordFormData("passwords_notes", passwords_notes))
+                if (passwords_title.isNotEmpty()) {
+                    passwords_title = Utils.encryptData(passwords_title, secretKey ?: "", saltKey ?: "")
+                }
+                if (passwords_url.isNotEmpty()) {
+                    passwords_url = Utils.encryptData(passwords_url, secretKey ?: "", saltKey ?: "")
+                }
+                if (passwords_username.isNotEmpty()) {
+                    passwords_username =
+                        Utils.encryptData(passwords_username, secretKey ?: "", saltKey ?: "")
+                }
+                if (passwords_password.isNotEmpty()) {
+                    passwords_password =
+                        Utils.encryptData(passwords_password, secretKey ?: "", saltKey ?: "")
+                }
+                if (passwords_notes.isNotEmpty()) {
+                    passwords_notes =
+                        Utils.encryptData(passwords_notes, secretKey ?: "", saltKey ?: "")
+                }
+
+                passwordsFormList.add(NewItemFormField("passwords_title", passwords_title))
+                passwordsFormList.add(NewItemFormField("passwords_url", passwords_url))
+                passwordsFormList.add(NewItemFormField("passwords_username", passwords_username))
+                passwordsFormList.add(NewItemFormField("passwords_password", passwords_password))
+                passwordsFormList.add(NewItemFormField("passwords_notes", passwords_notes))
 
-                onPasswordsFormList.invoke(passwordsFormList)
+                onPasswordsFormList.invoke(passwordsFormList, isTitleEmpty)
             }
         )
 

+ 4 - 3
app/src/main/java/com/fastest/pass/home/presentation/ui/components/NewItemFormScreen.kt

@@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import com.fastest.pass.R
 import com.fastest.pass.helpers.BasePreferenceHelper
+import com.fastest.pass.home.data.model.NewItemFormField
 import com.fastest.pass.home.domain.model.CountryInfo
 import com.fastest.pass.home.domain.model.PasswordFormData
 
@@ -49,7 +50,7 @@ fun NewItemFormScreen(
     onSearchTextChanged: (String) -> Unit,
     onCountryList: (List<CountryInfo>, String) -> Unit,
     countryListDisplayed: List<CountryInfo>,
-    onPasswordsFormList: (ArrayList<PasswordFormData>) -> Unit,
+    onPasswordsFormList: (ArrayList<NewItemFormField>, Boolean) -> Unit,
     basePreferenceHelper: BasePreferenceHelper
 ) {
     val keyboardController = LocalSoftwareKeyboardController.current
@@ -86,8 +87,8 @@ fun NewItemFormScreen(
         when (screenNameType) {
             ClickTypeAddNewItem.Password -> {
                 AddPasswordFormScreen(
-                    onPasswordsFormList = {
-                        onPasswordsFormList.invoke(it)
+                    onPasswordsFormList = { data, boolean ->
+                        onPasswordsFormList.invoke(data, boolean)
                     },
                     basePreferenceHelper = basePreferenceHelper
                 )

+ 17 - 3
app/src/main/java/com/fastest/pass/home/presentation/ui/fragment/NewItemFormFragment.kt

@@ -13,6 +13,7 @@ import androidx.compose.material.Scaffold
 import androidx.compose.material3.SnackbarHostState
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.res.colorResource
@@ -29,7 +30,9 @@ import com.fastest.pass.home.presentation.viewmodels.NewItemFormViewModel
 import com.fastest.pass.home.utils.NewItemFormNavigation
 import com.fastest.pass.home.utils.NewItemFormRoute
 import com.fastest.pass.ui.theme.FastestPassTheme
+import com.fastest.pass.views.ShowCustomSnackBar
 import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -68,6 +71,7 @@ class NewItemFormFragment : BaseFragment() {
                             val screenName = viewmodelAddNewItems.itemScreenName.collectAsState()
                             var headerName: String = ""
                             val snackBarStateRed = remember { SnackbarHostState() }
+                            val coroutineScope = rememberCoroutineScope()
                             Log.d("test_screen_name", "NIFF::screenName = ${screenName.value}")
 
                             val countryList = viewmodel.countriesList.collectAsState()
@@ -116,14 +120,24 @@ class NewItemFormFragment : BaseFragment() {
                                     viewmodel.getCountries(text =  text, countryList = list)
                                 },
                                 countryListDisplayed = countryList.value,
-                                onPasswordsFormList = { passwordFormData ->
-                                    Log.d("passwordFormData", passwordFormData.size.toString())
+                                onPasswordsFormList = { passwordFormData, isTitleEmpty ->
+                                    Log.d("passwordFormData", "${passwordFormData.size} , $isTitleEmpty")
                                     passwordFormData.forEachIndexed { i, data ->
-                                        Log.d("passwordFormData", "${data.key}, ${data.value}, ${data.vaultItemName}")
+                                        Log.d("passwordFormData", "${data.key}, ${data.value}")
+                                    }
+                                    if (isTitleEmpty) {
+                                        coroutineScope.launch {
+                                            snackBarStateRed.showSnackbar("Title field is empty.")
+                                        }
+                                    } else {
+                                        viewmodel.addItems("passwords", passwordFormData)
                                     }
                                 },
                                 basePreferenceHelper = basePreferenceHelper
                             )
+
+                            ShowCustomSnackBar(snackBarStateRed, R.color.red_login_button, R.color.white)
+
                         }
                     }
                 }

+ 41 - 1
app/src/main/java/com/fastest/pass/home/presentation/viewmodels/NewItemFormViewModel.kt

@@ -1,12 +1,29 @@
 package com.fastest.pass.home.presentation.viewmodels
 
 import android.util.Log
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
 import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.fastest.pass.app.Result
+import com.fastest.pass.app.UIState
+import com.fastest.pass.app.WebResponse
+import com.fastest.pass.home.data.model.NewItemFormField
+import com.fastest.pass.home.data.model.NewItemFormRequest
+import com.fastest.pass.home.data.model.NewItemFormResponse
 import com.fastest.pass.home.domain.model.CountryInfo
+import com.fastest.pass.home.domain.usecase.NewItemFormUseCase
 import com.fastest.pass.home.utils.NewItemFormRoute
+import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class NewItemFormViewModel @Inject constructor(
+    private val newItemFormUseCase: NewItemFormUseCase
+): ViewModel() {
 
-class NewItemFormViewModel : ViewModel() {
     private val _router = MutableStateFlow<NewItemFormRoute>(NewItemFormRoute.OpenNoneScreen)
     val router: MutableStateFlow<NewItemFormRoute> = _router
 
@@ -16,6 +33,29 @@ class NewItemFormViewModel : ViewModel() {
     private val _countriesList = MutableStateFlow<List<CountryInfo>>(emptyList())
     val countriesList: MutableStateFlow<List<CountryInfo>> = _countriesList
 
+    private val _newItemFormResponse = mutableStateOf(UIState<WebResponse<NewItemFormResponse>>())
+    val newItemFormResponse: State<UIState<WebResponse<NewItemFormResponse>>> = _newItemFormResponse
+
+    fun addItems(vault: String, fields: List<NewItemFormField>) {
+        viewModelScope.launch {
+            val request = NewItemFormRequest(fields)
+            newItemFormUseCase(Pair(vault, request)) { result ->
+                Log.d("addItems_api", "result = $result")
+                when (result) {
+                    is Result.Loading -> {
+                        _newItemFormResponse.value = UIState(true)
+                    }
+                    is Result.Success -> {
+                        _newItemFormResponse.value = UIState(false, result.data)
+                    }
+                    is Result.Failure -> {
+                        _newItemFormResponse.value = UIState(false)
+                    }
+                }
+            }
+        }
+    }
+
     fun navigateTo(newItemFormRoute: NewItemFormRoute) {
         _router.value = newItemFormRoute
     }

+ 1 - 1
app/src/main/java/com/fastest/pass/login/domain/usecase/LoginUseCase.kt

@@ -10,7 +10,7 @@ import javax.inject.Inject
 
 class LoginUseCase @Inject constructor(
     private val loginRepository: LoginRepository
-): BaseUseCase<LoginRequest, WebResponse<LoginResponse>>()  {
+): BaseUseCase<LoginRequest, WebResponse<LoginResponse>>() {
     override suspend fun execute(params: LoginRequest): WebResponse<LoginResponse> {
         Log.d("test_api_login", "invoke() , LoginUseCase")
         return loginRepository.loginRepository(params)