Чё-то делается

This commit is contained in:
ElEgEv 2023-12-12 01:06:48 +04:00
parent 178d0953d3
commit 9fea8e3bc4
48 changed files with 921 additions and 99 deletions

Binary file not shown.

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="ModuleClassLoaderOverlays">
<paths>
<option value="C:\Users\egore\AppData\Local\Temp\overlay16197097467506260191" />
</paths>
</component>
</module>

View File

@ -0,0 +1,11 @@
package ru.ulstu.`is`.pmu.tank.api
object ApiRoutes {
const val BASE = "http://10.0.2.2:8079/"
const val USER = "users"
const val LEVEL = "level"
const val NATION = "nation"
const val TANK = "TANK"
const val USER_TANK = "userTanks"
const val NOT_USER_TANK = "notUserTanks"
}

View File

@ -0,0 +1,184 @@
package ru.ulstu.`is`.pmu.tank.api
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
import ru.ulstu.`is`.pmu.tank.api.model.LevelRemote
import ru.ulstu.`is`.pmu.tank.api.model.NationRemote
import ru.ulstu.`is`.pmu.tank.api.model.TankRemote
import ru.ulstu.`is`.pmu.tank.api.model.TankWithNationAndLevelRemote
import ru.ulstu.`is`.pmu.tank.api.model.UserRemote
import ru.ulstu.`is`.pmu.tank.api.model.UserTankCrossRefRemote
interface ServerService {
// :[USER]
@GET(ApiRoutes.USER)
suspend fun getUserByEmail(
@Query("email") email: String
): UserRemote
@POST(ApiRoutes.USER)
suspend fun insertUser(
@Body user: UserRemote
): UserRemote
@PUT("${ApiRoutes.USER}/{id}")
suspend fun updateUser(
@Path("id") id: Long,
@Body user: UserRemote
): UserRemote
// ![USER]
// :[LEVEL]
@GET(ApiRoutes.LEVEL)
suspend fun getLevels(): List<LevelRemote>
@GET("${ApiRoutes.LEVEL}/{id}")
suspend fun getLevel(
@Path("id") id: Long
): LevelRemote
@POST(ApiRoutes.LEVEL)
suspend fun insertLevel(
@Body level: LevelRemote
): LevelRemote
@PUT("${ApiRoutes.LEVEL}/{id}")
suspend fun updateLevel(
@Path("id") id: Long,
@Body level: LevelRemote
): LevelRemote
@DELETE("${ApiRoutes.LEVEL}/{id}")
suspend fun deleteLevel(
@Path("id") id: Long
): LevelRemote
// ![LEVEL]
// :[NATION]
@GET(ApiRoutes.NATION)
suspend fun getNations(
@Query("_page") page: Int,
@Query("_limit") limit: Int
): List<NationRemote>
@GET("${ApiRoutes.NATION}/{id}")
suspend fun getNation(
@Path("id") id: Long
): NationRemote
@POST(ApiRoutes.NATION)
suspend fun insertNation(
@Body product: NationRemote
): NationRemote
@PUT("${ApiRoutes.NATION}/{id}")
suspend fun updateNation(
@Path("id") id: Long,
@Body product: NationRemote
): NationRemote
@DELETE("${ApiRoutes.NATION}/{id}")
suspend fun deleteNation(
@Path("id") id: Long
): NationRemote
// ![NATION]
// :[TANK]
@GET(ApiRoutes.TANK)
suspend fun getTanks(): List<TankRemote>
@GET("${ApiRoutes.TANK}/{id}")
suspend fun getTank(
@Path("id") id: Long
): TankRemote
@GET("${ApiRoutes.TANK}/forPurchase")
suspend fun getTanksForPurchase(
@Path("id") userId: Long
): List<TankRemote>
@GET("${ApiRoutes.TANK}/myHangar")
suspend fun getTanksFromHangar(
@Path("id") userId: Long
): List<TankWithNationAndLevelRemote>
@POST(ApiRoutes.TANK)
suspend fun insertTank(
@Body tank: TankRemote
): TankRemote
@PUT("${ApiRoutes.TANK}/{id}")
suspend fun updateTank(
@Path("id") id: Long,
@Body tank: TankRemote
): TankRemote
@DELETE("${ApiRoutes.TANK}/{id}")
suspend fun deleteTank(
@Path("id") id: Long
): TankRemote
// ![TANK]
// :[USER_TANK_CROSS_REF]
@GET("${ApiRoutes.USER_TANK}?_hangar={userId}")
suspend fun getUserTankCrossRef(
@Path("userId") userId: Long,
): List<UserTankCrossRefRemote>
@POST(ApiRoutes.USER_TANK)
suspend fun insertUserTankCrossRef(
@Body tank: UserTankCrossRefRemote
) : UserTankCrossRefRemote
@DELETE("${ApiRoutes.USER_TANK}/deleteMyTank")
suspend fun deleteUserTankCrossRef(
@Body tank: UserTankCrossRefRemote
): UserTankCrossRefRemote
// ![USER_TANK_CROSS_REF]
companion object {
private const val BASE_URL = ApiRoutes.BASE
@Volatile
private var INSTANCE: ServerService? = null
fun getInstance(): ServerService {
return INSTANCE ?: synchronized(this) {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
.create(ServerService::class.java)
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,111 @@
package ru.ulstu.`is`.pmu.tank.api.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import retrofit2.HttpException
import ru.ulstu.`is`.pmu.tank.api.ServerService
import ru.ulstu.`is`.pmu.tank.api.model.toLevel
import ru.ulstu.`is`.pmu.tank.api.model.toNation
import ru.ulstu.`is`.pmu.tank.database.AppDatabase
import ru.ulstu.`is`.pmu.tank.model.Nation
import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType
import ru.ulstu.`is`.pmu.tank.model.RemoteKeys
import ru.ulstu.`is`.pmu.tank.repository.OfflineNationRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineRemoteKeyRepository
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class NationRemoteMediator(
private val service: ServerService,
private val dbNationRepository: OfflineNationRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: AppDatabase
) : RemoteMediator<Int, Nation>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Nation>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val nations = service.getNations(page, state.config.pageSize).map { it.toNation() }
val endOfPaginationReached = nations.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.NATION)
dbNationRepository.deleteNations()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = nations.map {
RemoteKeys(
entityId = it.uid!!.toInt(),
type = RemoteKeyType.NATION,
prevKey = prevKey,
nextKey = nextKey
)
}
dbRemoteKeyRepository.createRemoteKeys(keys)
dbNationRepository.insertNations(nations)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Nation>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { nation ->
dbRemoteKeyRepository.getAllRemoteKeys(nation.uid!!.toInt(), RemoteKeyType.NATION)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Nation>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { nation ->
dbRemoteKeyRepository.getAllRemoteKeys(nation.uid!!.toInt(), RemoteKeyType.NATION)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Nation>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.uid?.let { nationUid ->
dbRemoteKeyRepository.getAllRemoteKeys(nationUid.toInt(), RemoteKeyType.NATION)
}
}
}
}

View File

@ -0,0 +1,22 @@
package ru.ulstu.`is`.pmu.tank.api.model
import androidx.room.ColumnInfo
import kotlinx.serialization.Serializable
import ru.ulstu.`is`.pmu.tank.model.Level
import ru.ulstu.`is`.pmu.tank.model.Nation
@Serializable
data class LevelRemote (
val uid: Long = 0,
val level: Int = 0
)
fun LevelRemote.toLevel(): Level = Level(
uid,
level
)
fun Level.toRemote(): LevelRemote = LevelRemote(
uid!!,
level
)

View File

@ -0,0 +1,20 @@
package ru.ulstu.`is`.pmu.tank.api.model
import kotlinx.serialization.Serializable
import ru.ulstu.`is`.pmu.tank.model.Nation
@Serializable
data class NationRemote(
val uid: Long = 0,
val nationName: String = ""
)
fun NationRemote.toNation(): Nation = Nation(
uid,
nationName
)
fun Nation.toRemote(): NationRemote = NationRemote(
uid!!,
nationName
)

View File

@ -0,0 +1,38 @@
package ru.ulstu.`is`.pmu.tank.api.model
import android.graphics.Bitmap
import com.application.ui.toBase64
import com.application.ui.toBitmap
import kotlinx.serialization.Serializable
import ru.ulstu.`is`.pmu.tank.model.Tank
@Serializable
data class TankRemote(
val tankId: Long = 0,
val name: String = "",
val price: Int = 0,
val miniature: String = "",
val imageId: Long = 0,
val levelId: Long = 0,
val nationId: Long = 0,
)
fun TankRemote.toTank(): Tank = Tank(
tankId = tankId,
name = name,
price = price,
miniature = miniature.toBitmap(),
imageId = imageId,
levelId = levelId,
nationId = nationId
)
fun Tank.toRemote(): TankRemote = TankRemote(
tankId = tankId!!,
name = name,
price = price,
miniature = miniature.toBase64(),
imageId = imageId,
levelId = levelId!!,
nationId = nationId!!
)

View File

@ -0,0 +1,34 @@
package ru.ulstu.`is`.pmu.tank.api.model
import com.application.ui.toBase64
import com.application.ui.toBitmap
import kotlinx.serialization.Serializable
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
@Serializable
data class TankWithNationAndLevelRemote(
val tankId: Long,
val name: String,
val price: Int,
val image: String,
val level: Int,
val nationName: String
)
fun TankWithNationAndLevelRemote.toTankWithNationAndLevel(): TankWithNationAndLevel = TankWithNationAndLevel(
tankId = tankId,
name = name,
price = price,
image = image.toBitmap(),
level = level,
nationName = nationName
)
fun TankWithNationAndLevel.toRemote(): TankWithNationAndLevelRemote = TankWithNationAndLevelRemote(
tankId = tankId!!,
name = name,
price = price,
image = image.toBase64(),
level = level,
nationName = nationName
)

View File

@ -0,0 +1,33 @@
package ru.ulstu.`is`.pmu.tank.api.model
import kotlinx.serialization.Serializable
import ru.ulstu.`is`.pmu.tank.model.User
import ru.ulstu.`is`.pmu.tank.model.UserRole
@Serializable
data class UserRemote (
val id: Long = 0,
val nickname: String = "",
val email: String = " ",
val role: Int = -1,
val password: String = "",
val balance: Int = 0
)
fun UserRemote.toUser(): User = User(
userId = id,
nickname = nickname,
email = email,
password = password,
role = enumValues<UserRole>()[role],
balance = balance
)
fun User.toRemote(): UserRemote = UserRemote(
id = userId,
nickname = nickname,
email = email,
password = password,
role = role.ordinal,
balance = balance
)

View File

@ -0,0 +1,9 @@
package ru.ulstu.`is`.pmu.tank.api.model
import kotlinx.serialization.Serializable
@Serializable
data class UserTankCrossRefRemote (
val userId: Long = 0,
val tankId: Long = 0
)

View File

@ -0,0 +1,51 @@
package ru.ulstu.`is`.pmu.tank.api.repository
import kotlinx.coroutines.flow.Flow
import ru.ulstu.`is`.pmu.tank.api.ServerService
import ru.ulstu.`is`.pmu.tank.api.model.toLevel
import ru.ulstu.`is`.pmu.tank.api.model.toRemote
import ru.ulstu.`is`.pmu.tank.model.Level
import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks
import ru.ulstu.`is`.pmu.tank.repository.LevelRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineLevelRepository
class RestLevelRepository(
private val service: ServerService,
private val dbLevelRepository: OfflineLevelRepository,
) : LevelRepository {
override suspend fun getAllLevels(): List<Level> {
dbLevelRepository.deleteAll()
val levels = service.getLevels().map { it.toLevel() }
dbLevelRepository.insertMany(levels)
return levels
}
override suspend fun getSimpleLevel(uid: Long): Level {
TODO("Not yet implemented")
}
override fun getFullLevel(uid: Long): Flow<LevelWithTanks?> {
TODO("Not yet implemented")
}
override suspend fun insertLevel(level: Level) {
service.insertLevel(level.toRemote())
}
override suspend fun insertMany(levels: List<Level>) {
TODO("Not yet implemented")
}
override suspend fun updateLevel(level: Level) {
service.updateLevel(level.uid!!, level.toRemote())
}
override suspend fun deleteLevel(level: Level) {
service.deleteLevel(level.uid!!)
dbLevelRepository.deleteLevel(level)
}
override suspend fun deleteAll() {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,84 @@
package ru.ulstu.`is`.pmu.tank.api.repository
import android.util.Log
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import androidx.paging.map
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import ru.ulstu.`is`.pmu.tank.api.ServerService
import ru.ulstu.`is`.pmu.tank.api.mediator.NationRemoteMediator
import ru.ulstu.`is`.pmu.tank.api.model.NationRemote
import ru.ulstu.`is`.pmu.tank.api.model.toNation
import ru.ulstu.`is`.pmu.tank.api.model.toRemote
import ru.ulstu.`is`.pmu.tank.database.AppContainer
import ru.ulstu.`is`.pmu.tank.database.AppDatabase
import ru.ulstu.`is`.pmu.tank.model.Nation
import ru.ulstu.`is`.pmu.tank.model.NationWithTanks
import ru.ulstu.`is`.pmu.tank.repository.NationRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineNationRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineRemoteKeyRepository
class RestNationRepository(
private val service: ServerService,
private val dbNationRepository: OfflineNationRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: AppDatabase
) : NationRepository {
override suspend fun getAllNations(): List<Nation> {
TODO("Not yet implemented")
}
override suspend fun getAll(): Flow<PagingData<Nation>> {
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = 10,
enablePlaceholders = false
),
remoteMediator = NationRemoteMediator(
service = service,
dbNationRepository = dbNationRepository,
dbRemoteKeyRepository = dbRemoteKeyRepository,
database = database,
),
) {
dbNationRepository.pagingSource()
}.flow
}
override fun getSimpleNation(uid: Long): Flow<Nation?> {
TODO("Not yet implemented")
}
override fun getFullNation(uid: Long): Flow<NationWithTanks?> {
TODO("Not yet implemented")
}
override fun pagingSource(): PagingSource<Int, Nation> {
TODO("Not yet implemented")
}
override suspend fun insertNation(nation: Nation) {
service.insertNation(nation.toRemote()).toNation()
}
override suspend fun insertNations(nations: List<Nation>) {
TODO("Not yet implemented")
}
override suspend fun updateNation(nation: Nation) {
service.updateNation(nation.uid!!, nation.toRemote())
}
override suspend fun deleteNation(nation: Nation) {
service.deleteNation(nation.uid!!)
}
override suspend fun deleteNations() {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,66 @@
package ru.ulstu.`is`.pmu.tank.api.repository
import android.graphics.Bitmap
import kotlinx.coroutines.flow.Flow
import ru.ulstu.`is`.pmu.tank.api.ServerService
import ru.ulstu.`is`.pmu.tank.api.model.toLevel
import ru.ulstu.`is`.pmu.tank.api.model.toRemote
import ru.ulstu.`is`.pmu.tank.api.model.toTank
import ru.ulstu.`is`.pmu.tank.api.model.toTankWithNationAndLevel
import ru.ulstu.`is`.pmu.tank.api.model.toUser
import ru.ulstu.`is`.pmu.tank.model.Tank
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
import ru.ulstu.`is`.pmu.tank.model.User
import ru.ulstu.`is`.pmu.tank.repository.OfflineTankRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineUserRepository
import ru.ulstu.`is`.pmu.tank.repository.TankRepository
import ru.ulstu.`is`.pmu.tank.repository.UserRepository
class RestTankReppository (
private val service: ServerService,
private val dbTankRepository: OfflineTankRepository,
) : TankRepository {
override suspend fun getAll(): List<Tank> {
dbTankRepository.deleteAll()
val tanks = service.getTanks().map { it.toTank() }
dbTankRepository.insertMany(tanks)
return tanks
}
override suspend fun getForUserAll(userId: Long): List<Tank> {
return service.getTanksForPurchase(userId).map { it.toTank() }
}
override suspend fun getTank(uid: Long): Tank {
return service.getTank(uid).toTank()
}
override suspend fun getUserTanks(uid: Long): List<TankWithNationAndLevel> {
return service.getTanksFromHangar(uid).map { it.toTankWithNationAndLevel() }
}
override suspend fun insertTank(tank: Tank, image: Bitmap) {
service.insertTank(tank.toRemote())
}
override suspend fun insertMany(tankList: List<Tank>) {
TODO("Not yet implemented")
}
override suspend fun updateTank(tank: Tank, image: Bitmap) {
service.updateTank(tank.tankId!!, tank.toRemote())
}
override suspend fun deleteTank(tank: Tank) {
TODO("Not yet implemented")
}
override suspend fun delete(tankId: Long) {
service.deleteTank(tankId)
dbTankRepository.delete(tankId)
}
override suspend fun deleteAll() {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,44 @@
package ru.ulstu.`is`.pmu.tank.api.repository
import kotlinx.coroutines.flow.Flow
import ru.ulstu.`is`.pmu.tank.api.ServerService
import ru.ulstu.`is`.pmu.tank.api.model.toLevel
import ru.ulstu.`is`.pmu.tank.api.model.toRemote
import ru.ulstu.`is`.pmu.tank.api.model.toUser
import ru.ulstu.`is`.pmu.tank.model.Level
import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
import ru.ulstu.`is`.pmu.tank.model.User
import ru.ulstu.`is`.pmu.tank.repository.LevelRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineLevelRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineUserRepository
import ru.ulstu.`is`.pmu.tank.repository.UserRepository
class RestUserRepository(
private val service: ServerService,
private val dbUserRepository: OfflineUserRepository,
) : UserRepository {
override fun getAllUsers(): Flow<List<User>> {
TODO("Not yet implemented")
}
override suspend fun getSimpleUser(email: String): User {
return service.getUserByEmail(email).toUser()
}
override fun getFullUser(uid: Long): Flow<Map<User, List<TankWithNationAndLevel>>> {
TODO("Not yet implemented")
}
override suspend fun insertUser(user: User) {
service.insertUser(user.toRemote())
}
override suspend fun updateUser(user: User) {
service.updateUser(user.userId, user.toRemote())
}
override suspend fun deleteUser(user: User) {
TODO("Not yet implemented")
}
}

View File

@ -73,13 +73,11 @@ fun TankList(
viewModel: TankListViewModel = viewModel(factory = AppViewModelProvider.Factory), viewModel: TankListViewModel = viewModel(factory = AppViewModelProvider.Factory),
listNations: NationDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory) listNations: NationDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory)
) { ) {
val tankListUiState by viewModel.tankListUiState.collectAsState()
// Lazy Column, Pass the numbers array // Lazy Column, Pass the numbers array
if (navController != null) { if (navController != null) {
TankList( TankList(
nations = listNations.nationsListUiState, nations = listNations.nationsListUiState,
listTanks = tankListUiState.tankList listTanks = viewModel.tankListUiState.tankList
) { uid: Long -> ) { uid: Long ->
val route = Screen.Constructor.route.replace("{id}", uid.toString()) val route = Screen.Constructor.route.replace("{id}", uid.toString())
navController.navigate(route) navController.navigate(route)

View File

@ -33,8 +33,6 @@ class TankEditViewModel(
viewModelScope.launch { viewModelScope.launch {
if (tankUid > 0) { if (tankUid > 0) {
tankUiState = tankRepository.getTank(tankUid) tankUiState = tankRepository.getTank(tankUid)
.filterNotNull()
.first()
.toUiState(true) .toUiState(true)
} }
} }

View File

@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.ulstu.`is`.pmu.tank.model.User import ru.ulstu.`is`.pmu.tank.model.User
import ru.ulstu.`is`.pmu.tank.model.UserRole
import ru.ulstu.`is`.pmu.tank.repository.UserRepository import ru.ulstu.`is`.pmu.tank.repository.UserRepository
class UserEditViewModel( class UserEditViewModel(
@ -22,14 +23,12 @@ class UserEditViewModel(
//private val userUid: Long = checkNotNull(savedStateHandle["id"]) //private val userUid: Long = checkNotNull(savedStateHandle["id"])
private val userUid: Long = 100L private val userEmail: String = "egor@mail.ru"
init { init {
viewModelScope.launch { viewModelScope.launch {
if (userUid > 0) { if (userEmail.length > 0) {
userUiState = userRepository.getSimpleUser(userUid) userUiState = userRepository.getSimpleUser(userEmail)
.filterNotNull()
.first()
.toUiState(true) .toUiState(true)
} }
} }
@ -44,8 +43,8 @@ class UserEditViewModel(
suspend fun saveUser() { suspend fun saveUser() {
if (validateInput()) { if (validateInput()) {
if (userUid > 0) { if (userEmail.length > 0) {
userRepository.updateUser(userUiState.userDetails.toUser(userUid)) userRepository.updateUser(userUiState.userDetails.toUser(100L))
} else { } else {
userRepository.insertUser(userUiState.userDetails.toUser()) userRepository.insertUser(userUiState.userDetails.toUser())
} }
@ -57,6 +56,7 @@ class UserEditViewModel(
nickname.isNotBlank() nickname.isNotBlank()
&& email.isNotBlank() && email.isNotBlank()
&& password.isNotBlank() && password.isNotBlank()
&& role.value > -1
&& balance > 0 && balance > 0
} }
} }
@ -71,6 +71,7 @@ data class UserDetails(
val nickname: String = "", val nickname: String = "",
val email: String = "", val email: String = "",
val password: String = "", val password: String = "",
val role: UserRole = UserRole.USER,
val balance: Int = 0, val balance: Int = 0,
) )
@ -79,6 +80,7 @@ fun UserDetails.toUser(uid: Long = 0): User = User(
nickname = nickname, nickname = nickname,
email = email, email = email,
password = password, password = password,
role = role,
balance = balance balance = balance
) )
@ -86,6 +88,7 @@ fun User.toDetails(): UserDetails = UserDetails(
nickname = nickname, nickname = nickname,
email = email, email = email,
password = password, password = password,
role = role,
balance = balance balance = balance
) )

View File

@ -1,5 +1,8 @@
package ru.ulstu.`is`.pmu.tank.composeui.list package ru.ulstu.`is`.pmu.tank.composeui.list
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@ -15,21 +18,11 @@ class TankListViewModel(
private val tankRepository: TankRepository, private val tankRepository: TankRepository,
private var userId: Long = 100L private var userId: Long = 100L
) : ViewModel() { ) : ViewModel() {
val tankListUiState: StateFlow<TankListUiState> = tankRepository.getForUserAll(userId).map { var tankListUiState by mutableStateOf(TankListUiState())
TankListUiState(it) private set
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = TankListUiState()
)
val usersTanksUiState: StateFlow<UserTankListUiState> = tankRepository.getUserTanks(userId).map { var usersTanksUiState by mutableStateOf(UserTankListUiState())
UserTankListUiState(it) private set
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = UserTankListUiState()
)
suspend fun deleteTank(tank: Tank) { suspend fun deleteTank(tank: Tank) {
tankRepository.deleteTank(tank) tankRepository.deleteTank(tank)

View File

@ -30,14 +30,21 @@ interface LevelDao {
@Query( @Query(
"SELECT level FROM levels where levels.uid = :uid" "SELECT level FROM levels where levels.uid = :uid"
) )
open fun getSimpleLevelUid(uid: Long): Flow<Level?> open fun getSimpleLevelUid(uid: Long): Level
@Insert @Insert
suspend fun insert(level: Level) suspend fun insert(level: Level)
@Insert
suspend fun insertMany(level: List<Level>)
@Update @Update
suspend fun update(level: Level) suspend fun update(level: Level)
@Delete @Delete
suspend fun delete(level: Level) suspend fun delete(level: Level)
@Query("DELETE FROM levels")
suspend fun deleteAll()
} }

View File

@ -23,24 +23,33 @@ interface NationDao {
suspend fun getNationsWithTanks(): List<NationWithTanks> suspend fun getNationsWithTanks(): List<NationWithTanks>
//получить конкретную нацию //получить конкретную нацию
@Transaction
@Query("select * from nations where nations.uid = :uid") @Query("select * from nations where nations.uid = :uid")
fun getNationUid(uid: Long): Flow<NationWithTanks?> fun getNationUid(uid: Long): Flow<NationWithTanks?>
//получить нацию без списка танков //получить нацию без списка танков
@Transaction
@Query( @Query(
"SELECT nationName FROM nations where nations.uid = :uid" "SELECT nationName FROM nations where nations.uid = :uid"
) )
open fun getSimpleNationUid(uid: Long): Flow<Nation?> open fun getSimpleNationUid(uid: Long): Flow<Nation?>
@Transaction
@Query("SELECT * FROM nations") @Query("SELECT * FROM nations")
fun pagingSource(): PagingSource<Int, Nation> fun pagingSource(): PagingSource<Int, Nation>
@Insert @Insert
suspend fun insert(nation: Nation) suspend fun insert(nation: Nation)
@Insert
suspend fun insertMany(nations: List<Nation>)
@Update @Update
suspend fun update(nation: Nation) suspend fun update(nation: Nation)
@Delete @Delete
suspend fun delete(nation: Nation) suspend fun delete(nation: Nation)
@Query("DELETE FROM nations")
suspend fun deleteAll()
} }

View File

@ -0,0 +1,20 @@
package ru.ulstu.`is`.pmu.tank.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType
import ru.ulstu.`is`.pmu.tank.model.RemoteKeys
@Dao
interface RemoteKeysDao {
@Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type")
suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys>)
@Query("DELETE FROM remote_keys WHERE type = :type")
suspend fun clearRemoteKeys(type: RemoteKeyType)
}

View File

@ -16,33 +16,36 @@ import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
@Dao @Dao
interface TankDao { interface TankDao {
@Query("select * from tanks GROUP BY nationId, levelId ORDER BY nationId") @Query("select * from tanks GROUP BY nationId, levelId ORDER BY nationId")
fun getAll(): Flow<List<Tank>> fun getAll(): List<Tank>
//получить конкретный танк //получить конкретный танк
@Query("select t.*, ti.data from tanks AS t " + @Query("select t.*, ti.data from tanks AS t " +
"LEFT JOIN tank_images as ti on t.image_id = ti.image_id " + "LEFT JOIN tank_images as ti on t.image_id = ti.image_id " +
"where t.tankId = :uid") "where t.tankId = :uid")
fun getTankUid(uid: Long): Flow<Tank?> fun getTankUid(uid: Long): Tank
//получаем все танки пользователя по его Id //получаем все танки пользователя по его Id
@Query( @Query(
"SELECT t.tankId, t.name, t.price, t.image_id, l.level, n.nationName, ti.data AS image FROM UserTankCrossRef AS ut " + "SELECT t.tankId, t.name, t.price, t.image_id, l.level, n.nationName, ti.data AS image FROM users_tanks AS ut " +
"LEFT JOIN tanks as t on ut.tankId = t.tankId " + "LEFT JOIN tanks as t on ut.tankId = t.tankId " +
"LEFT JOIN tank_images as ti on t.image_id = ti.image_id " + "LEFT JOIN tank_images as ti on t.image_id = ti.image_id " +
"LEFT JOIN levels as l on t.levelId = l.uid " + "LEFT JOIN levels as l on t.levelId = l.uid " +
"LEFT JOIN nations as n on t.nationId = n.uid " + "LEFT JOIN nations as n on t.nationId = n.uid " +
"WHERE ut.userId = :uid GROUP BY t.nationId, t.levelId ORDER BY t.nationId" "WHERE ut.userId = :uid GROUP BY t.nationId, t.levelId ORDER BY t.nationId"
) )
fun getUserTanks(uid: Long): Flow<List<TankWithNationAndLevel>> fun getUserTanks(uid: Long): List<TankWithNationAndLevel>
@Query( @Query(
"SELECT t.* FROM tanks AS t WHERE t.tankId NOT IN (SELECT ut.tankId FROM UserTankCrossRef AS ut WHERE ut.userId = :uid)" "SELECT t.* FROM tanks AS t WHERE t.tankId NOT IN (SELECT ut.tankId FROM users_tanks AS ut WHERE ut.userId = :uid)"
) )
fun getNotUserTank(uid: Long): Flow<List<Tank>> fun getNotUserTank(uid: Long): List<Tank>
@Insert @Insert
suspend fun insert(tank: Tank) suspend fun insert(tank: Tank)
@Insert
suspend fun insertMany(tankList: List<Tank>)
@Update @Update
suspend fun update(tank: Tank) suspend fun update(tank: Tank)
@ -51,4 +54,7 @@ interface TankDao {
@Query("DELETE FROM tanks WHERE tankId = :id") @Query("DELETE FROM tanks WHERE tankId = :id")
suspend fun delete(id: Long) suspend fun delete(id: Long)
@Query("DELETE FROM tanks")
suspend fun deleteAll()
} }

View File

@ -11,7 +11,6 @@ import ru.ulstu.`is`.pmu.tank.model.Tank
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
import ru.ulstu.`is`.pmu.tank.model.User import ru.ulstu.`is`.pmu.tank.model.User
import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef
import ru.ulstu.`is`.pmu.tank.model.UserWithTanks
@Dao @Dao
interface UserDao { interface UserDao {
@ -21,7 +20,7 @@ interface UserDao {
//получить конкретного пользователя //получить конкретного пользователя
@Query( @Query(
"SELECT u.*, t.*, l.level, n.nationName, ti.data AS image FROM users AS u " + "SELECT u.*, t.*, l.level, n.nationName, ti.data AS image FROM users AS u " +
"LEFT JOIN UserTankCrossRef as ut on u.userId = ut.userId " + "LEFT JOIN users_tanks as ut on u.userId = ut.userId " +
"LEFT JOIN tanks as t on ut.tankId = t.tankId " + "LEFT JOIN tanks as t on ut.tankId = t.tankId " +
"LEFT JOIN tank_images as ti on ut.tankId = ti.image_id " + "LEFT JOIN tank_images as ti on ut.tankId = ti.image_id " +
"LEFT JOIN levels as l on t.levelId = l.uid " + "LEFT JOIN levels as l on t.levelId = l.uid " +
@ -30,8 +29,8 @@ interface UserDao {
) )
fun getUserUid(uid: Long): Flow<Map<User, List<TankWithNationAndLevel>>> fun getUserUid(uid: Long): Flow<Map<User, List<TankWithNationAndLevel>>>
@Query("select * from users where users.userId = :uid") @Query("select * from users where users.email = :email")
fun getSimpleUserUid(uid: Long): Flow<User?> fun getSimpleUserUid(email: String): User
//добавить танк в ангар пользователя //добавить танк в ангар пользователя
@Insert @Insert

View File

@ -20,8 +20,8 @@ import ru.ulstu.`is`.pmu.tank.model.Nation
import ru.ulstu.`is`.pmu.tank.model.Tank import ru.ulstu.`is`.pmu.tank.model.Tank
import ru.ulstu.`is`.pmu.tank.model.TankImage import ru.ulstu.`is`.pmu.tank.model.TankImage
import ru.ulstu.`is`.pmu.tank.model.User import ru.ulstu.`is`.pmu.tank.model.User
import ru.ulstu.`is`.pmu.tank.model.UserRole
import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef
import ru.ulstu.`is`.pmu.tank.model.UserWithTanks
//тут, собственно говоря, всё и мутится с БД :))) //тут, собственно говоря, всё и мутится с БД :)))
@Database(entities = [Nation::class, Level::class, Tank::class, User::class, UserTankCrossRef::class, TankImage::class], version = 1, exportSchema = false) @Database(entities = [Nation::class, Level::class, Tank::class, User::class, UserTankCrossRef::class, TankImage::class], version = 1, exportSchema = false)
@ -34,7 +34,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun tankImageDao() : TankImageDao abstract fun tankImageDao() : TankImageDao
companion object { companion object {
private const val DB_NAME: String = "19-db" private const val DB_NAME: String = "20-db"
@Volatile @Volatile
private var INSTANCE: AppDatabase? = null private var INSTANCE: AppDatabase? = null
@ -129,7 +129,7 @@ abstract class AppDatabase : RoomDatabase() {
//Users //Users
val userDao = database.userDao() val userDao = database.userDao()
val user = User(100L,"3tankista73", "egor@mail.ru", "12032003", 10000000) val user = User(100L,"3tankista73", "egor@mail.ru", "12032003", UserRole.ADMIN, 10000000)
userDao.insert(user) userDao.insert(user)
@ -148,14 +148,14 @@ abstract class AppDatabase : RoomDatabase() {
AppDatabase::class.java, AppDatabase::class.java,
DB_NAME DB_NAME
) )
.addCallback(object : Callback() { // .addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) { // override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db) // super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch { // CoroutineScope(Dispatchers.IO).launch {
populateDatabase(appContext) // populateDatabase(appContext)
} // }
} // }
}) // })
.build() .build()
.also { INSTANCE = it } .also { INSTANCE = it }
} }

View File

@ -0,0 +1,25 @@
package ru.ulstu.`is`.pmu.tank.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
enum class RemoteKeyType(private val type: String) {
NATION(Nation::class.simpleName ?: "Nation");
@TypeConverter
fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value }
@TypeConverter
fun fromRemoteKeyType(value: RemoteKeyType) = value.type
}
@Entity(tableName = "remote_keys")
data class RemoteKeys(
@PrimaryKey val entityId: Int,
@TypeConverters(RemoteKeyType::class)
val type: RemoteKeyType,
val prevKey: Int?,
val nextKey: Int?
)

View File

@ -17,6 +17,8 @@ data class User (
val email: String, val email: String,
@ColumnInfo(name = "password") @ColumnInfo(name = "password")
val password: String, val password: String,
@ColumnInfo(name = "role")
val role: UserRole,
@ColumnInfo(name = "balance") @ColumnInfo(name = "balance")
val balance: Int val balance: Int
){ ){
@ -25,8 +27,9 @@ data class User (
nickname: String, nickname: String,
email: String, email: String,
password: String, password: String,
role: UserRole,
balance: Int balance: Int
) : this(0L, nickname, email, password, balance) ) : this(0L, nickname, email, password, UserRole.ADMIN, balance)
companion object { companion object {
fun getUser(index: Long = 0L): User { fun getUser(index: Long = 0L): User {
@ -35,6 +38,7 @@ data class User (
"3tankista73", "3tankista73",
"egor@mail.ru", "egor@mail.ru",
"1234567890!", "1234567890!",
UserRole.USER,
10000000 10000000
) )
} }
@ -50,6 +54,7 @@ data class User (
if (nickname != other.nickname) return false if (nickname != other.nickname) return false
if (email != other.email) return false if (email != other.email) return false
if (password != other.password) return false if (password != other.password) return false
if (role != other.role) return false
if (balance != other.balance) return false if (balance != other.balance) return false
return true return true
@ -60,6 +65,7 @@ data class User (
result = 31 * result + nickname.hashCode() result = 31 * result + nickname.hashCode()
result = 31 * result + email.hashCode() result = 31 * result + email.hashCode()
result = 31 * result + password.hashCode() result = 31 * result + password.hashCode()
result = 31 * result + role.hashCode()
result = 31 * result + balance result = 31 * result + balance
return result return result
} }

View File

@ -0,0 +1,6 @@
package ru.ulstu.`is`.pmu.tank.model
enum class UserRole(val value: Int) {
USER(0),
ADMIN(1)
}

View File

@ -8,7 +8,10 @@ import androidx.room.PrimaryKey
import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.NotNull
//many to many for user and tank //many to many for user and tank
@Entity(primaryKeys = ["userId", "tankId"]) @Entity(
tableName = "users_tanks",
primaryKeys = ["userId", "tankId"]
)
data class UserTankCrossRef( data class UserTankCrossRef(
val userId: Long, val userId: Long,
val tankId: Long val tankId: Long

View File

@ -6,9 +6,11 @@ import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks
interface LevelRepository { interface LevelRepository {
suspend fun getAllLevels(): List<Level> suspend fun getAllLevels(): List<Level>
fun getSimpleLevel(uid: Long): Flow<Level?> suspend fun getSimpleLevel(uid: Long): Level
fun getFullLevel(uid: Long): Flow<LevelWithTanks?> fun getFullLevel(uid: Long): Flow<LevelWithTanks?>
suspend fun insertLevel(level: Level) suspend fun insertLevel(level: Level)
suspend fun insertMany(levels: List<Level>)
suspend fun updateLevel(level: Level) suspend fun updateLevel(level: Level)
suspend fun deleteLevel(level: Level) suspend fun deleteLevel(level: Level)
suspend fun deleteAll()
} }

View File

@ -1,16 +1,21 @@
package ru.ulstu.`is`.pmu.tank.repository package ru.ulstu.`is`.pmu.tank.repository
import androidx.paging.PagingData
import androidx.paging.PagingSource import androidx.paging.PagingSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import ru.ulstu.`is`.pmu.tank.api.model.NationRemote
import ru.ulstu.`is`.pmu.tank.model.Nation import ru.ulstu.`is`.pmu.tank.model.Nation
import ru.ulstu.`is`.pmu.tank.model.NationWithTanks import ru.ulstu.`is`.pmu.tank.model.NationWithTanks
interface NationRepository { interface NationRepository {
suspend fun getAllNations(): List<Nation> suspend fun getAllNations(): List<Nation>
suspend fun getAll(): Flow<PagingData<Nation>>
fun getSimpleNation(uid: Long): Flow<Nation?> fun getSimpleNation(uid: Long): Flow<Nation?>
fun getFullNation(uid: Long): Flow<NationWithTanks?> fun getFullNation(uid: Long): Flow<NationWithTanks?>
fun pagingSource(): PagingSource<Int, Nation> fun pagingSource(): PagingSource<Int, Nation>
suspend fun insertNation(nation: Nation) suspend fun insertNation(nation: Nation)
suspend fun insertNations(nations: List<Nation>)
suspend fun updateNation(nation: Nation) suspend fun updateNation(nation: Nation)
suspend fun deleteNation(nation: Nation) suspend fun deleteNation(nation: Nation)
suspend fun deleteNations()
} }

View File

@ -8,13 +8,16 @@ import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks
class OfflineLevelRepository(private val levelDao: LevelDao) : LevelRepository { class OfflineLevelRepository(private val levelDao: LevelDao) : LevelRepository {
override suspend fun getAllLevels(): List<Level> = levelDao.getAll() override suspend fun getAllLevels(): List<Level> = levelDao.getAll()
override fun getSimpleLevel(uid: Long): Flow<Level?> = levelDao.getSimpleLevelUid(uid) override suspend fun getSimpleLevel(uid: Long): Level = levelDao.getSimpleLevelUid(uid)
override fun getFullLevel(uid: Long): Flow<LevelWithTanks?> = levelDao.getLevelUid(uid) override fun getFullLevel(uid: Long): Flow<LevelWithTanks?> = levelDao.getLevelUid(uid)
override suspend fun insertLevel(level: Level) = levelDao.insert(level) override suspend fun insertLevel(level: Level) = levelDao.insert(level)
override suspend fun insertMany(levels: List<Level>) = levelDao.insertMany(levels)
override suspend fun updateLevel(level: Level) = levelDao.update(level) override suspend fun updateLevel(level: Level) = levelDao.update(level)
override suspend fun deleteLevel(level: Level) = levelDao.delete(level) override suspend fun deleteLevel(level: Level) = levelDao.delete(level)
override suspend fun deleteAll() = levelDao.deleteAll()
} }

View File

@ -1,5 +1,6 @@
package ru.ulstu.`is`.pmu.tank.repository package ru.ulstu.`is`.pmu.tank.repository
import androidx.paging.PagingData
import androidx.paging.PagingSource import androidx.paging.PagingSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import ru.ulstu.`is`.pmu.tank.dao.NationDao import ru.ulstu.`is`.pmu.tank.dao.NationDao
@ -8,16 +9,25 @@ import ru.ulstu.`is`.pmu.tank.model.NationWithTanks
class OfflineNationRepository(private val nationDao: NationDao) : NationRepository { class OfflineNationRepository(private val nationDao: NationDao) : NationRepository {
override suspend fun getAllNations(): List<Nation> = nationDao.getAll() override suspend fun getAllNations(): List<Nation> = nationDao.getAll()
override suspend fun getAll(): Flow<PagingData<Nation>> {
TODO("Not yet implemented")
}
override fun getSimpleNation(uid: Long): Flow<Nation?> = nationDao.getSimpleNationUid(uid) override fun getSimpleNation(uid: Long): Flow<Nation?> = nationDao.getSimpleNationUid(uid)
override fun getFullNation(uid: Long): Flow<NationWithTanks?> = nationDao.getNationUid(uid) override fun getFullNation(uid: Long): Flow<NationWithTanks?> = nationDao.getNationUid(uid)
override fun pagingSource(): PagingSource<Int, Nation> = nationDao.pagingSource() override fun pagingSource(): PagingSource<Int, Nation> {
return nationDao.pagingSource()
}
override suspend fun insertNation(nation: Nation) = nationDao.insert(nation) override suspend fun insertNation(nation: Nation) = nationDao.insert(nation)
override suspend fun updateNation(nation: Nation) = nationDao.update(nation) override suspend fun updateNation(nation: Nation) = nationDao.update(nation)
override suspend fun deleteNation(nation: Nation) = nationDao.delete(nation) override suspend fun deleteNation(nation: Nation) = nationDao.delete(nation)
override suspend fun insertNations(nations: List<Nation>) = nationDao.insertMany(nations)
override suspend fun deleteNations() = nationDao.deleteAll()
} }

View File

@ -0,0 +1,16 @@
package ru.ulstu.`is`.pmu.tank.repository
import ru.ulstu.`is`.pmu.tank.dao.RemoteKeysDao
import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType
import ru.ulstu.`is`.pmu.tank.model.RemoteKeys
class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository {
override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) =
remoteKeysDao.getRemoteKeys(id, type)
override suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>) =
remoteKeysDao.insertAll(remoteKeys)
override suspend fun deleteRemoteKey(type: RemoteKeyType) =
remoteKeysDao.clearRemoteKeys(type)
}

View File

@ -13,13 +13,13 @@ class OfflineTankRepository(
private val tankDao: TankDao, private val tankDao: TankDao,
private val tankImageDao: TankImageDao private val tankImageDao: TankImageDao
) : TankRepository { ) : TankRepository {
override fun getAll(): Flow<List<Tank>> = tankDao.getAll() override suspend fun getAll(): List<Tank> = tankDao.getAll()
override fun getForUserAll(userId: Long): Flow<List<Tank>> = tankDao.getNotUserTank(userId) override suspend fun getForUserAll(userId: Long): List<Tank> = tankDao.getNotUserTank(userId)
override fun getTank(uid: Long): Flow<Tank?> = tankDao.getTankUid(uid) override suspend fun getTank(uid: Long): Tank = tankDao.getTankUid(uid)
override fun getUserTanks(uid: Long): Flow<List<TankWithNationAndLevel>> = tankDao.getUserTanks(uid) override suspend fun getUserTanks(uid: Long): List<TankWithNationAndLevel> = tankDao.getUserTanks(uid)
override suspend fun insertTank(tank: Tank, image: Bitmap) { override suspend fun insertTank(tank: Tank, image: Bitmap) {
val imageId = tankImageDao.insert( val imageId = tankImageDao.insert(
@ -32,6 +32,10 @@ class OfflineTankRepository(
tankDao.insert(tank.copy(imageId = imageId)) tankDao.insert(tank.copy(imageId = imageId))
} }
override suspend fun insertMany(tankList: List<Tank>) {
tankDao.insertMany(tankList)
}
override suspend fun updateTank(tank: Tank, image: Bitmap) { override suspend fun updateTank(tank: Tank, image: Bitmap) {
val imageId = tankImageDao.insert( val imageId = tankImageDao.insert(
TankImage( TankImage(
@ -49,4 +53,8 @@ class OfflineTankRepository(
override suspend fun delete(tankId: Long) { override suspend fun delete(tankId: Long) {
tankDao.delete(tankId) tankDao.delete(tankId)
} }
override suspend fun deleteAll() {
tankDao.deleteAll()
}
} }

View File

@ -8,7 +8,7 @@ import ru.ulstu.`is`.pmu.tank.model.User
class OfflineUserRepository(private val userDao: UserDao) : UserRepository { class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
override fun getAllUsers(): Flow<List<User>> = userDao.getAll() override fun getAllUsers(): Flow<List<User>> = userDao.getAll()
override fun getSimpleUser(uid: Long): Flow<User?> = userDao.getSimpleUserUid(uid) override suspend fun getSimpleUser(email: String): User = userDao.getSimpleUserUid(email)
override fun getFullUser(uid: Long): Flow<Map<User, List<TankWithNationAndLevel>>> = userDao.getUserUid(uid) override fun getFullUser(uid: Long): Flow<Map<User, List<TankWithNationAndLevel>>> = userDao.getUserUid(uid)

View File

@ -0,0 +1,10 @@
package ru.ulstu.`is`.pmu.tank.repository
import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType
import ru.ulstu.`is`.pmu.tank.model.RemoteKeys
interface RemoteKeyRepository {
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>)
suspend fun deleteRemoteKey(type: RemoteKeyType)
}

View File

@ -7,12 +7,14 @@ import ru.ulstu.`is`.pmu.tank.model.TankExtra
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
interface TankRepository { interface TankRepository {
fun getAll(): Flow<List<Tank>> suspend fun getAll(): List<Tank>
fun getForUserAll(userId: Long): Flow<List<Tank>> suspend fun getForUserAll(userId: Long): List<Tank>
fun getTank(uid: Long): Flow<Tank?> suspend fun getTank(uid: Long): Tank
fun getUserTanks(uid: Long): Flow<List<TankWithNationAndLevel>> suspend fun getUserTanks(uid: Long): List<TankWithNationAndLevel>
suspend fun insertTank(tank: Tank, image: Bitmap) suspend fun insertTank(tank: Tank, image: Bitmap)
suspend fun insertMany(tankList: List<Tank>)
suspend fun updateTank(tank: Tank, image: Bitmap) suspend fun updateTank(tank: Tank, image: Bitmap)
suspend fun deleteTank(tank: Tank) suspend fun deleteTank(tank: Tank)
suspend fun delete(tankId: Long) suspend fun delete(tankId: Long)
suspend fun deleteAll()
} }

View File

@ -6,7 +6,7 @@ import ru.ulstu.`is`.pmu.tank.model.User
interface UserRepository { interface UserRepository {
fun getAllUsers(): Flow<List<User>> fun getAllUsers(): Flow<List<User>>
fun getSimpleUser(uid: Long): Flow<User?> suspend fun getSimpleUser(email: String): User
fun getFullUser(uid: Long): Flow<Map<User, List<TankWithNationAndLevel>>> fun getFullUser(uid: Long): Flow<Map<User, List<TankWithNationAndLevel>>>
suspend fun insertUser(user: User) suspend fun insertUser(user: User)
suspend fun updateUser(user: User) suspend fun updateUser(user: User)

View File

@ -1,14 +1,11 @@
package ru.ulstu.`is`.pmu.tanks.composeui package ru.ulstu.`is`.pmu.tanks.composeui
import android.content.res.Configuration import android.content.res.Configuration
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -20,20 +17,12 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -41,23 +30,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
import ru.ulstu.`is`.pmu.R
import ru.ulstu.`is`.pmu.composeui.navigation.Screen
import ru.ulstu.`is`.pmu.tank.composeui.list.TankListViewModel import ru.ulstu.`is`.pmu.tank.composeui.list.TankListViewModel
import ru.ulstu.`is`.pmu.tank.database.AppDatabase
import ru.ulstu.`is`.pmu.tank.model.Level
import ru.ulstu.`is`.pmu.tank.model.Nation
import ru.ulstu.`is`.pmu.tank.model.Tank
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
import ru.ulstu.`is`.pmu.tank.model.UserWithTanks
import ru.ulstu.`is`.pmu.tanks.composeui.image.CuteImage
import ru.ulstu.`is`.pmu.tanks.composeui.image.Dimensions import ru.ulstu.`is`.pmu.tanks.composeui.image.Dimensions
import ru.ulstu.`is`.pmu.tanks.composeui.image.RoundedCorderImage import ru.ulstu.`is`.pmu.tanks.composeui.image.RoundedCorderImage
import ru.ulstu.`is`.pmu.ui.AppViewModelProvider import ru.ulstu.`is`.pmu.ui.AppViewModelProvider
@ -71,10 +45,9 @@ fun Hangar(
viewModel: TankListViewModel = viewModel(factory = AppViewModelProvider.Factory) viewModel: TankListViewModel = viewModel(factory = AppViewModelProvider.Factory)
){ ){
viewModel.setUserId(100L) viewModel.setUserId(100L)
val userTankListUiState by viewModel.usersTanksUiState.collectAsState()
//новый вызов основного списка //новый вызов основного списка
Hangar(tankList = userTankListUiState.userTankList ) Hangar(tankList = viewModel.usersTanksUiState.userTankList )
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)

View File

@ -1,6 +1,9 @@
package com.application.ui package com.application.ui
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Base64
import java.io.ByteArrayOutputStream
import kotlin.math.sqrt import kotlin.math.sqrt
const val miniatureBound: Int = 250 const val miniatureBound: Int = 250
@ -24,3 +27,21 @@ fun Bitmap.resize(bound: Int): Bitmap {
val newHeight: Int = (height / factor).toInt() val newHeight: Int = (height / factor).toInt()
return Bitmap.createScaledBitmap(this, newWidth, newHeight, false) return Bitmap.createScaledBitmap(this, newWidth, newHeight, false)
} }
fun Bitmap.toByteArray(): ByteArray {
val outputStream = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.PNG, 1, outputStream)
return outputStream.toByteArray()
}
fun Bitmap.toBase64(): String {
return Base64.encodeToString(this.toByteArray(), Base64.DEFAULT)
}
fun ByteArray.toBitmap(): Bitmap {
return BitmapFactory.decodeByteArray(this, 0, this.size)
}
fun String.toBitmap(): Bitmap {
return Base64.decode(this, Base64.DEFAULT).toBitmap()
}