Added start logic for visits pagination, changed visits logic, changed Dto objects, added password in login, bug fixes,
This commit is contained in:
		
							parent
							
								
									12c6b6e1c2
								
							
						
					
					
						commit
						961c37d265
					
				| @ -39,7 +39,7 @@ android { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| dependencies { | dependencies { | ||||||
| 
 |     implementation("androidx.paging:paging-runtime:3.3.2") | ||||||
|     val cameraX = "1.3.4" |     val cameraX = "1.3.4" | ||||||
|     implementation("androidx.camera:camera-core:$cameraX") |     implementation("androidx.camera:camera-core:$cameraX") | ||||||
|     implementation("androidx.camera:camera-camera2:$cameraX") |     implementation("androidx.camera:camera-camera2:$cameraX") | ||||||
|  | |||||||
| @ -6,5 +6,5 @@ data class UserEntity( | |||||||
|     val name: String, |     val name: String, | ||||||
|     val photo: String, |     val photo: String, | ||||||
|     val position: String, |     val position: String, | ||||||
|     val lastVisit: String, | //    val lastVisit: String, | ||||||
| ) | ) | ||||||
| @ -10,7 +10,7 @@ class UserMapper { | |||||||
|             login = userDTO.login, |             login = userDTO.login, | ||||||
|             position = userDTO.position, |             position = userDTO.position, | ||||||
|             name = userDTO.name, |             name = userDTO.name, | ||||||
|             lastVisit = userDTO.lastVisit, | //            lastVisit = userDTO.lastVisit, | ||||||
|             photo = userDTO.photo |             photo = userDTO.photo | ||||||
|         ) |         ) | ||||||
|         return userEntity |         return userEntity | ||||||
| @ -22,7 +22,7 @@ class UserMapper { | |||||||
|             name = userEntity.name, |             name = userEntity.name, | ||||||
|             photo = userEntity.photo, |             photo = userEntity.photo, | ||||||
|             position = userEntity.position, |             position = userEntity.position, | ||||||
|             lastVisit = userEntity.lastVisit, | //            lastVisit = userEntity.lastVisit, | ||||||
|         ) |         ) | ||||||
|         return userDto |         return userDto | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -3,7 +3,9 @@ package com.displaynone.acss.components.auth.models.user | |||||||
| import android.content.Context | import android.content.Context | ||||||
| import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager | import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager | ||||||
| import com.displaynone.acss.components.auth.models.user.repository.UserRepository | import com.displaynone.acss.components.auth.models.user.repository.UserRepository | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.dto.LastVisitsDto | ||||||
| import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.dto.VisitDto | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class UserServiceST( | class UserServiceST( | ||||||
| @ -28,13 +30,34 @@ class UserServiceST( | |||||||
|             return instance ?: throw RuntimeException("null instance") |             return instance ?: throw RuntimeException("null instance") | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     suspend fun login(login: String): Result<Unit>{ |     suspend fun login(login: String, password:String): Result<Unit>{ | ||||||
|         return runCatching { |         return runCatching { | ||||||
|             userRepository.login(login = login).getOrThrow().let { data -> |             userRepository.login(login = login, password).getOrThrow().let { data -> | ||||||
|                 tokenManager.saveTokens(data) |                 tokenManager.saveTokens(data) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     suspend fun getMyLastVisits(pageNum: Int, | ||||||
|  |                                 pageSize: Int): Result<List<VisitDto>> { | ||||||
|  |         if (!tokenManager.hasTokens()) { | ||||||
|  |             throw RuntimeException("access token is null") | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return userRepository.getMyLastVisits( | ||||||
|  |             pageNum = pageNum, | ||||||
|  |             pageSize = pageSize, | ||||||
|  |             token = tokenManager.authTokenPair!!.accessToken | ||||||
|  |         ).map { pagingDto -> pagingDto.content | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     suspend fun getLastVisitsByLogin(pageNum: Int, | ||||||
|  |                                      pageSize: Int, | ||||||
|  |                                      login: String): Result<List<VisitDto>> { | ||||||
|  |         if (!tokenManager.hasTokens()) { | ||||||
|  |             throw RuntimeException("access token is null") | ||||||
|  |         } | ||||||
|  |         return userRepository.getLastVisitsByLogin(pageNum, pageSize, tokenManager.authTokenPair!!.accessToken, login).map { pagingDto -> pagingDto.content } | ||||||
|  |     } | ||||||
|     fun logout(){ |     fun logout(){ | ||||||
|         tokenManager.clear() |         tokenManager.clear() | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -2,10 +2,12 @@ package com.displaynone.acss.components.auth.models.user.repository | |||||||
| 
 | 
 | ||||||
| import android.util.Log | import android.util.Log | ||||||
| import com.displaynone.acss.components.auth.models.AuthTokenPair | import com.displaynone.acss.components.auth.models.AuthTokenPair | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.dto.LastVisitsDto | ||||||
| import com.displaynone.acss.config.Constants.serverUrl | import com.displaynone.acss.config.Constants.serverUrl | ||||||
| import com.displaynone.acss.config.Network | import com.displaynone.acss.config.Network | ||||||
| import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto | import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto | ||||||
| import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.dto.UserLoginDto | ||||||
| import io.ktor.client.call.body | import io.ktor.client.call.body | ||||||
| import io.ktor.client.request.get | import io.ktor.client.request.get | ||||||
| import io.ktor.client.request.headers | import io.ktor.client.request.headers | ||||||
| @ -33,13 +35,14 @@ class UserRepository( | |||||||
|             result.status != HttpStatusCode.OK |             result.status != HttpStatusCode.OK | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     suspend fun login(login: String): Result<AuthTokenPair> = withContext(Dispatchers.IO) { |     suspend fun login(login: String, password: String): Result<AuthTokenPair> = withContext(Dispatchers.IO) { | ||||||
|         runCatching { |         runCatching { | ||||||
|             val result = Network.client.post("$serverUrl/api/auth/login") { |             val result = Network.client.post("$serverUrl/api/auth/login") { | ||||||
|                 headers { |                 headers { | ||||||
|                     append(HttpHeaders.ContentType, ContentType.Application.Json.toString()) |                     append(HttpHeaders.ContentType, ContentType.Application.Json.toString()) | ||||||
|                 } |                 } | ||||||
|                 setBody("""{ "login": "$login" }""") |                 contentType(ContentType.Application.Json) | ||||||
|  |                 setBody(UserLoginDto(login, password)) | ||||||
|             } |             } | ||||||
|             if (result.status != HttpStatusCode.OK) { |             if (result.status != HttpStatusCode.OK) { | ||||||
|                 error("Status ${result.status}: ${result.body<String>()}") |                 error("Status ${result.status}: ${result.body<String>()}") | ||||||
| @ -99,6 +102,57 @@ class UserRepository( | |||||||
|             result.body() |             result.body() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     suspend fun getMyLastVisits(pageNum: Int, | ||||||
|  |                                 pageSize: Int, | ||||||
|  |                                 token: String): Result<LastVisitsDto> = withContext(Dispatchers.IO){ | ||||||
|  |         runCatching { | ||||||
|  |             val result = Network.client.get("$serverUrl/api/acs/visits/me?page=$pageNum&size=$pageSize") { | ||||||
|  |                 headers { | ||||||
|  |                     append(HttpHeaders.Authorization, "Bearer $token") | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Log.d("UserRepository", result.bodyAsText()) | ||||||
|  |             if (result.status != HttpStatusCode.OK) { | ||||||
|  |                 error("Status ${result.status}: ${result.body<String>()}") | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             result.body() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     suspend fun getLastVisitsByLogin(pageNum: Int, | ||||||
|  |                                      pageSize: Int, | ||||||
|  |                                      token: String, login: String): Result<LastVisitsDto> = withContext(Dispatchers.IO){ | ||||||
|  |         runCatching { | ||||||
|  |             val encodedLogin = login.encodeURLPath() | ||||||
|  |             val result = Network.client.get("$serverUrl/api/acs/login/${encodedLogin}?page=$pageNum&size=$pageSize") { | ||||||
|  |                 headers { | ||||||
|  |                     append(HttpHeaders.Authorization, "Bearer $token") | ||||||
|  |                 } | ||||||
|  |                 setBody("""{ "login": "$encodedLogin" }""") | ||||||
|  |             } | ||||||
|  |             if (result.status != HttpStatusCode.OK) { | ||||||
|  |                 error("Status ${result.status}: ${result.body<String>()}") | ||||||
|  |             } | ||||||
|  |             Log.d("UserRepository", result.bodyAsText()) | ||||||
|  |             result.body() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     suspend fun getAllLastVisitsAsAdmin(token: String, login: String): Result<UserDTO> = withContext(Dispatchers.IO){ | ||||||
|  |         runCatching { | ||||||
|  |             val encodedLogin = login.encodeURLPath() | ||||||
|  |             val result = Network.client.get("$serverUrl/api/users/login/$encodedLogin") { | ||||||
|  |                 headers { | ||||||
|  |                     append(HttpHeaders.Authorization, "Bearer $token") | ||||||
|  |                 } | ||||||
|  |                 setBody("""{ "code": "$encodedLogin" }""") | ||||||
|  |             } | ||||||
|  |             if (result.status != HttpStatusCode.OK) { | ||||||
|  |                 error("Status ${result.status}: ${result.body<String>()}") | ||||||
|  |             } | ||||||
|  |             Log.d("UserRepository", result.bodyAsText()) | ||||||
|  |             result.body() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     suspend fun openDoor(token: String, code: Long): Result<Unit> = withContext(Dispatchers.IO) { |     suspend fun openDoor(token: String, code: Long): Result<Unit> = withContext(Dispatchers.IO) { | ||||||
|         runCatching { |         runCatching { | ||||||
|             val result = Network.client.patch("$serverUrl/api/open") { |             val result = Network.client.patch("$serverUrl/api/open") { | ||||||
|  | |||||||
| @ -0,0 +1,55 @@ | |||||||
|  | package com.displaynone.acss.components.auth.models.user.repository | ||||||
|  | 
 | ||||||
|  | import android.graphics.Color | ||||||
|  | import android.view.LayoutInflater | ||||||
|  | import android.view.ViewGroup | ||||||
|  | import androidx.paging.PagingDataAdapter | ||||||
|  | import androidx.recyclerview.widget.DiffUtil | ||||||
|  | import androidx.recyclerview.widget.RecyclerView | ||||||
|  | import com.bumptech.glide.Glide | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.dto.VisitDto | ||||||
|  | import com.displaynone.acss.databinding.ItemScannerViewBinding | ||||||
|  | 
 | ||||||
|  | class VisitAdapter: PagingDataAdapter<VisitDto, VisitAdapter.ViewHolder>(VisitDiff) { | ||||||
|  | 
 | ||||||
|  |     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { | ||||||
|  |         return ViewHolder( | ||||||
|  |             ItemScannerViewBinding.inflate( | ||||||
|  |                 LayoutInflater.from(parent.context), | ||||||
|  |                 parent, | ||||||
|  |                 false | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun onBindViewHolder(holder: ViewHolder, position: Int) { | ||||||
|  |         holder.bind(getItem(position) ?: | ||||||
|  |         VisitDto( | ||||||
|  |             id = -1, | ||||||
|  |             userId = -1, | ||||||
|  |             gateId = -1, | ||||||
|  |             createdAt = "Loading...", | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     class ViewHolder( | ||||||
|  |         private val binding: ItemScannerViewBinding | ||||||
|  |     ): RecyclerView.ViewHolder(binding.root) { | ||||||
|  |         fun bind(item: VisitDto){ | ||||||
|  |             binding.scanTime.text = item.createdAt | ||||||
|  |             binding.scannerId.text = item.gateId.toString() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     object VisitDiff : DiffUtil.ItemCallback<VisitDto>() { | ||||||
|  |         override fun areItemsTheSame(oldItem: VisitDto, newItem: VisitDto): Boolean { | ||||||
|  |             return oldItem.id == newItem.id | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         override fun areContentsTheSame(oldItem: VisitDto, newItem: VisitDto): Boolean { | ||||||
|  |             return oldItem == newItem | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,35 @@ | |||||||
|  | package com.displaynone.acss.components.auth.models.user.repository | ||||||
|  | 
 | ||||||
|  | import androidx.paging.PagingSource | ||||||
|  | import androidx.paging.PagingState | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.dto.VisitDto | ||||||
|  | 
 | ||||||
|  | class VisitListPagingSource( | ||||||
|  |     private val request: suspend (pageNum: Int, pageSize: Int) -> Result<List<VisitDto>> | ||||||
|  | ) : PagingSource<Int, VisitDto>() { | ||||||
|  |     override fun getRefreshKey(state: PagingState<Int, VisitDto>): Int? { | ||||||
|  |         return state.anchorPosition?.let { | ||||||
|  |             state.closestPageToPosition(it)?.prevKey?.plus(1) | ||||||
|  |                 ?: state.closestPageToPosition(it)?.nextKey?.minus(1) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun load(params: LoadParams<Int>): LoadResult<Int, VisitDto> { | ||||||
|  |         val pageNum = params.key ?: 0 | ||||||
|  |         return request.invoke( | ||||||
|  |             pageNum, | ||||||
|  |             params.loadSize | ||||||
|  |         ).fold( | ||||||
|  |             onSuccess = { value -> | ||||||
|  |                 LoadResult.Page( | ||||||
|  |                     data = value, | ||||||
|  |                     prevKey = (pageNum - 1).takeIf { it >= 0 }, | ||||||
|  |                     nextKey = (pageNum + 1).takeIf { value.size == params.loadSize } | ||||||
|  |                 ) | ||||||
|  |             }, | ||||||
|  |             onFailure = { error -> | ||||||
|  |                 LoadResult.Error(error) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,12 @@ | |||||||
|  | package com.displaynone.acss.components.auth.models.user.repository.dto | ||||||
|  | 
 | ||||||
|  | import kotlinx.serialization.SerialName | ||||||
|  | import kotlinx.serialization.Serializable | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @Serializable | ||||||
|  | data class LastVisitsDto( | ||||||
|  |     @SerialName("content") | ||||||
|  |     val content: List<VisitDto> | ||||||
|  | ) { | ||||||
|  | } | ||||||
| @ -21,6 +21,6 @@ data class UserDTO ( | |||||||
|     @SerialName("position") |     @SerialName("position") | ||||||
|     val position: String, |     val position: String, | ||||||
| 
 | 
 | ||||||
|     @SerialName("lastVisit") | //    @SerialName("lastVisit") | ||||||
|     val lastVisit: String, | //    val lastVisit: String, | ||||||
| ) : java.io.Serializable | ) : java.io.Serializable | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | package com.displaynone.acss.components.auth.models.user.repository.dto | ||||||
|  | 
 | ||||||
|  | import kotlinx.serialization.SerialName | ||||||
|  | import kotlinx.serialization.Serializable | ||||||
|  | 
 | ||||||
|  | @Serializable | ||||||
|  | data class UserLoginDto( | ||||||
|  |     @SerialName("login") | ||||||
|  |     val login: String, | ||||||
|  |     @SerialName("password") | ||||||
|  |     val password: String, | ||||||
|  | ) { | ||||||
|  | } | ||||||
| @ -0,0 +1,17 @@ | |||||||
|  | package com.displaynone.acss.components.auth.models.user.repository.dto | ||||||
|  | 
 | ||||||
|  | import kotlinx.serialization.SerialName | ||||||
|  | import kotlinx.serialization.Serializable | ||||||
|  | 
 | ||||||
|  | @Serializable | ||||||
|  | data class VisitDto( | ||||||
|  |     @SerialName("id") | ||||||
|  |     val id: Long, | ||||||
|  |     @SerialName("userId") | ||||||
|  |     val userId: Long, | ||||||
|  |     @SerialName("gateId") | ||||||
|  |     val gateId: Long, | ||||||
|  |     @SerialName("createdAt") | ||||||
|  |     val createdAt: String, | ||||||
|  | ) { | ||||||
|  | } | ||||||
| @ -40,9 +40,14 @@ class AdminFragment : Fragment(R.layout.fragment_admin) { | |||||||
|                 val userDto = state.item |                 val userDto = state.item | ||||||
|                 val bundle = Bundle().apply{ |                 val bundle = Bundle().apply{ | ||||||
|                     putSerializable("user", userDto) |                     putSerializable("user", userDto) | ||||||
|  |                     putBoolean("isMe", false) | ||||||
|                 } |                 } | ||||||
|                 navigateTo(view, R.id.action_adminFragment_to_profileFragment, bundle) |                 navigateTo(view, R.id.action_adminFragment_to_profileFragment, bundle) | ||||||
|             } |             } | ||||||
|  |             if (state is AdminViewModel.State.Error){ | ||||||
|  |                 val errorMessage = state.errorMessage | ||||||
|  |                 binding.loginSearch.setError(errorMessage) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -5,7 +5,6 @@ import androidx.lifecycle.ViewModel | |||||||
| import androidx.lifecycle.viewModelScope | import androidx.lifecycle.viewModelScope | ||||||
| import com.displaynone.acss.components.auth.models.user.UserServiceST | import com.displaynone.acss.components.auth.models.user.UserServiceST | ||||||
| import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | ||||||
| import com.displaynone.acss.ui.profile.ProfileViewModel.State |  | ||||||
| import kotlinx.coroutines.flow.MutableStateFlow | import kotlinx.coroutines.flow.MutableStateFlow | ||||||
| import kotlinx.coroutines.flow.asStateFlow | import kotlinx.coroutines.flow.asStateFlow | ||||||
| import kotlinx.coroutines.launch | import kotlinx.coroutines.launch | ||||||
| @ -20,7 +19,8 @@ class AdminViewModel: ViewModel() { | |||||||
|                     _state.emit(State.Show(item = dto)) |                     _state.emit(State.Show(item = dto)) | ||||||
|                 }, |                 }, | ||||||
|                 onFailure = { error -> |                 onFailure = { error -> | ||||||
|                     error.message?.let { error(it) } | //                    error.message?.let { error(it) } | ||||||
|  |                     _state.emit(State.Error(error.message.toString())) | ||||||
|                     Log.e("AdminViewModel", error.message.toString()) } |                     Log.e("AdminViewModel", error.message.toString()) } | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
| @ -30,6 +30,9 @@ class AdminViewModel: ViewModel() { | |||||||
|         data class Show( |         data class Show( | ||||||
|             val item: UserDTO |             val item: UserDTO | ||||||
|             ): State |             ): State | ||||||
|  |         data class Error( | ||||||
|  |             val errorMessage: String | ||||||
|  |         ): State | ||||||
|         data object Loading : State |         data object Loading : State | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -59,6 +59,7 @@ class AuthFragment: Fragment(R.layout.fragment_auth) { | |||||||
|                 val username = s.toString() |                 val username = s.toString() | ||||||
|                 val valid = isUsernameValid(username) |                 val valid = isUsernameValid(username) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|                 binding.hint.visibility = if(valid) View.INVISIBLE else View.VISIBLE |                 binding.hint.visibility = if(valid) View.INVISIBLE else View.VISIBLE | ||||||
|                 binding.next.isEnabled = valid |                 binding.next.isEnabled = valid | ||||||
|                 val color = if (valid) R.color.primary else R.color.secondary |                 val color = if (valid) R.color.primary else R.color.secondary | ||||||
| @ -67,6 +68,35 @@ class AuthFragment: Fragment(R.layout.fragment_auth) { | |||||||
| 
 | 
 | ||||||
|             override fun afterTextChanged(s: Editable?) {} |             override fun afterTextChanged(s: Editable?) {} | ||||||
|         }) |         }) | ||||||
|  | //        binding.password.addTextChangedListener(object : TextWatcher { | ||||||
|  | //            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} | ||||||
|  | //            @SuppressLint("ResourceAsColor") | ||||||
|  | //            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { | ||||||
|  | //                binding.error.visibility = View.GONE | ||||||
|  | //                val username = s.toString() | ||||||
|  | //                val valid = isPasswordValid(username) | ||||||
|  | // | ||||||
|  | //                binding.hint.visibility = if(valid) View.INVISIBLE else View.VISIBLE | ||||||
|  | //                if (!valid){ | ||||||
|  | //                    val errorMessage = getPasswordValidError(s.toString()) | ||||||
|  | //                    binding.password.setError(errorMessage) | ||||||
|  | //                } | ||||||
|  | //                binding.next.isEnabled = valid | ||||||
|  | //                val color = if (valid) R.color.primary else R.color.secondary | ||||||
|  | //                binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) | ||||||
|  | //            } | ||||||
|  | // | ||||||
|  | //            override fun afterTextChanged(s: Editable?) {} | ||||||
|  | //        }) | ||||||
|  |     } | ||||||
|  |     private fun getPasswordValidError(password: String): String { | ||||||
|  |         if (password.length < 8){ return "LenError" } | ||||||
|  |         val letterRegex = Regex("^(?=.*[A-Z]).+$") | ||||||
|  |         if(!letterRegex.matches(password)){ return "UpperCaseError"} | ||||||
|  |         val digitRegex = Regex("^(?=.*\\\\d).+$") | ||||||
|  |         if(!digitRegex.matches(password)){ return "DigitCaseError"} | ||||||
|  | 
 | ||||||
|  |         return "NoErrors" | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun isUsernameValid(username: String): Boolean { |     private fun isUsernameValid(username: String): Boolean { | ||||||
| @ -76,6 +106,10 @@ class AuthFragment: Fragment(R.layout.fragment_auth) { | |||||||
|                 !username[0].isDigit() && |                 !username[0].isDigit() && | ||||||
|                 alf.matches(username) |                 alf.matches(username) | ||||||
|     } |     } | ||||||
|  |     private fun isPasswordValid(password: String): Boolean { | ||||||
|  |         return password.isNotEmpty() && | ||||||
|  |                 password.length >= 8 | ||||||
|  |     } | ||||||
| //    private fun subscribe() { | //    private fun subscribe() { | ||||||
| //        viewModel.state.collectWhenStarted(this) { state -> | //        viewModel.state.collectWhenStarted(this) { state -> | ||||||
| //            binding.login.setOnClickListener(this::onLoginButtonClicked) | //            binding.login.setOnClickListener(this::onLoginButtonClicked) | ||||||
| @ -84,8 +118,9 @@ class AuthFragment: Fragment(R.layout.fragment_auth) { | |||||||
| 
 | 
 | ||||||
|     private fun onLoginButtonClicked(view: View) { |     private fun onLoginButtonClicked(view: View) { | ||||||
|         val login = binding.login.text.toString() |         val login = binding.login.text.toString() | ||||||
|  |         val password = binding.password.text.toString() | ||||||
|         if (login.isEmpty()) return |         if (login.isEmpty()) return | ||||||
|         viewModel.login(login) |         viewModel.login(login, password) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,10 +22,10 @@ class AuthViewModel(): ViewModel() { | |||||||
|     private val _errorState = MutableStateFlow<String?>(null) |     private val _errorState = MutableStateFlow<String?>(null) | ||||||
|     val errorState: StateFlow<String?> = _errorState.asStateFlow() |     val errorState: StateFlow<String?> = _errorState.asStateFlow() | ||||||
| 
 | 
 | ||||||
|     fun login(login: String){ |     fun login(login: String, password:String){ | ||||||
|         viewModelScope.launch { |         viewModelScope.launch { | ||||||
|             try { |             try { | ||||||
|                 UserServiceST.getInstance().login(login).fold( |                 UserServiceST.getInstance().login(login, password).fold( | ||||||
|                     onSuccess = { openProfile() }, |                     onSuccess = { openProfile() }, | ||||||
|                     onFailure = { error -> |                     onFailure = { error -> | ||||||
|                         Log.e("AuthViewModel", "Login failed: ${error.message ?: "Unknown error"}") |                         Log.e("AuthViewModel", "Login failed: ${error.message ?: "Unknown error"}") | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import androidx.fragment.app.viewModels | |||||||
| import androidx.navigation.fragment.findNavController | import androidx.navigation.fragment.findNavController | ||||||
| import com.bumptech.glide.Glide | import com.bumptech.glide.Glide | ||||||
| import com.displaynone.acss.R | import com.displaynone.acss.R | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.VisitAdapter | ||||||
| import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | ||||||
| import com.displaynone.acss.databinding.FragmentProfileBinding | import com.displaynone.acss.databinding.FragmentProfileBinding | ||||||
| import com.displaynone.acss.ui.profile.ProfileViewModel.Action | import com.displaynone.acss.ui.profile.ProfileViewModel.Action | ||||||
| @ -27,31 +28,58 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { | |||||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||||
|         super.onViewCreated(view, savedInstanceState) |         super.onViewCreated(view, savedInstanceState) | ||||||
|         _binding = FragmentProfileBinding.bind(view) |         _binding = FragmentProfileBinding.bind(view) | ||||||
| 
 | // | ||||||
|         binding.swipeRefresh.setOnRefreshListener { | //        binding.swipeRefresh.setOnRefreshListener { | ||||||
|             refreshData() | //            if (getIsMe()){ | ||||||
|         } | //                refreshData() | ||||||
|  | //            } else{ | ||||||
|  | //                showData(getUserDto()!!) | ||||||
|  | //            } | ||||||
|  | //        } | ||||||
|         binding.logout.setOnClickListener{ |         binding.logout.setOnClickListener{ | ||||||
|             logout() |             logout() | ||||||
|         } |         } | ||||||
|         binding.scan.setOnClickListener{ |         binding.scan.setOnClickListener{ | ||||||
|             viewModel.openScan() |             viewModel.openScan() | ||||||
|         } |         } | ||||||
|         val user = getUserDto() |         val adapter = VisitAdapter() | ||||||
|         if (user == null) { |         binding.recyclerViewLogs.adapter = adapter | ||||||
|  |         if (getIsMe()) { | ||||||
|             refreshData() |             refreshData() | ||||||
|  | //            viewModel.visitListState.collectWithLifecycle(this) { data -> | ||||||
|  | //                adapter.submitData(data) | ||||||
|  | //            } | ||||||
|             waitForQRScanResult() |             waitForQRScanResult() | ||||||
|         } else{ |         } else{ | ||||||
|             showData(user) // TODO()  не показывать кнопки |             showData(getUserDto()!!) | ||||||
|  |             hideButtons() | ||||||
|  | 
 | ||||||
|         } |         } | ||||||
|         subscribe() |         subscribe() | ||||||
| 
 | 
 | ||||||
|  |         viewModel.visitListState.collectWithLifecycle(this) { data -> | ||||||
|  |             adapter.submitData(data) | ||||||
|  |             Log.d("ProfileFragment", "submitted data") | ||||||
|  |         } | ||||||
|  | //        viewModel.visitListStateFromLogin.collectWithLifecycle(this) { data -> | ||||||
|  | //            adapter.submitData(data) | ||||||
|  | //        } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun hideButtons() { | ||||||
|  |         binding.logout.visibility = View.INVISIBLE | ||||||
|  |         binding.scan.visibility = View.INVISIBLE | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun showData(userDTO: UserDTO){ |     fun showData(userDTO: UserDTO){ | ||||||
|         binding.fio.text = userDTO.name |         binding.fio.text = userDTO.name | ||||||
|         binding.position.text = userDTO.position |         binding.position.text = userDTO.position | ||||||
|         binding.lastEntry.text = userDTO.lastVisit |         viewModel.setLogin(login1 = userDTO.login) | ||||||
|  | //        binding.lastEntry.text = userDTO.lastVisit | ||||||
|  | //        viewModel.visitListStateFromLogin.collectWithLifecycle(this){ | ||||||
|  | // | ||||||
|  | //        } | ||||||
|         setAvatar(userDTO.photo) |         setAvatar(userDTO.photo) | ||||||
|     } |     } | ||||||
|     private fun refreshData() { |     private fun refreshData() { | ||||||
| @ -72,6 +100,10 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { | |||||||
|     private fun getUserDto(): UserDTO? { |     private fun getUserDto(): UserDTO? { | ||||||
|         return arguments?.getSerializable("user") as? UserDTO |         return arguments?.getSerializable("user") as? UserDTO | ||||||
|     } |     } | ||||||
|  |     private fun getIsMe(): Boolean { | ||||||
|  |         return arguments?.getBoolean("isMe", true) ?: true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun waitForQRScanResult() { |     private fun waitForQRScanResult() { | ||||||
| 
 | 
 | ||||||
|         requireActivity().onBackPressedDispatcher.addCallback( |         requireActivity().onBackPressedDispatcher.addCallback( | ||||||
|  | |||||||
| @ -3,7 +3,12 @@ package com.displaynone.acss.ui.profile | |||||||
| import android.util.Log | import android.util.Log | ||||||
| import androidx.lifecycle.ViewModel | import androidx.lifecycle.ViewModel | ||||||
| import androidx.lifecycle.viewModelScope | import androidx.lifecycle.viewModelScope | ||||||
|  | import androidx.paging.Pager | ||||||
|  | import androidx.paging.PagingConfig | ||||||
|  | import androidx.paging.cachedIn | ||||||
|  | import androidx.paging.log | ||||||
| import com.displaynone.acss.components.auth.models.user.UserServiceST | import com.displaynone.acss.components.auth.models.user.UserServiceST | ||||||
|  | import com.displaynone.acss.components.auth.models.user.repository.VisitListPagingSource | ||||||
| import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO | ||||||
| import kotlinx.coroutines.channels.BufferOverflow | import kotlinx.coroutines.channels.BufferOverflow | ||||||
| import kotlinx.coroutines.channels.Channel | import kotlinx.coroutines.channels.Channel | ||||||
| @ -17,10 +22,31 @@ class ProfileViewModel(): ViewModel() { | |||||||
|         capacity = Channel.BUFFERED, |         capacity = Channel.BUFFERED, | ||||||
|         onBufferOverflow = BufferOverflow.DROP_OLDEST, |         onBufferOverflow = BufferOverflow.DROP_OLDEST, | ||||||
|     ) |     ) | ||||||
|  |     private var login: String = "" // FIXME() | ||||||
|  |     fun setLogin(login1: String){ | ||||||
|  |         login = login1 | ||||||
|  |     } | ||||||
|     val action = _action.receiveAsFlow() |     val action = _action.receiveAsFlow() | ||||||
|     val _state = MutableStateFlow<State>(State.Loading) |     val _state = MutableStateFlow<State>(State.Loading) | ||||||
|     val state = _state.asStateFlow() |     val state = _state.asStateFlow() | ||||||
| 
 | 
 | ||||||
|  |     val visitListState = Pager( | ||||||
|  |         config = PagingConfig(pageSize = 20, | ||||||
|  |             enablePlaceholders = false, | ||||||
|  |             maxSize = 100 | ||||||
|  |         ) | ||||||
|  |     ) { | ||||||
|  |         VisitListPagingSource(UserServiceST.getInstance()::getMyLastVisits) | ||||||
|  |     }.flow | ||||||
|  |         .cachedIn(viewModelScope) | ||||||
|  | 
 | ||||||
|  |     val visitListStateFromLogin = Pager( | ||||||
|  |         config = PagingConfig(pageSize = 20, enablePlaceholders = false, maxSize = 100) | ||||||
|  |     ) { | ||||||
|  |         VisitListPagingSource { pageNum, pageSize -> | ||||||
|  |             UserServiceST.getInstance().getLastVisitsByLogin(pageNum,pageSize, login) | ||||||
|  |         } | ||||||
|  |     }.flow.cachedIn(viewModelScope) | ||||||
| 
 | 
 | ||||||
|     fun logout(){ |     fun logout(){ | ||||||
|         UserServiceST.getInstance().logout() |         UserServiceST.getInstance().logout() | ||||||
| @ -48,6 +74,7 @@ class ProfileViewModel(): ViewModel() { | |||||||
|             _action.send(Action.GoToScan) |             _action.send(Action.GoToScan) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     sealed interface State { |     sealed interface State { | ||||||
|         data object Loading : State |         data object Loading : State | ||||||
|         data class Show( |         data class Show( | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user