Merge remote-tracking branch 'origin/master'

This commit is contained in:
Serafim_Pankin 2025-02-20 10:51:34 +03:00
commit 3db6d74782
15 changed files with 203 additions and 118 deletions

View File

@ -1,11 +1,15 @@
package com.displaynone
import android.app.Application
import com.displaynone.acss.components.acs.models.gate.GateServiceST
import com.displaynone.acss.components.acs.models.visit.VisitServiceST
import com.displaynone.acss.components.auth.models.user.UserServiceST
class ACSSApplication : Application() {
override fun onCreate() {
super.onCreate()
UserServiceST.createInstance(this)
GateServiceST.createInstance()
VisitServiceST.createInstance()
}
}

View File

@ -1,4 +1,28 @@
package com.displaynone.acss.components.acs.models.gate
import android.content.Context
import android.util.Log
import com.displaynone.acss.components.acs.models.gate.repository.GateRepository
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.user.UserServiceST
class GateServiceST {
companion object {
private var instance: GateServiceST? = null
fun createInstance() {
if (instance == null) {
instance = GateServiceST()
}
}
fun getInstance(): GateServiceST {
return instance ?: throw RuntimeException("null instance")
}
}
private val gateRepository: GateRepository = GateRepository()
suspend fun openDoor(code: String): Result<Int> {
Log.d("1234", UserServiceST.getInstance().getTokenPair().accessToken)
return gateRepository.openDoor(UserServiceST.getInstance().getTokenPair().accessToken, code = code)
}
}

View File

@ -0,0 +1,37 @@
package com.displaynone.acss.components.acs.models.gate.repository
import android.util.Log
import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import io.ktor.client.call.body
import io.ktor.client.request.headers
import io.ktor.client.request.patch
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class GateRepository {
suspend fun openDoor(token: String, code: String): Result<Int> = withContext(Dispatchers.IO){
runCatching{
val result = Network.client.post("$serverUrl/api/acs/open") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
contentType(ContentType.Application.Json)
setBody("""{ "code": $code }""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.status.value
}
}
}

View File

@ -1,13 +1,11 @@
package com.displaynone.acss.components.auth.models.user.repository
package com.displaynone.acss.components.acs.models.visit
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.components.acs.models.visit.repository.dto.VisitDto
import com.displaynone.acss.databinding.ItemScannerViewBinding
class VisitAdapter: PagingDataAdapter<VisitDto, VisitAdapter.ViewHolder>(VisitDiff) {
@ -29,7 +27,8 @@ class VisitAdapter: PagingDataAdapter<VisitDto, VisitAdapter.ViewHolder>(VisitDi
userId = -1,
gateId = -1,
createdAt = "Loading...",
))
)
)
}

View File

@ -1,8 +1,8 @@
package com.displaynone.acss.components.auth.models.user.repository
package com.displaynone.acss.components.acs.models.visit
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.displaynone.acss.components.auth.models.user.repository.dto.VisitDto
import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
class VisitListPagingSource(
private val request: suspend (pageNum: Int, pageSize: Int) -> Result<List<VisitDto>>

View File

@ -1,4 +1,45 @@
package com.displaynone.acss.components.acs.models.visit
import android.content.Context
import com.displaynone.acss.components.acs.models.visit.repository.VisitRepository
import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.user.UserServiceST
class VisitServiceST {
companion object {
private var instance: VisitServiceST? = null
fun createInstance() {
if (instance == null) {
instance = VisitServiceST()
}
}
fun getInstance(): VisitServiceST {
return instance ?: throw RuntimeException("null instance")
}
}
private val visitRepository: VisitRepository = VisitRepository()
suspend fun getLastVisitsByLogin(pageNum: Int,
pageSize: Int,
login: String): Result<List<VisitDto>> {
if (!UserServiceST.getInstance().hasTokens()) {
throw RuntimeException("access token is null")
}
return visitRepository.getLastVisitsByLogin(pageNum, pageSize, UserServiceST.getInstance().getTokenPair().accessToken, login).map { pagingDto -> pagingDto.content }
}
suspend fun getMyLastVisits(pageNum: Int,
pageSize: Int): Result<List<VisitDto>> {
if (!UserServiceST.getInstance().hasTokens()) {
throw RuntimeException("access token is null")
}
return visitRepository.getMyLastVisits(
pageNum = pageNum,
pageSize = pageSize,
token = UserServiceST.getInstance().getTokenPair().accessToken
).map { pagingDto -> pagingDto.content }
}
}

View File

@ -0,0 +1,57 @@
package com.displaynone.acss.components.acs.models.visit.repository
import android.util.Log
import com.displaynone.acss.components.acs.models.visit.repository.dto.LastVisitsDto
import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.encodeURLPath
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class VisitRepository {
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("VisitRepository", 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 {
Log.d("VisitRepository", login)
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/acs/visits/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("VisitRepository", result.bodyAsText())
result.body()
}
}
}

View File

@ -1,4 +1,4 @@
package com.displaynone.acss.components.auth.models.user.repository.dto
package com.displaynone.acss.components.acs.models.visit.repository.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.displaynone.acss.components.auth.models.user.repository.dto
package com.displaynone.acss.components.acs.models.visit.repository.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,14 +1,12 @@
package com.displaynone.acss.components.auth.models.user
import android.content.Context
import android.util.Log
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.AuthTokenPair
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.VisitDto
import java.util.Optional
import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
class UserServiceST(
@ -43,29 +41,32 @@ class UserServiceST(
}
}
}
fun getTokenPair(): AuthTokenPair {
return tokenManager.authTokenPair!!
}
fun hasTokens(): Boolean {
return tokenManager.hasTokens()
}
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 }
}
// 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(){
tokenManager.clear()
}

View File

@ -2,7 +2,6 @@ package com.displaynone.acss.components.auth.models.user.repository
import android.util.Log
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.Network
import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto
@ -11,7 +10,6 @@ import com.displaynone.acss.components.auth.models.user.repository.dto.UserLogin
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.client.request.patch
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
@ -24,7 +22,6 @@ import io.ktor.http.encodeURLPath
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import kotlin.math.log
class UserRepository(
@ -104,72 +101,6 @@ class UserRepository(
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 {
Log.d("UserRepository", login)
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/acs/visits/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) {
runCatching {
val result = Network.client.patch("$serverUrl/api/open") {
headers {
append(HttpHeaders.Authorization, token)
}
setBody("""{"value":$code}""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Unit
}
}
suspend fun register(login: String, password: String): Result<Unit> =
withContext(Dispatchers.IO) {

View File

@ -12,7 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.displaynone.acss.R
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.VisitAdapter
import com.displaynone.acss.components.acs.models.visit.VisitAdapter
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.databinding.FragmentProfileBinding
import com.displaynone.acss.ui.profile.ProfileViewModel.Action

View File

@ -6,9 +6,9 @@ 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.repository.VisitListPagingSource
import com.displaynone.acss.components.acs.models.visit.VisitListPagingSource
import com.displaynone.acss.components.acs.models.visit.VisitServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
@ -36,7 +36,7 @@ class ProfileViewModel(): ViewModel() {
maxSize = 100
)
) {
VisitListPagingSource(UserServiceST.getInstance()::getMyLastVisits)
VisitListPagingSource(VisitServiceST.getInstance()::getMyLastVisits)
}.flow
.cachedIn(viewModelScope)
@ -44,7 +44,7 @@ class ProfileViewModel(): ViewModel() {
config = PagingConfig(pageSize = 20, enablePlaceholders = false, maxSize = 100)
) {
VisitListPagingSource { pageNum, pageSize ->
UserServiceST.getInstance().getLastVisitsByLogin(pageNum,pageSize, login)
VisitServiceST.getInstance().getLastVisitsByLogin(pageNum,pageSize, login)
}
}.flow.cachedIn(viewModelScope)

View File

@ -6,6 +6,7 @@ import android.app.Application
import android.net.http.HttpException
import android.util.Log
import androidx.lifecycle.viewModelScope
import com.displaynone.acss.components.acs.models.gate.GateServiceST
import com.displaynone.acss.components.auth.models.user.UserServiceST
import kotlinx.coroutines.launch
import kotlinx.io.IOException
@ -18,7 +19,7 @@ class QrResultViewModel(application: Application) : AndroidViewModel(application
viewModelScope.launch {
try {
val stringCode = code.toString()
UserServiceST.getInstance().openDoor(stringCode).fold(
GateServiceST.getInstance().openDoor(stringCode).fold(
onSuccess = { response ->
Log.d("QrResultViewModel", "Door opened")
_state.emit(State.Result(

View File

@ -209,16 +209,6 @@
</LinearLayout>
</LinearLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_nav_menu"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>