lw5
This commit is contained in:
parent
e4bc55da4e
commit
f499c56d14
@ -2,6 +2,7 @@ plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("com.google.devtools.ksp")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
}
|
||||
|
||||
android {
|
||||
@ -57,11 +58,6 @@ dependencies {
|
||||
implementation("io.github.vanpra.compose-material-dialogs:datetime:0.8.1-rc")
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
|
||||
|
||||
// Pagination
|
||||
val paging_version = "3.2.0-rc01"
|
||||
implementation("androidx.paging:paging-runtime:$paging_version")
|
||||
implementation("androidx.paging:paging-compose:$paging_version")
|
||||
|
||||
// Core
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||
@ -74,6 +70,7 @@ dependencies {
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3:1.1.2")
|
||||
implementation("androidx.compose.material:material:1.4.3")
|
||||
|
||||
// Room
|
||||
val room_version = "2.5.2"
|
||||
@ -83,6 +80,14 @@ dependencies {
|
||||
implementation("androidx.room:room-ktx:$room_version")
|
||||
implementation("androidx.room:room-paging:$room_version")
|
||||
|
||||
// retrofit
|
||||
val retrofitVersion = "2.9.0"
|
||||
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
|
||||
implementation("androidx.paging:paging-compose:3.2.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
|
||||
|
||||
// Tests
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
|
@ -2,6 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".CinemaApplication"
|
||||
android:allowBackup="true"
|
||||
@ -12,7 +14,8 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Pmudemo"
|
||||
tools:targetApi="31">
|
||||
tools:targetApi="31"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
<activity
|
||||
android:name=".MainComposeActivity"
|
||||
android:exported="true"
|
||||
|
@ -13,15 +13,12 @@ import com.example.myapplication.composeui.navigation.MainNavbar
|
||||
import com.example.myapplication.datastore.DataStoreManager
|
||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||
|
||||
//import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
|
||||
class MainComposeActivity : ComponentActivity() {
|
||||
private val dataStoreManager = DataStoreManager(this)
|
||||
private val isDarkTheme = mutableStateOf(true)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
application.deleteDatabase("pmy-db")
|
||||
//AndroidThreeTen.init(this)
|
||||
setContent {
|
||||
PmudemoTheme(darkTheme = isDarkTheme.value) {
|
||||
LaunchedEffect(key1 = true) {
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.example.myapplication.api
|
||||
|
||||
import androidx.room.TypeConverters
|
||||
import com.example.myapplication.database.entities.model.LocalDateTimeConverter
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.DateTimeUtils.toLocalDateTime
|
||||
import org.threeten.bp.format.DateTimeFormatter
|
||||
|
||||
@Serializer(forClass = LocalDateTime::class)
|
||||
object LocalDateTimeSerializer : KSerializer<LocalDateTime> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: LocalDateTime) {
|
||||
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
|
||||
encoder.encodeString(dateFormatter.format(value).toString())
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): LocalDateTime {
|
||||
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
|
||||
return LocalDateTime.parse(decoder.decodeString(), dateFormatter)
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package com.example.myapplication.api
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.api.order.OrderRemote
|
||||
import com.example.myapplication.api.session.SessionFromCinemaRemote
|
||||
import com.example.myapplication.api.session.SessionRemote
|
||||
import com.example.myapplication.api.session.SessionWithCinemaRemote
|
||||
import com.example.myapplication.api.user.UserRemote
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.contextual
|
||||
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
|
||||
|
||||
interface MyServerService {
|
||||
@GET("orders")
|
||||
suspend fun getOrders(): List<OrderRemote>
|
||||
|
||||
@GET("users")
|
||||
suspend fun getUsers(): List<UserRemote>
|
||||
|
||||
@GET("cinemas")
|
||||
suspend fun getCinemas(
|
||||
@Query("_page") page: Int,
|
||||
@Query("_limit") limit: Int,
|
||||
): List<CinemaRemote>
|
||||
|
||||
@GET("cinemas/{id}")
|
||||
suspend fun getCinema(
|
||||
@Path("id") id: Int,
|
||||
): CinemaRemote
|
||||
|
||||
@POST("cinemas")
|
||||
suspend fun createCinema(
|
||||
@Body cinema: CinemaRemote,
|
||||
): CinemaRemote
|
||||
|
||||
@PUT("cinemas/{id}")
|
||||
suspend fun updateCinema(
|
||||
@Path("id") id: Int,
|
||||
@Body cinema: CinemaRemote,
|
||||
): CinemaRemote
|
||||
|
||||
@DELETE("cinemas/{id}")
|
||||
suspend fun deleteCinema(
|
||||
@Path("id") id: Int,
|
||||
)
|
||||
|
||||
@GET("cinemas/{cinemaId}/sessions")
|
||||
suspend fun getSessionsForCinema(
|
||||
@Path("cinemaId") cinemaId: Int
|
||||
): List<SessionFromCinemaRemote>
|
||||
|
||||
@GET("sessions/{id}?_expand=cinema")
|
||||
suspend fun getSession(
|
||||
@Path("id") id: Int,
|
||||
): SessionWithCinemaRemote
|
||||
|
||||
@POST("sessions")
|
||||
suspend fun createSession(
|
||||
@Body session: SessionRemote,
|
||||
): SessionRemote
|
||||
|
||||
@PUT("sessions/{id}")
|
||||
suspend fun updateSession(
|
||||
@Path("id") id: Int,
|
||||
@Body session: SessionRemote,
|
||||
): SessionRemote
|
||||
|
||||
@DELETE("sessions/{id}")
|
||||
suspend fun deleteSession(
|
||||
@Path("id") id: Int,
|
||||
): SessionFromCinemaRemote
|
||||
|
||||
@GET("users/{id}")
|
||||
suspend fun getUserCart(
|
||||
@Path("id") id: Int,
|
||||
): UserRemote
|
||||
|
||||
@PUT("users/{id}")
|
||||
suspend fun updateUserCart(
|
||||
@Path("id") id: Int,
|
||||
@Body userRemote: UserRemote,
|
||||
): UserRemote
|
||||
|
||||
@GET("orders")
|
||||
suspend fun getOrders(
|
||||
@Query("_page") page: Int,
|
||||
@Query("_limit") limit: Int,
|
||||
): List<OrderRemote>
|
||||
|
||||
@GET("orders/{id}")
|
||||
suspend fun getOrder(
|
||||
@Path("id") id: Int,
|
||||
): OrderRemote
|
||||
|
||||
@POST("orders")
|
||||
suspend fun createOrder(
|
||||
@Body cinema: OrderRemote,
|
||||
): OrderRemote
|
||||
|
||||
@PUT("orders/{id}")
|
||||
suspend fun updateOrder(
|
||||
@Path("id") id: Int,
|
||||
@Body orderRemote: OrderRemote,
|
||||
): OrderRemote
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL = "http://192.168.154.166:8079/"
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: MyServerService? = null
|
||||
|
||||
fun getInstance(): MyServerService {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
val logger = HttpLoggingInterceptor()
|
||||
logger.level = HttpLoggingInterceptor.Level.BASIC
|
||||
val client = OkHttpClient.Builder().addInterceptor(logger).build()
|
||||
val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
serializersModule = SerializersModule {
|
||||
contextual(LocalDateTimeSerializer)
|
||||
}
|
||||
} // Создаем экземпляр Json с ignoreUnknownKeys = true
|
||||
return Retrofit.Builder().baseUrl(BASE_URL).client(client)
|
||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType())) // Применяем конфигурацию Json
|
||||
.build().create(MyServerService::class.java).also { INSTANCE = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.example.myapplication.api.cinema
|
||||
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CinemaRemote(
|
||||
val id: Int = 0,
|
||||
val name: String = "",
|
||||
val description: String = "",
|
||||
val image: ByteArray? = null,
|
||||
val year: Long = 0
|
||||
)
|
||||
|
||||
fun CinemaRemote.toCinema(): Cinema = Cinema(
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
image,
|
||||
year
|
||||
)
|
||||
|
||||
fun Cinema.toCinemaRemote(): CinemaRemote = CinemaRemote(
|
||||
uid,
|
||||
name,
|
||||
description,
|
||||
image,
|
||||
year
|
||||
)
|
@ -0,0 +1,121 @@
|
||||
package com.example.myapplication.api.cinema
|
||||
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.LoadType
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.RemoteMediator
|
||||
import androidx.room.withTransaction
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.session.toSession
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.repository.OfflineCinemaRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineSessionRepository
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeys
|
||||
import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
class CinemaRemoteMediator(
|
||||
private val service: MyServerService,
|
||||
private val dbCinemaRepository: OfflineCinemaRepository,
|
||||
private val dbSessionRepository: OfflineSessionRepository,
|
||||
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||
private val database: AppDatabase
|
||||
) : RemoteMediator<Int, Cinema>() {
|
||||
|
||||
override suspend fun initialize(): InitializeAction {
|
||||
return InitializeAction.LAUNCH_INITIAL_REFRESH
|
||||
}
|
||||
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<Int, Cinema>
|
||||
): 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 cinemas = service.getCinemas(page, state.config.pageSize).map { it.toCinema() }
|
||||
val cinemasWithSessions = cinemas.map { cinema ->
|
||||
service.getSessionsForCinema(cinema.uid).map {
|
||||
service.getSession(it.id).toSession()
|
||||
}
|
||||
}
|
||||
val endOfPaginationReached = cinemas.isEmpty()
|
||||
database.withTransaction {
|
||||
if (loadType == LoadType.REFRESH) {
|
||||
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.CINEMA)
|
||||
dbSessionRepository.clearSessions()
|
||||
dbCinemaRepository.clearCinemas()
|
||||
}
|
||||
val prevKey = if (page == 1) null else page - 1
|
||||
val nextKey = if (endOfPaginationReached) null else page + 1
|
||||
val keys = cinemas.map {
|
||||
RemoteKeys(
|
||||
entityId = it.uid,
|
||||
type = RemoteKeyType.CINEMA,
|
||||
prevKey = prevKey,
|
||||
nextKey = nextKey
|
||||
)
|
||||
}
|
||||
dbRemoteKeyRepository.createRemoteKeys(keys)
|
||||
dbCinemaRepository.insertCinemas(cinemas)
|
||||
cinemasWithSessions.forEach {
|
||||
try {
|
||||
dbSessionRepository.insertSessions(it)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
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, Cinema>): RemoteKeys? {
|
||||
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
||||
?.let { cinema ->
|
||||
dbRemoteKeyRepository.getAllRemoteKeys(cinema.uid, RemoteKeyType.CINEMA)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Cinema>): RemoteKeys? {
|
||||
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
||||
?.let { cinema ->
|
||||
dbRemoteKeyRepository.getAllRemoteKeys(cinema.uid, RemoteKeyType.CINEMA)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getRemoteKeyClosestToCurrentPosition(
|
||||
state: PagingState<Int, Cinema>
|
||||
): RemoteKeys? {
|
||||
return state.anchorPosition?.let { position ->
|
||||
state.closestItemToPosition(position)?.uid?.let { cinemaUid ->
|
||||
dbRemoteKeyRepository.getAllRemoteKeys(cinemaUid, RemoteKeyType.CINEMA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package com.example.myapplication.api.cinema
|
||||
|
||||
import android.util.Log
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.database.AppContainer
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||
import com.example.myapplication.database.entities.model.SessionFromCinema
|
||||
import com.example.myapplication.database.entities.repository.CinemaRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineCinemaRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineSessionRepository
|
||||
import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class RestCinemaRepository(
|
||||
private val service: MyServerService,
|
||||
private val dbCinemaRepository: OfflineCinemaRepository,
|
||||
private val dbSessionRepository: OfflineSessionRepository,
|
||||
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||
private val database: AppDatabase
|
||||
) : CinemaRepository {
|
||||
override fun getAllCinemas(): Flow<PagingData<Cinema>> {
|
||||
Log.d(RestCinemaRepository::class.simpleName, "Get cinemas")
|
||||
|
||||
val pagingSourceFactory = { dbCinemaRepository.getAllCinemasPagingSource() }
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
return Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = AppContainer.LIMIT,
|
||||
enablePlaceholders = false
|
||||
),
|
||||
remoteMediator = CinemaRemoteMediator(
|
||||
service,
|
||||
dbCinemaRepository,
|
||||
dbSessionRepository,
|
||||
dbRemoteKeyRepository,
|
||||
database,
|
||||
),
|
||||
pagingSourceFactory = pagingSourceFactory
|
||||
).flow
|
||||
}
|
||||
|
||||
override suspend fun getCinema(uid: Int): CinemaWithSessions {
|
||||
val cinema = service.getCinema(uid).toCinema()
|
||||
|
||||
val sessions = service.getSessionsForCinema(uid).map { x ->
|
||||
SessionFromCinema(
|
||||
x.id,
|
||||
x.dateTime,
|
||||
x.price,
|
||||
x.maxCount - service.getOrders().flatMap { order ->
|
||||
order.sessions.filter { session -> session.id == x.id }
|
||||
}.sumOf { session -> session.count },
|
||||
uid
|
||||
)
|
||||
}
|
||||
return CinemaWithSessions(cinema, sessions)
|
||||
}
|
||||
|
||||
override suspend fun insertCinema(cinema: Cinema) {
|
||||
service.createCinema(cinema.toCinemaRemote()).toCinema()
|
||||
}
|
||||
|
||||
override suspend fun updateCinema(cinema: Cinema) {
|
||||
service.updateCinema(cinema.uid, cinema.toCinemaRemote()).toCinema()
|
||||
}
|
||||
|
||||
override suspend fun deleteCinema(cinema: Cinema) {
|
||||
val cart = service.getUsers()
|
||||
cart.forEach { userRemote ->
|
||||
userRemote.sessions = userRemote.sessions.filter { x -> x.cinemaId != cinema.uid }
|
||||
service.updateUserCart(userRemote.id, userRemote)
|
||||
}
|
||||
val orders = service.getOrders()
|
||||
orders.forEach { orderRemote ->
|
||||
orderRemote.sessions = orderRemote.sessions.filter { x -> x.cinemaId != cinema.uid }
|
||||
service.updateOrder(orderRemote.id, orderRemote)
|
||||
}
|
||||
service.deleteCinema(cinema.uid)
|
||||
dbCinemaRepository.deleteCinema(cinema)
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.example.myapplication.api.order
|
||||
|
||||
import com.example.myapplication.api.session.SessionFromOrderRemote
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OrderRemote(
|
||||
val id: Int = 0, val userId: Int = 0, var sessions: List<SessionFromOrderRemote> = emptyList()
|
||||
)
|
||||
|
||||
fun OrderRemote.toOrder(): Order = Order(
|
||||
id, userId
|
||||
)
|
||||
|
||||
fun Order.toOrderRemote(): OrderRemote = OrderRemote(
|
||||
uid, userId!!, sessions = emptyList()
|
||||
)
|
@ -0,0 +1,106 @@
|
||||
package com.example.myapplication.api.order
|
||||
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.LoadType
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.RemoteMediator
|
||||
import androidx.room.withTransaction
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.repository.OfflineOrderRepository
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeys
|
||||
import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
class OrderRemoteMediator(
|
||||
private val service: MyServerService,
|
||||
private val dbOrderRepository: OfflineOrderRepository,
|
||||
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||
private val database: AppDatabase
|
||||
) : RemoteMediator<Int, Order>() {
|
||||
|
||||
override suspend fun initialize(): InitializeAction {
|
||||
return InitializeAction.LAUNCH_INITIAL_REFRESH
|
||||
}
|
||||
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<Int, Order>
|
||||
): 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 orders = service.getOrders(page, state.config.pageSize).map { it.toOrder() }
|
||||
val endOfPaginationReached = orders.isEmpty()
|
||||
database.withTransaction {
|
||||
if (loadType == LoadType.REFRESH) {
|
||||
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.ORDER)
|
||||
dbOrderRepository.clearOrders()
|
||||
}
|
||||
val prevKey = if (page == 1) null else page - 1
|
||||
val nextKey = if (endOfPaginationReached) null else page + 1
|
||||
val keys = orders.map {
|
||||
RemoteKeys(
|
||||
entityId = it.uid,
|
||||
type = RemoteKeyType.ORDER,
|
||||
prevKey = prevKey,
|
||||
nextKey = nextKey
|
||||
)
|
||||
}
|
||||
dbRemoteKeyRepository.createRemoteKeys(keys)
|
||||
dbOrderRepository.insertOrders(orders)
|
||||
}
|
||||
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, Order>): RemoteKeys? {
|
||||
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
||||
?.let { order ->
|
||||
dbRemoteKeyRepository.getAllRemoteKeys(order.uid, RemoteKeyType.ORDER)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Order>): RemoteKeys? {
|
||||
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
||||
?.let { order ->
|
||||
dbRemoteKeyRepository.getAllRemoteKeys(order.uid, RemoteKeyType.ORDER)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getRemoteKeyClosestToCurrentPosition(
|
||||
state: PagingState<Int, Order>
|
||||
): RemoteKeys? {
|
||||
return state.anchorPosition?.let { position ->
|
||||
state.closestItemToPosition(position)?.uid?.let { orderUid ->
|
||||
dbRemoteKeyRepository.getAllRemoteKeys(orderUid, RemoteKeyType.ORDER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.example.myapplication.api.order
|
||||
|
||||
import android.util.Log
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.cinema.toCinemaRemote
|
||||
import com.example.myapplication.api.session.toSessionFromOrder
|
||||
import com.example.myapplication.database.AppContainer
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||
import com.example.myapplication.database.entities.repository.OfflineCinemaRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineOrderRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineOrderSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||
import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class RestOrderRepository(
|
||||
private val service: MyServerService,
|
||||
private val dbOrderRepository: OfflineOrderRepository,
|
||||
private val dbCinemaRepository: OfflineCinemaRepository,
|
||||
private val dbOrderSessionRepository: OfflineOrderSessionRepository,
|
||||
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||
private val database: AppDatabase
|
||||
) : OrderRepository {
|
||||
override fun getAllOrders(userId: Int?): Flow<PagingData<Order>> {
|
||||
Log.d(RestOrderRepository::class.simpleName, "Get orders")
|
||||
|
||||
val pagingSourceFactory = { dbOrderRepository.getAllOrdersPagingSource(userId) }
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
return Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = AppContainer.LIMIT,
|
||||
enablePlaceholders = false
|
||||
),
|
||||
remoteMediator = OrderRemoteMediator(
|
||||
service,
|
||||
dbOrderRepository,
|
||||
dbRemoteKeyRepository,
|
||||
database,
|
||||
),
|
||||
pagingSourceFactory = pagingSourceFactory
|
||||
).flow
|
||||
}
|
||||
|
||||
override suspend fun getOrder(uid: Int): List<SessionFromOrder> {
|
||||
val order = service.getOrder(uid)
|
||||
|
||||
dbOrderSessionRepository.deleteOrderSessions(uid)
|
||||
order.sessions.map {
|
||||
dbOrderSessionRepository.insertOrderSession(
|
||||
OrderSessionCrossRef(
|
||||
uid,
|
||||
it.id,
|
||||
it.frozenPrice,
|
||||
it.count
|
||||
)
|
||||
)
|
||||
}
|
||||
return order.sessions.map { x -> x.toSessionFromOrder(dbCinemaRepository.getCinema(x.cinemaId).cinema.toCinemaRemote()) }
|
||||
}
|
||||
|
||||
override suspend fun insertOrder(order: Order): Long {
|
||||
return dbOrderRepository.insertOrder(service.createOrder(order.toOrderRemote()).toOrder())
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.example.myapplication.api.ordersession
|
||||
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.session.SessionFromOrderRemote
|
||||
import com.example.myapplication.api.session.toSession
|
||||
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
||||
import com.example.myapplication.database.entities.repository.OfflineOrderSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OrderSessionRepository
|
||||
|
||||
class RestOrderSessionRepository(
|
||||
private val service: MyServerService,
|
||||
private val dbOrderSessionRepository: OfflineOrderSessionRepository
|
||||
) : OrderSessionRepository {
|
||||
override suspend fun insertOrderSession(orderSessionCrossRef: OrderSessionCrossRef) {
|
||||
var orderRemote = service.getOrder(orderSessionCrossRef.orderId)
|
||||
val session = service.getSession(orderSessionCrossRef.sessionId).toSession()
|
||||
|
||||
val sessionFromOrder = SessionFromOrderRemote(
|
||||
session.uid,
|
||||
session.dateTime,
|
||||
session.price,
|
||||
orderSessionCrossRef.count,
|
||||
session.cinemaId
|
||||
)
|
||||
|
||||
val updatedSessions = orderRemote.sessions.toMutableList()
|
||||
updatedSessions.add(sessionFromOrder)
|
||||
|
||||
orderRemote = orderRemote.copy(sessions = updatedSessions)
|
||||
service.updateOrder(orderSessionCrossRef.orderId, orderRemote)
|
||||
dbOrderSessionRepository.insertOrderSession(orderSessionCrossRef)
|
||||
}
|
||||
|
||||
override suspend fun updateOrderSession(orderSessionCrossRef: OrderSessionCrossRef) {
|
||||
}
|
||||
|
||||
override suspend fun deleteOrderSession(orderSessionCrossRef: OrderSessionCrossRef) {
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.api.cinema.toCinema
|
||||
import com.example.myapplication.api.session.SessionFromCinemaRemote
|
||||
import com.example.myapplication.api.session.toSessionFromCinema
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/*
|
||||
@Serializable
|
||||
data class CinemaWithSessionsRemote(
|
||||
val id: Int = 0,
|
||||
val name: String = "",
|
||||
val description: String = "",
|
||||
val image: ByteArray? = null,
|
||||
val year: Long = 0,
|
||||
@SerialName("sessions")
|
||||
val sessions: List<SessionFromCinemaRemote>,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CinemaWithSessionsRemote
|
||||
|
||||
if (id != other.id) return false
|
||||
if (name != other.name) return false
|
||||
if (description != other.description) return false
|
||||
if (image != null) {
|
||||
if (other.image == null) return false
|
||||
if (!image.contentEquals(other.image)) return false
|
||||
} else if (other.image != null) return false
|
||||
if (year != other.year) return false
|
||||
if (sessions != other.sessions) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + description.hashCode()
|
||||
result = 31 * result + (image?.contentHashCode() ?: 0)
|
||||
result = 31 * result + year.hashCode()
|
||||
result = 31 * result + sessions.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun CinemaWithSessionsRemote.toCinemaWithSessions(): CinemaWithSessions = CinemaWithSessions(
|
||||
Cinema(
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
image,
|
||||
year
|
||||
),
|
||||
sessions.map { x -> x.toSessionFromCinema() }
|
||||
)
|
||||
|
||||
fun Cinema.toCinemaWithSessionsRemote(): CinemaWithSessionsRemote = CinemaWithSessionsRemote(
|
||||
uid,
|
||||
name,
|
||||
description,
|
||||
image,
|
||||
year,
|
||||
sessions = emptyList()
|
||||
)*/
|
@ -0,0 +1,51 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import com.example.myapplication.database.entities.repository.OfflineOrderSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.SessionRepository
|
||||
|
||||
class RestSessionRepository(
|
||||
private val service: MyServerService,
|
||||
private val dbSessionRepository: OfflineSessionRepository,
|
||||
private val dbUserSessionRepository: OfflineUserSessionRepository,
|
||||
private val dbOrderSessionRepository: OfflineOrderSessionRepository,
|
||||
) : SessionRepository {
|
||||
override suspend fun getSession(uid: Int): Session {
|
||||
return service.getSession(uid).toSession()
|
||||
}
|
||||
|
||||
override suspend fun insertSession(session: Session) {
|
||||
dbSessionRepository.insertSession(
|
||||
service.createSession(session.toSessionRemote()).toSession()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun updateSession(session: Session) {
|
||||
dbSessionRepository.updateSession(
|
||||
service.updateSession(
|
||||
session.uid,
|
||||
session.toSessionRemote()
|
||||
).toSession()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun deleteSession(session: Session) {
|
||||
val cart = service.getUsers()
|
||||
cart.forEach { userRemote ->
|
||||
userRemote.sessions = userRemote.sessions.filter { x -> x.id != session.uid }
|
||||
service.updateUserCart(userRemote.id, userRemote)
|
||||
}
|
||||
val orders = service.getOrders()
|
||||
orders.forEach { orderRemote ->
|
||||
orderRemote.sessions = orderRemote.sessions.filter { x -> x.id != session.uid }
|
||||
service.updateOrder(orderRemote.id, orderRemote)
|
||||
}
|
||||
service.deleteSession(session.uid)
|
||||
dbUserSessionRepository.deleteSessionsByUid(session.uid)
|
||||
dbOrderSessionRepository.deleteSessionsByUid(session.uid)
|
||||
dbSessionRepository.deleteSession(session)
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.api.cinema.toCinema
|
||||
import com.example.myapplication.database.entities.model.SessionFromCart
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
@Serializable
|
||||
class SessionFromCartRemote(
|
||||
val id: Int = 0,
|
||||
var count: Int = 0,
|
||||
var cinemaId: Int = 0,
|
||||
)
|
||||
|
||||
fun SessionFromCartRemote.toSessionFromCart(cinema: CinemaRemote, dateTime: LocalDateTime, price: Double, availableCount: Int): SessionFromCart =
|
||||
SessionFromCart(
|
||||
id, dateTime, price, availableCount, count, cinema.id, cinema.toCinema()
|
||||
)
|
@ -0,0 +1,34 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import com.example.myapplication.database.entities.model.SessionFromCinema
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
@Serializable
|
||||
class SessionFromCinemaRemote(
|
||||
val id: Int = 0,
|
||||
@Contextual
|
||||
val dateTime: LocalDateTime = LocalDateTime.MIN,
|
||||
val price: Double = 0.0,
|
||||
val maxCount: Int = 0,
|
||||
val availableCount: Int = 0,
|
||||
val cinemaId: Int = 0,
|
||||
)
|
||||
|
||||
fun SessionFromCinemaRemote.toSessionFromCinema(): SessionFromCinema = SessionFromCinema(
|
||||
id,
|
||||
dateTime,
|
||||
price,
|
||||
availableCount,
|
||||
cinemaId
|
||||
)
|
||||
|
||||
fun SessionFromCinema.toSessionFromCinemaRemote(): SessionFromCinemaRemote = SessionFromCinemaRemote(
|
||||
uid,
|
||||
dateTime,
|
||||
price,
|
||||
availableCount,
|
||||
cinemaId
|
||||
)
|
@ -0,0 +1,22 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.api.cinema.toCinema
|
||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
@Serializable
|
||||
class SessionFromOrderRemote(
|
||||
val id: Int = 0,
|
||||
@Contextual val dateTime: LocalDateTime = LocalDateTime.MIN,
|
||||
val frozenPrice: Double = 0.0,
|
||||
val count: Int = 0,
|
||||
val cinemaId: Int = 0,
|
||||
)
|
||||
|
||||
fun SessionFromOrderRemote.toSessionFromOrder(cinema: CinemaRemote): SessionFromOrder =
|
||||
SessionFromOrder(
|
||||
id, dateTime, frozenPrice, count, cinemaId, cinema.toCinema()
|
||||
)
|
@ -0,0 +1,34 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.api.cinema.toCinema
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
@Serializable
|
||||
data class SessionRemote(
|
||||
val id: Int = 0,
|
||||
@Contextual
|
||||
val dateTime: LocalDateTime,
|
||||
val price: Double,
|
||||
val maxCount: Int,
|
||||
val cinemaId: Int = 0
|
||||
)
|
||||
|
||||
fun SessionRemote.toSession(): Session = Session(
|
||||
id,
|
||||
dateTime,
|
||||
price,
|
||||
maxCount,
|
||||
cinemaId
|
||||
)
|
||||
|
||||
fun Session.toSessionRemote(): SessionRemote = SessionRemote(
|
||||
uid,
|
||||
dateTime,
|
||||
price,
|
||||
maxCount,
|
||||
cinemaId
|
||||
)
|
@ -0,0 +1,26 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
@Serializable
|
||||
data class SessionWithCinemaRemote(
|
||||
val id: Int = 0,
|
||||
@Contextual
|
||||
val dateTime: LocalDateTime,
|
||||
val price: Double,
|
||||
val maxCount: Int,
|
||||
val cinemaId: Int = 0,
|
||||
val cinema: CinemaRemote,
|
||||
)
|
||||
|
||||
fun SessionWithCinemaRemote.toSession(): Session = Session(
|
||||
id,
|
||||
dateTime,
|
||||
price,
|
||||
maxCount,
|
||||
cinemaId
|
||||
)
|
@ -0,0 +1,57 @@
|
||||
package com.example.myapplication.api.user
|
||||
|
||||
import android.util.Log
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.session.toSessionFromCart
|
||||
import com.example.myapplication.database.entities.model.SessionFromCart
|
||||
import com.example.myapplication.database.entities.model.User
|
||||
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.UserRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class RestUserRepository(
|
||||
private val service: MyServerService,
|
||||
private val dbUserRepository: OfflineUserRepository,
|
||||
private val dbUserSessionRepository: OfflineUserSessionRepository,
|
||||
) : UserRepository {
|
||||
override fun getAllUsers(): Flow<List<User>> {
|
||||
Log.d(RestUserRepository::class.simpleName, "Get users")
|
||||
return dbUserRepository.getAllUsers()
|
||||
}
|
||||
|
||||
override suspend fun getCartByUser(userId: Int): List<SessionFromCart> {
|
||||
val cart = service.getUserCart(userId)
|
||||
dbUserSessionRepository.deleteUserSessions(userId)
|
||||
cart.sessions.map { sessionFromCartRemote ->
|
||||
dbUserSessionRepository.insertUserSession(
|
||||
UserSessionCrossRef(
|
||||
userId,
|
||||
sessionFromCartRemote.id,
|
||||
sessionFromCartRemote.count
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return cart.sessions.map {
|
||||
val session = service.getSession(it.id)
|
||||
it.toSessionFromCart(
|
||||
session.cinema,
|
||||
session.dateTime,
|
||||
session.price,
|
||||
session.maxCount - service.getOrders().flatMap { order ->
|
||||
order.sessions.filter { session -> session.id == it.id }
|
||||
}.sumOf { session -> session.count })
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insertUser(user: User) {
|
||||
}
|
||||
|
||||
override suspend fun updateUser(user: User) {
|
||||
}
|
||||
|
||||
override suspend fun deleteUser(user: User) {
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.example.myapplication.api.user
|
||||
|
||||
import com.example.myapplication.api.session.SessionFromCartRemote
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UserRemote(
|
||||
val id: Int = 0,
|
||||
val login: String = "",
|
||||
val password: String = "",
|
||||
var sessions: List<SessionFromCartRemote> = emptyList()
|
||||
)
|
@ -0,0 +1,62 @@
|
||||
package com.example.myapplication.api.usersession
|
||||
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.session.SessionFromCartRemote
|
||||
import com.example.myapplication.api.session.toSession
|
||||
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||
|
||||
class RestUserSessionRepository(
|
||||
private val service: MyServerService,
|
||||
private val dbUserSessionRepository: OfflineUserSessionRepository
|
||||
) : UserSessionRepository {
|
||||
override suspend fun insertUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
||||
var cartSessions = service.getUserCart(userSessionCrossRef.userId)
|
||||
cartSessions.sessions.forEach { session ->
|
||||
if (session.id == userSessionCrossRef.sessionId)
|
||||
return
|
||||
}
|
||||
val session = service.getSession(userSessionCrossRef.sessionId).toSession()
|
||||
|
||||
val sessionFromCart = SessionFromCartRemote(
|
||||
session.uid,
|
||||
userSessionCrossRef.count,
|
||||
session.cinemaId,
|
||||
)
|
||||
|
||||
val updatedSessions = cartSessions.sessions.toMutableList()
|
||||
updatedSessions.add(sessionFromCart)
|
||||
|
||||
cartSessions = cartSessions.copy(sessions = updatedSessions)
|
||||
service.updateUserCart(userSessionCrossRef.userId, cartSessions)
|
||||
dbUserSessionRepository.insertUserSession(userSessionCrossRef)
|
||||
}
|
||||
|
||||
override suspend fun updateUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
||||
val userRemote = service.getUserCart(userSessionCrossRef.userId)
|
||||
if (userSessionCrossRef.count <= 0) {
|
||||
userRemote.sessions =
|
||||
userRemote.sessions.filter { x -> x.id != userSessionCrossRef.sessionId }
|
||||
} else
|
||||
userRemote.sessions.forEach {
|
||||
if (it.id == userSessionCrossRef.sessionId) {
|
||||
it.count = userSessionCrossRef.count
|
||||
}
|
||||
}
|
||||
service.updateUserCart(userSessionCrossRef.userId, userRemote)
|
||||
dbUserSessionRepository.updateUserSession(userSessionCrossRef)
|
||||
}
|
||||
|
||||
override suspend fun deleteUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
||||
updateUserSession(userSessionCrossRef)
|
||||
dbUserSessionRepository.deleteUserSession(userSessionCrossRef)
|
||||
}
|
||||
|
||||
override suspend fun deleteUserSessions(userId: Int) {
|
||||
val userRemote = service.getUserCart(userId)
|
||||
userRemote.sessions = emptyList()
|
||||
service.updateUserCart(userId, userRemote)
|
||||
dbUserSessionRepository.deleteUserSessions(userId)
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ import androidx.compose.material3.SwipeToDismiss
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberDismissState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@ -58,6 +58,7 @@ import com.example.myapplication.database.entities.composeui.CartViewModel
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import com.example.myapplication.database.entities.model.SessionFromCart
|
||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.threeten.bp.format.DateTimeFormatter
|
||||
|
||||
@ -66,7 +67,11 @@ fun Cart(
|
||||
viewModel: CartViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val cartUiState by viewModel.cartUiState.collectAsState()
|
||||
val cartUiState = viewModel.cartUiState
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.refreshState()
|
||||
}
|
||||
|
||||
Cart(
|
||||
cartUiState = cartUiState,
|
||||
@ -276,7 +281,13 @@ private fun SessionListItem(
|
||||
)
|
||||
|
||||
IconButton(
|
||||
onClick = { onChangeCount(session, 1, ++currentCount) }
|
||||
onClick = {
|
||||
onChangeCount(
|
||||
session,
|
||||
1,
|
||||
if (currentCount != session.availableCount) ++currentCount else currentCount
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
|
@ -1,49 +1,101 @@
|
||||
package com.example.myapplication.database
|
||||
|
||||
import android.content.Context
|
||||
import com.example.myapplication.database.entities.repository.CinemaRepository
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.cinema.RestCinemaRepository
|
||||
import com.example.myapplication.api.order.RestOrderRepository
|
||||
import com.example.myapplication.api.ordersession.RestOrderSessionRepository
|
||||
import com.example.myapplication.api.session.RestSessionRepository
|
||||
import com.example.myapplication.api.user.RestUserRepository
|
||||
import com.example.myapplication.api.usersession.RestUserSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineCinemaRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineOrderRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineOrderSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||
import com.example.myapplication.database.entities.repository.OrderSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.SessionRepository
|
||||
import com.example.myapplication.database.entities.repository.UserRepository
|
||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||
import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
|
||||
|
||||
interface AppContainer {
|
||||
val cinemaRepository: CinemaRepository
|
||||
val orderRepository: OrderRepository
|
||||
val orderSessionRepository: OrderSessionRepository
|
||||
val sessionRepository: SessionRepository
|
||||
val userRepository: UserRepository
|
||||
val userSessionRepository: UserSessionRepository
|
||||
}
|
||||
|
||||
class AppDataContainer(private val context: Context) : AppContainer {
|
||||
override val cinemaRepository: CinemaRepository by lazy {
|
||||
OfflineCinemaRepository(AppDatabase.getInstance(context).cinemaDao())
|
||||
}
|
||||
override val orderRepository: OrderRepository by lazy {
|
||||
OfflineOrderRepository(AppDatabase.getInstance(context).orderDao())
|
||||
}
|
||||
override val orderSessionRepository: OrderSessionRepository by lazy {
|
||||
OfflineOrderSessionRepository(AppDatabase.getInstance(context).orderSessionCrossRefDao())
|
||||
}
|
||||
override val sessionRepository: SessionRepository by lazy {
|
||||
OfflineSessionRepository(AppDatabase.getInstance(context).sessionDao())
|
||||
}
|
||||
override val userRepository: UserRepository by lazy {
|
||||
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
||||
}
|
||||
override val userSessionRepository: UserSessionRepository by lazy {
|
||||
OfflineUserSessionRepository(AppDatabase.getInstance(context).userSessionCrossRefDao())
|
||||
}
|
||||
val cinemaRestRepository: RestCinemaRepository
|
||||
val sessionRestRepository: RestSessionRepository
|
||||
val userRestRepository: RestUserRepository
|
||||
val orderRestRepository: RestOrderRepository
|
||||
val orderSessionRestRepository: RestOrderSessionRepository
|
||||
val userSessionRestRepository: RestUserSessionRepository
|
||||
|
||||
companion object {
|
||||
const val TIMEOUT = 5000L
|
||||
const val LIMIT = 10
|
||||
}
|
||||
}
|
||||
|
||||
class AppDataContainer(private val context: Context) : AppContainer {
|
||||
private val cinemaRepository: OfflineCinemaRepository by lazy {
|
||||
OfflineCinemaRepository(AppDatabase.getInstance(context).cinemaDao())
|
||||
}
|
||||
private val orderRepository: OfflineOrderRepository by lazy {
|
||||
OfflineOrderRepository(AppDatabase.getInstance(context).orderDao())
|
||||
}
|
||||
private val orderSessionRepository: OfflineOrderSessionRepository by lazy {
|
||||
OfflineOrderSessionRepository(AppDatabase.getInstance(context).orderSessionCrossRefDao())
|
||||
}
|
||||
private val sessionRepository: OfflineSessionRepository by lazy {
|
||||
OfflineSessionRepository(AppDatabase.getInstance(context).sessionDao())
|
||||
}
|
||||
private val userRepository: OfflineUserRepository by lazy {
|
||||
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
||||
}
|
||||
private val userSessionRepository: OfflineUserSessionRepository by lazy {
|
||||
OfflineUserSessionRepository(AppDatabase.getInstance(context).userSessionCrossRefDao())
|
||||
}
|
||||
private val remoteKeyRepository: OfflineRemoteKeyRepository by lazy {
|
||||
OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao())
|
||||
}
|
||||
override val cinemaRestRepository: RestCinemaRepository by lazy {
|
||||
RestCinemaRepository(
|
||||
MyServerService.getInstance(),
|
||||
cinemaRepository,
|
||||
sessionRepository,
|
||||
remoteKeyRepository,
|
||||
AppDatabase.getInstance(context)
|
||||
)
|
||||
}
|
||||
override val sessionRestRepository: RestSessionRepository by lazy {
|
||||
RestSessionRepository(
|
||||
MyServerService.getInstance(),
|
||||
sessionRepository,
|
||||
userSessionRepository,
|
||||
orderSessionRepository,
|
||||
)
|
||||
}
|
||||
override val userRestRepository: RestUserRepository by lazy {
|
||||
RestUserRepository(
|
||||
MyServerService.getInstance(),
|
||||
userRepository,
|
||||
userSessionRepository,
|
||||
)
|
||||
}
|
||||
override val orderRestRepository: RestOrderRepository by lazy {
|
||||
RestOrderRepository(
|
||||
MyServerService.getInstance(),
|
||||
orderRepository,
|
||||
cinemaRepository,
|
||||
orderSessionRepository,
|
||||
remoteKeyRepository,
|
||||
AppDatabase.getInstance(context)
|
||||
)
|
||||
}
|
||||
override val userSessionRestRepository: RestUserSessionRepository by lazy {
|
||||
RestUserSessionRepository(
|
||||
MyServerService.getInstance(),
|
||||
userSessionRepository,
|
||||
)
|
||||
}
|
||||
override val orderSessionRestRepository: RestOrderSessionRepository by lazy {
|
||||
RestOrderSessionRepository(
|
||||
MyServerService.getInstance(),
|
||||
orderSessionRepository,
|
||||
)
|
||||
}
|
||||
}
|
@ -20,6 +20,8 @@ import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import com.example.myapplication.database.entities.model.User
|
||||
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
||||
import com.example.myapplication.database.remotekeys.dao.RemoteKeysDao
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeys
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@ -27,8 +29,15 @@ import org.threeten.bp.LocalDateTime
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
@Database(
|
||||
entities = [Cinema::class, Session::class, Order::class,
|
||||
OrderSessionCrossRef::class, User::class, UserSessionCrossRef::class],
|
||||
entities = [
|
||||
Cinema::class,
|
||||
Session::class,
|
||||
Order::class,
|
||||
OrderSessionCrossRef::class,
|
||||
User::class,
|
||||
UserSessionCrossRef::class,
|
||||
RemoteKeys::class
|
||||
],
|
||||
version = 1,
|
||||
exportSchema = false
|
||||
)
|
||||
@ -40,6 +49,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun orderSessionCrossRefDao(): OrderSessionCrossRefDao
|
||||
abstract fun userDao(): UserDao
|
||||
abstract fun userSessionCrossRefDao(): UserSessionCrossRefDao
|
||||
abstract fun remoteKeysDao(): RemoteKeysDao
|
||||
|
||||
companion object {
|
||||
private const val DB_NAME: String = "pmy-db"
|
||||
@ -51,11 +61,9 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
INSTANCE?.let { database ->
|
||||
// Users
|
||||
val userDao = database.userDao()
|
||||
val user1 = User(1, "Login", "password")
|
||||
val user2 = User(2, "Login123", "password123")
|
||||
val user1 = User(1, "login", "password")
|
||||
userDao.insert(user1)
|
||||
userDao.insert(user2)
|
||||
// Cinemas
|
||||
/*// Cinemas
|
||||
val cinemaDao = database.cinemaDao()
|
||||
val cinema1 =
|
||||
Cinema(1, "a", "Desc1", createColoredImage(android.graphics.Color.BLUE), 2023)
|
||||
@ -128,7 +136,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
val userSessionCrossRef1 = UserSessionCrossRef(1, 1, 5)
|
||||
val userSessionCrossRef2 = UserSessionCrossRef(1, 3, 15)
|
||||
userSessionCrossRefDao.insert(userSessionCrossRef1)
|
||||
userSessionCrossRefDao.insert(userSessionCrossRef2)
|
||||
userSessionCrossRefDao.insert(userSessionCrossRef2)*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,15 +173,15 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
return stream.toByteArray()
|
||||
}
|
||||
|
||||
fun getRandomColorInt(): Int {
|
||||
private fun getRandomColorInt(): Int {
|
||||
val red = (0..255).random()
|
||||
val green = (0..255).random()
|
||||
val blue = (0..255).random()
|
||||
return (0xFF shl 24) or (red shl 16) or (green shl 8) or blue
|
||||
}
|
||||
|
||||
fun generateCinemaName(index: Int): String {
|
||||
val base = 'a'.toInt()
|
||||
private fun generateCinemaName(index: Int): String {
|
||||
val base = 'a'.code
|
||||
val alphabetSize = 26
|
||||
val sb = StringBuilder()
|
||||
var remainder = index
|
||||
|
@ -12,49 +12,49 @@ import com.example.myapplication.database.entities.composeui.edit.SessionEditVie
|
||||
object AppViewModelProvider {
|
||||
val Factory = viewModelFactory {
|
||||
initializer {
|
||||
CinemaListViewModel(cinemaApplication().container.cinemaRepository)
|
||||
CinemaListViewModel(cinemaApplication().container.cinemaRestRepository)
|
||||
}
|
||||
initializer {
|
||||
CinemaEditViewModel(
|
||||
this.createSavedStateHandle(),
|
||||
cinemaApplication().container.cinemaRepository
|
||||
cinemaApplication().container.cinemaRestRepository
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
CinemaViewModel(
|
||||
this.createSavedStateHandle(),
|
||||
cinemaApplication().container.cinemaRepository,
|
||||
cinemaApplication().container.cinemaRestRepository,
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
SessionListViewModel(
|
||||
cinemaApplication().container.sessionRepository,
|
||||
cinemaApplication().container.userSessionRepository,
|
||||
cinemaApplication().container.sessionRestRepository,
|
||||
cinemaApplication().container.userSessionRestRepository,
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
SessionEditViewModel(
|
||||
this.createSavedStateHandle(),
|
||||
cinemaApplication().container.sessionRepository,
|
||||
cinemaApplication().container.sessionRestRepository,
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
CartViewModel(
|
||||
cinemaApplication().container.userSessionRepository,
|
||||
cinemaApplication().container.orderRepository,
|
||||
cinemaApplication().container.orderSessionRepository,
|
||||
cinemaApplication().container.userRepository,
|
||||
cinemaApplication().container.userSessionRestRepository,
|
||||
cinemaApplication().container.orderRestRepository,
|
||||
cinemaApplication().container.orderSessionRestRepository,
|
||||
cinemaApplication().container.userRestRepository,
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
OrderListViewModel(
|
||||
cinemaApplication().container.orderRepository,
|
||||
cinemaApplication().container.orderRestRepository,
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
OrderViewModel(
|
||||
this.createSavedStateHandle(),
|
||||
cinemaApplication().container.orderRepository,
|
||||
cinemaApplication().container.orderRestRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package com.example.myapplication.database.entities.composeui
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.myapplication.database.AppDataContainer
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
@ -12,10 +13,7 @@ import com.example.myapplication.database.entities.repository.OrderRepository
|
||||
import com.example.myapplication.database.entities.repository.OrderSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.UserRepository
|
||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class CartViewModel(
|
||||
private val userSessionRepository: UserSessionRepository,
|
||||
@ -24,14 +22,13 @@ class CartViewModel(
|
||||
private val userRepository: UserRepository,
|
||||
) : ViewModel() {
|
||||
private val userUid: Int = 1
|
||||
var cartUiState by mutableStateOf(CartUiState())
|
||||
private set
|
||||
|
||||
val cartUiState: StateFlow<CartUiState> = userRepository.getCartByUser(userUid).map {
|
||||
CartUiState(it)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
||||
initialValue = CartUiState()
|
||||
)
|
||||
suspend fun refreshState() {
|
||||
val cart = userRepository.getCartByUser(userUid)
|
||||
cartUiState = CartUiState(cart)
|
||||
}
|
||||
|
||||
suspend fun addToOrder(userId: Int, sessions: List<SessionFromCart>) {
|
||||
if (sessions.isEmpty())
|
||||
@ -48,10 +45,12 @@ class CartViewModel(
|
||||
)
|
||||
}
|
||||
userSessionRepository.deleteUserSessions(userId)
|
||||
refreshState()
|
||||
}
|
||||
|
||||
suspend fun removeFromCart(user: Int, session: Session, count: Int = 1) {
|
||||
userSessionRepository.deleteUserSession(UserSessionCrossRef(user, session.uid, count))
|
||||
refreshState()
|
||||
}
|
||||
|
||||
suspend fun updateFromCart(userId: Int, session: Session, count: Int, availableCount: Int)
|
||||
@ -63,6 +62,7 @@ class CartViewModel(
|
||||
if (count > availableCount)
|
||||
return false
|
||||
userSessionRepository.updateUserSession(UserSessionCrossRef(userId, session.uid, count))
|
||||
refreshState()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -46,17 +46,7 @@ fun CinemaList(
|
||||
viewModel: CinemaListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val cinemaPagingItems = viewModel.cinemaPagerState.cinemaPagingData.collectAsLazyPagingItems()
|
||||
|
||||
fun findCinemas() {
|
||||
coroutineScope.launch {
|
||||
viewModel.findCinemas()
|
||||
}
|
||||
}
|
||||
LaunchedEffect(1) {
|
||||
findCinemas()
|
||||
}
|
||||
val cinemaPagingItems = viewModel.cinemaListUiState.collectAsLazyPagingItems()
|
||||
|
||||
Scaffold(
|
||||
topBar = {},
|
||||
@ -132,7 +122,6 @@ private fun CinemaList(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,29 +15,9 @@ import kotlinx.coroutines.flow.emptyFlow
|
||||
class CinemaListViewModel(
|
||||
private val cinemaRepository: CinemaRepository
|
||||
) : ViewModel() {
|
||||
private val pagingConfig = PagingConfig(
|
||||
pageSize = 4,
|
||||
prefetchDistance = 4
|
||||
)
|
||||
|
||||
var cinemaPagerState by mutableStateOf(CinemaPagerState())
|
||||
private set
|
||||
|
||||
fun findCinemas() {
|
||||
val pager = Pager(
|
||||
config = pagingConfig,
|
||||
pagingSourceFactory = {
|
||||
cinemaRepository.getAllCinemasPaged()
|
||||
}
|
||||
)
|
||||
cinemaPagerState = CinemaPagerState(pager.flow)
|
||||
}
|
||||
val cinemaListUiState: Flow<PagingData<Cinema>> = cinemaRepository.getAllCinemas()
|
||||
|
||||
suspend fun deleteCinema(cinema: Cinema) {
|
||||
cinemaRepository.deleteCinema(cinema)
|
||||
}
|
||||
}
|
||||
|
||||
data class CinemaPagerState(
|
||||
val cinemaPagingData: Flow<PagingData<Cinema>> = emptyFlow()
|
||||
)
|
@ -19,8 +19,7 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
@ -38,7 +37,11 @@ fun CinemaView(
|
||||
navController: NavController,
|
||||
viewModel: CinemaViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||
) {
|
||||
val cinemaUiState by viewModel.cinemaUiState.collectAsState()
|
||||
val cinemaUiState = viewModel.cinemaUiState
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.refreshState()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@ -131,7 +134,7 @@ fun CinemaView(
|
||||
}
|
||||
}
|
||||
if (cinemaUiState.cinemaWithSessions != null) {
|
||||
SessionList(cinemaUiState.cinemaWithSessions!!, navController)
|
||||
SessionList(viewModel, navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,45 @@
|
||||
package com.example.myapplication.database.entities.composeui
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.myapplication.database.AppDataContainer
|
||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||
import com.example.myapplication.database.entities.repository.CinemaRepository
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class CinemaViewModel(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val cinemaRepository: CinemaRepository
|
||||
savedStateHandle: SavedStateHandle, private val cinemaRepository: CinemaRepository
|
||||
) : ViewModel() {
|
||||
private val cinemaUid: Int = checkNotNull(savedStateHandle["id"])
|
||||
|
||||
val cinemaUiState: StateFlow<CinemaUiState> = cinemaRepository.getCinema(
|
||||
cinemaUid
|
||||
).map {
|
||||
CinemaUiState(it)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
||||
initialValue = CinemaUiState()
|
||||
)
|
||||
var cinemaUiState by mutableStateOf(CinemaUiState())
|
||||
private set
|
||||
|
||||
suspend fun refreshState() {
|
||||
if (cinemaUid > 0) {
|
||||
cinemaUiState = CinemaUiState(cinemaRepository.getCinema(cinemaUid))
|
||||
}
|
||||
}
|
||||
|
||||
// init {
|
||||
// viewModelScope.launch {
|
||||
// if (cinemaUid > 0) {
|
||||
// cinemaUiState = CinemaUiState(cinemaRepository.getCinema(cinemaUid))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// val cinemaUiState: mutableStateOf(CinemaUiState()) = cinemaRepository.getCinema(
|
||||
// cinemaUid
|
||||
// ).map
|
||||
// {
|
||||
// CinemaUiState(it)
|
||||
// }.stateIn(
|
||||
// scope = viewModelScope,
|
||||
// started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
||||
// initialValue = CinemaUiState()
|
||||
// )
|
||||
}
|
||||
|
||||
data class CinemaUiState(val cinemaWithSessions: CinemaWithSessions? = null)
|
@ -25,6 +25,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemContentType
|
||||
import androidx.paging.compose.itemKey
|
||||
import com.example.myapplication.composeui.navigation.Screen
|
||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||
|
||||
@ -35,14 +38,17 @@ fun OrderList(
|
||||
viewModel: OrderListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val ordersUiState by viewModel.orderListUiState.collectAsState()
|
||||
val ordersUiState = viewModel.orderListUiState.collectAsLazyPagingItems()
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 10.dp)
|
||||
) {
|
||||
items(ordersUiState.orderList) { order ->
|
||||
val orderId = Screen.OrderView.route.replace("{id}", order.uid.toString())
|
||||
items(count = ordersUiState.itemCount,
|
||||
key = ordersUiState.itemKey(),
|
||||
contentType = ordersUiState.itemContentType()) { index ->
|
||||
val order = ordersUiState[index]
|
||||
val orderId = Screen.OrderView.route.replace("{id}", order!!.uid.toString())
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
@ -1,25 +1,25 @@
|
||||
package com.example.myapplication.database.entities.composeui
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
import com.example.myapplication.database.AppDataContainer
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class OrderListViewModel(
|
||||
private val orderRepository: OrderRepository
|
||||
) : ViewModel() {
|
||||
val orderListUiState: StateFlow<OrderListUiState> = orderRepository.getAllOrders(1).map {
|
||||
OrderListUiState(it)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
||||
initialValue = OrderListUiState()
|
||||
)
|
||||
val orderListUiState: Flow<PagingData<Order>> = orderRepository.getAllOrders(1)
|
||||
}
|
||||
|
||||
data class OrderListUiState(val orderList: List<Order> = listOf())
|
@ -3,11 +3,13 @@ package com.example.myapplication.database.entities.composeui
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.myapplication.database.AppContainer
|
||||
import com.example.myapplication.database.AppDataContainer
|
||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
@ -16,11 +18,12 @@ class OrderViewModel(
|
||||
private val orderRepository: OrderRepository
|
||||
) : ViewModel() {
|
||||
private val orderUid: Int = checkNotNull(savedStateHandle["id"])
|
||||
val orderUiState: StateFlow<OrderUiState> = orderRepository.getOrder(orderUid).map {
|
||||
|
||||
val orderUiState: StateFlow<OrderUiState> = flow{emit(orderRepository.getOrder(orderUid))} .map {
|
||||
OrderUiState(it)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppContainer.TIMEOUT),
|
||||
initialValue = OrderUiState()
|
||||
)
|
||||
}
|
||||
|
@ -33,17 +33,18 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.composeui.navigation.Screen
|
||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||
import kotlinx.coroutines.launch
|
||||
import org.threeten.bp.format.DateTimeFormatter
|
||||
|
||||
@Composable
|
||||
fun SessionList(
|
||||
cinemaWithSessions: CinemaWithSessions,
|
||||
cinemaWithSessionsViewModel: CinemaViewModel,
|
||||
navController: NavController,
|
||||
viewModel: SessionListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val cinemaWithSessions = cinemaWithSessionsViewModel.cinemaUiState.cinemaWithSessions!!
|
||||
|
||||
LazyColumn {
|
||||
if (cinemaWithSessions.sessions.isEmpty()) {
|
||||
item {
|
||||
@ -107,6 +108,7 @@ fun SessionList(
|
||||
IconButton(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
if (session.availableCount != 0)
|
||||
viewModel.addSessionInCart(sessionId = session.uid)
|
||||
}
|
||||
},
|
||||
@ -122,6 +124,7 @@ fun SessionList(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
viewModel.deleteSession(session = session)
|
||||
cinemaWithSessionsViewModel.refreshState()
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
@ -27,8 +27,6 @@ class CinemaEditViewModel(
|
||||
viewModelScope.launch {
|
||||
if (cinemaUid > 0) {
|
||||
cinemaUiState = cinemaRepository.getCinema(cinemaUid)
|
||||
.filterNotNull()
|
||||
.first()
|
||||
.toUiState(true)
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,6 @@ class SessionEditViewModel(
|
||||
viewModelScope.launch {
|
||||
if (sessionUid > 0) {
|
||||
sessionUiState = sessionRepository.getSession(sessionUid)
|
||||
.filterNotNull()
|
||||
.first()
|
||||
.toUiState(true)
|
||||
}
|
||||
}
|
||||
|
@ -13,13 +13,10 @@ import kotlinx.coroutines.flow.Flow
|
||||
@Dao
|
||||
interface CinemaDao {
|
||||
@Query("select * from cinemas order by name")
|
||||
fun getAll(): Flow<List<Cinema>>
|
||||
|
||||
@Query("select * from cinemas order by name")
|
||||
fun getAllCinemasPaged(): PagingSource<Int, Cinema>
|
||||
fun getAll(): PagingSource<Int, Cinema>
|
||||
|
||||
@Query(
|
||||
"SELECT c.*, s.uid as session_uid, s.date_time, s.price, s.max_count-IFNULL(SUM(os.count), 0) as available_count " +
|
||||
"SELECT c.*, s.uid as session_uid, s.date_time, s.price, s.max_count-IFNULL(SUM(os.count), 0) as available_count, c.uid as cinema_id " +
|
||||
"FROM cinemas AS c " +
|
||||
"LEFT JOIN sessions AS s ON s.cinema_id = c.uid " +
|
||||
"LEFT JOIN orders_sessions AS os ON os.session_id = s.uid " +
|
||||
@ -29,11 +26,14 @@ interface CinemaDao {
|
||||
fun getByUid(cinemaId: Int?): Flow<Map<Cinema, List<SessionFromCinema>>>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(cinema: Cinema)
|
||||
suspend fun insert(vararg cinema: Cinema)
|
||||
|
||||
@Update
|
||||
suspend fun update(cinema: Cinema)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(cinema: Cinema)
|
||||
|
||||
@Query("DELETE FROM cinemas")
|
||||
suspend fun deleteAll()
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.example.myapplication.database.entities.dao
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
@ -7,12 +8,11 @@ import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface OrderDao {
|
||||
@Query("select * from orders where user_id = :userId")
|
||||
fun getAll(userId: Int?): Flow<List<Order>>
|
||||
fun getAll(userId: Int?): PagingSource<Int, Order>
|
||||
|
||||
@Query(
|
||||
"SELECT o.*, s.*, os.count, os.frozen_price " +
|
||||
@ -21,14 +21,17 @@ interface OrderDao {
|
||||
"JOIN sessions AS s ON s.uid = os.session_id " +
|
||||
"WHERE o.uid = :orderId"
|
||||
)
|
||||
fun getByUid(orderId: Int?): Flow<List<SessionFromOrder>>
|
||||
fun getByUid(orderId: Int?): List<SessionFromOrder>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(order: Order): Long
|
||||
suspend fun insert(vararg order: Order): List<Long>
|
||||
|
||||
@Update
|
||||
suspend fun update(order: Order)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(order: Order)
|
||||
|
||||
@Query("DELETE FROM orders")
|
||||
suspend fun deleteAll()
|
||||
}
|
@ -3,12 +3,14 @@ package com.example.myapplication.database.entities.dao
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
||||
|
||||
@Dao
|
||||
interface OrderSessionCrossRefDao {
|
||||
@Insert
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(orderSessionCrossRef: OrderSessionCrossRef)
|
||||
|
||||
@Update
|
||||
@ -16,4 +18,10 @@ interface OrderSessionCrossRefDao {
|
||||
|
||||
@Delete
|
||||
suspend fun delete(orderSessionCrossRef: OrderSessionCrossRef)
|
||||
|
||||
@Query("DELETE FROM orders_sessions where orders_sessions.order_id = :orderId")
|
||||
suspend fun deleteByOrderUid(orderId: Int)
|
||||
|
||||
@Query("DELETE FROM orders_sessions where orders_sessions.session_id = :sessionId")
|
||||
suspend fun deleteBySessionUid(sessionId: Int)
|
||||
}
|
@ -6,19 +6,30 @@ import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface SessionDao {
|
||||
@Query("select * from sessions where sessions.uid = :uid")
|
||||
fun getByUid(uid: Int): Flow<Session>
|
||||
suspend fun getByUid(uid: Int): Session
|
||||
|
||||
@Insert
|
||||
suspend fun insert(session: Session)
|
||||
suspend fun insert(vararg session: Session)
|
||||
|
||||
@Update
|
||||
suspend fun update(session: Session)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(session: Session)
|
||||
|
||||
@Query("DELETE FROM sessions")
|
||||
suspend fun deleteAll()
|
||||
|
||||
@Query(
|
||||
"SELECT s.max_count-IFNULL(SUM(os.count), 0) as available_count " +
|
||||
"FROM sessions AS s " +
|
||||
"LEFT JOIN orders_sessions AS os ON os.session_id = s.uid " +
|
||||
"WHERE s.uid = :sessionId " +
|
||||
"GROUP BY s.uid"
|
||||
)
|
||||
suspend fun getAvailableCountOfSession(sessionId: Int): Int
|
||||
}
|
@ -22,14 +22,17 @@ interface UserDao {
|
||||
"where users_sessions.user_id = :userId " +
|
||||
"GROUP BY users_sessions.session_id "
|
||||
)
|
||||
fun getCartByUid(userId: Int): Flow<List<SessionFromCart>>
|
||||
suspend fun getCartByUid(userId: Int): List<SessionFromCart>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(user: User)
|
||||
suspend fun insert(vararg user: User)
|
||||
|
||||
@Update
|
||||
suspend fun update(user: User)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(user: User)
|
||||
|
||||
@Query("DELETE FROM users")
|
||||
suspend fun deleteAll()
|
||||
}
|
@ -3,13 +3,14 @@ package com.example.myapplication.database.entities.dao
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
||||
|
||||
@Dao
|
||||
interface UserSessionCrossRefDao {
|
||||
@Insert
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(userSessionCrossRef: UserSessionCrossRef)
|
||||
|
||||
@Update
|
||||
@ -20,4 +21,7 @@ interface UserSessionCrossRefDao {
|
||||
|
||||
@Query("DELETE FROM users_sessions where users_sessions.user_id = :userId")
|
||||
suspend fun deleteByUserUid(userId: Int)
|
||||
|
||||
@Query("DELETE FROM users_sessions where users_sessions.session_id = :sessionId")
|
||||
suspend fun deleteBySessionUid(sessionId: Int)
|
||||
}
|
@ -43,7 +43,7 @@ data class Cinema(
|
||||
if (name != other.name) return false
|
||||
if (description != other.description) return false
|
||||
if (year != other.year) return false
|
||||
return true
|
||||
return image.contentEquals(other.image)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
@ -12,6 +12,8 @@ data class SessionFromCinema(
|
||||
val price: Double,
|
||||
@ColumnInfo(name = "available_count")
|
||||
val availableCount: Int,
|
||||
@ColumnInfo(name = "cinema_id")
|
||||
val cinemaId: Int,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
@ -22,6 +24,7 @@ data class SessionFromCinema(
|
||||
if (dateFormatter.format(dateTime) != dateFormatter.format(other.dateTime)) return false
|
||||
if (price != other.price) return false
|
||||
if (availableCount != other.availableCount) return false
|
||||
if (cinemaId != other.cinemaId) return false
|
||||
return true
|
||||
}
|
||||
|
||||
@ -30,6 +33,15 @@ data class SessionFromCinema(
|
||||
result = 31 * result + dateTime.hashCode()
|
||||
result = 31 * result + price.hashCode()
|
||||
result = 31 * result + availableCount.hashCode()
|
||||
result = 31 * result + cinemaId.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun SessionFromCinema.toSession(): Session = Session (
|
||||
uid,
|
||||
dateTime,
|
||||
price,
|
||||
availableCount,
|
||||
cinemaId
|
||||
)
|
@ -6,14 +6,14 @@ import org.threeten.bp.LocalDateTime
|
||||
|
||||
data class SessionFromOrder(
|
||||
@ColumnInfo(name = "uid")
|
||||
val uid: Int?,
|
||||
val uid: Int = 0,
|
||||
@ColumnInfo(name = "date_time")
|
||||
val dateTime: LocalDateTime,
|
||||
@ColumnInfo(name = "frozen_price")
|
||||
val frozenPrice: Double,
|
||||
val count: Int,
|
||||
@ColumnInfo(name = "cinema_id")
|
||||
val cinemaId: Int?,
|
||||
val cinemaId: Int = 0,
|
||||
@Relation(
|
||||
parentColumn = "cinema_id",
|
||||
entity = Cinema::class,
|
||||
|
@ -1,14 +1,14 @@
|
||||
package com.example.myapplication.database.entities.repository
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.PagingSource
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface CinemaRepository {
|
||||
fun getAllCinemas(): Flow<List<Cinema>>
|
||||
fun getAllCinemasPaged(): PagingSource<Int, Cinema>
|
||||
fun getCinema(uid: Int): Flow<CinemaWithSessions>
|
||||
fun getAllCinemas(): Flow<PagingData<Cinema>>
|
||||
suspend fun getCinema(uid: Int): CinemaWithSessions
|
||||
suspend fun insertCinema(cinema: Cinema)
|
||||
suspend fun updateCinema(cinema: Cinema)
|
||||
suspend fun deleteCinema(cinema: Cinema)
|
||||
|
@ -1,28 +1,38 @@
|
||||
package com.example.myapplication.database.entities.repository
|
||||
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.PagingSource
|
||||
import com.example.myapplication.database.AppContainer
|
||||
import com.example.myapplication.database.entities.dao.CinemaDao
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class OfflineCinemaRepository(private val cinemaDao: CinemaDao) : CinemaRepository {
|
||||
override fun getAllCinemas(): Flow<List<Cinema>> = cinemaDao.getAll()
|
||||
override fun getAllCinemas(): Flow<PagingData<Cinema>> = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = AppContainer.LIMIT,
|
||||
enablePlaceholders = false
|
||||
),
|
||||
pagingSourceFactory = cinemaDao::getAll
|
||||
).flow
|
||||
|
||||
override fun getAllCinemasPaged(): PagingSource<Int, Cinema> = cinemaDao.getAllCinemasPaged()
|
||||
|
||||
override fun getCinema(uid: Int): Flow<CinemaWithSessions> {
|
||||
return flow {
|
||||
cinemaDao.getByUid(uid).collect {
|
||||
emit(it.firstNotNullOf {
|
||||
override suspend fun getCinema(uid: Int): CinemaWithSessions {
|
||||
val item = cinemaDao.getByUid(uid)
|
||||
.map { map ->
|
||||
map.firstNotNullOf {
|
||||
CinemaWithSessions(
|
||||
cinema = it.key,
|
||||
sessions = it.value
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
.first()
|
||||
return item
|
||||
}
|
||||
|
||||
override suspend fun insertCinema(cinema: Cinema) = cinemaDao.insert(cinema)
|
||||
@ -30,4 +40,11 @@ class OfflineCinemaRepository(private val cinemaDao: CinemaDao) : CinemaReposito
|
||||
override suspend fun updateCinema(cinema: Cinema) = cinemaDao.update(cinema)
|
||||
|
||||
override suspend fun deleteCinema(cinema: Cinema) = cinemaDao.delete(cinema)
|
||||
|
||||
fun getAllCinemasPagingSource(): PagingSource<Int, Cinema> = cinemaDao.getAll()
|
||||
|
||||
suspend fun insertCinemas(cinemas: List<Cinema>) =
|
||||
cinemaDao.insert(*cinemas.toTypedArray())
|
||||
|
||||
suspend fun clearCinemas() = cinemaDao.deleteAll()
|
||||
}
|
@ -1,18 +1,31 @@
|
||||
package com.example.myapplication.database.entities.repository
|
||||
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.PagingSource
|
||||
import com.example.myapplication.database.AppContainer
|
||||
import com.example.myapplication.database.entities.dao.OrderDao
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class OfflineOrderRepository(private val orderDao: OrderDao) : OrderRepository {
|
||||
override fun getAllOrders(userId: Int?): Flow<List<Order>> = orderDao.getAll(userId)
|
||||
override fun getAllOrders(userId: Int?): Flow<PagingData<Order>> = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = AppContainer.LIMIT,
|
||||
enablePlaceholders = false
|
||||
),
|
||||
pagingSourceFactory = { orderDao.getAll(userId) }
|
||||
).flow
|
||||
|
||||
override fun getOrder(orderId: Int?): Flow<List<SessionFromOrder>> = orderDao.getByUid(orderId)
|
||||
override suspend fun getOrder(uid: Int): List<SessionFromOrder> = orderDao.getByUid(uid)
|
||||
|
||||
override suspend fun insertOrder(order: Order): Long = orderDao.insert(order)
|
||||
override suspend fun insertOrder(order: Order): Long = orderDao.insert(order).first()
|
||||
|
||||
override suspend fun updateOrder(order: Order) = orderDao.update(order)
|
||||
fun getAllOrdersPagingSource(userId: Int?): PagingSource<Int, Order> = orderDao.getAll(userId)
|
||||
|
||||
override suspend fun deleteOrder(order: Order) = orderDao.delete(order)
|
||||
suspend fun clearOrders() = orderDao.deleteAll()
|
||||
|
||||
suspend fun insertOrders(orders: List<Order>) = orderDao.insert(*orders.toTypedArray())
|
||||
}
|
@ -13,4 +13,8 @@ class OfflineOrderSessionRepository(private val orderSessionDao: OrderSessionCro
|
||||
|
||||
override suspend fun deleteOrderSession(orderSessionCrossRef: OrderSessionCrossRef) =
|
||||
orderSessionDao.delete(orderSessionCrossRef)
|
||||
|
||||
suspend fun deleteOrderSessions(userId: Int) = orderSessionDao.deleteByOrderUid(userId)
|
||||
|
||||
suspend fun deleteSessionsByUid(sessionId: Int) = orderSessionDao.deleteBySessionUid(sessionId)
|
||||
}
|
@ -2,14 +2,18 @@ package com.example.myapplication.database.entities.repository
|
||||
|
||||
import com.example.myapplication.database.entities.dao.SessionDao
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class OfflineSessionRepository(private val sessionDao: SessionDao) : SessionRepository {
|
||||
override fun getSession(uid: Int): Flow<Session?> = sessionDao.getByUid(uid)
|
||||
override suspend fun getSession(uid: Int): Session = sessionDao.getByUid(uid)
|
||||
|
||||
override suspend fun insertSession(session: Session) = sessionDao.insert(session)
|
||||
|
||||
override suspend fun updateSession(session: Session) = sessionDao.update(session)
|
||||
|
||||
override suspend fun deleteSession(session: Session) = sessionDao.delete(session)
|
||||
|
||||
suspend fun insertSessions(sessions: List<Session>) =
|
||||
sessionDao.insert(*sessions.toTypedArray())
|
||||
|
||||
suspend fun clearSessions() = sessionDao.deleteAll()
|
||||
}
|
@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
||||
override fun getAllUsers(): Flow<List<User>> = userDao.getAll()
|
||||
|
||||
override fun getCartByUser(userId: Int): Flow<List<SessionFromCart>> =
|
||||
override suspend fun getCartByUser(userId: Int): List<SessionFromCart> =
|
||||
userDao.getCartByUid(userId)
|
||||
|
||||
override suspend fun insertUser(user: User) = userDao.insert(user)
|
||||
@ -16,4 +16,9 @@ class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
||||
override suspend fun updateUser(user: User) = userDao.update(user)
|
||||
|
||||
override suspend fun deleteUser(user: User) = userDao.delete(user)
|
||||
|
||||
suspend fun insertUsers(users: List<User>) =
|
||||
userDao.insert(*users.toTypedArray())
|
||||
|
||||
suspend fun clearUsers() = userDao.deleteAll()
|
||||
}
|
@ -15,4 +15,6 @@ class OfflineUserSessionRepository(private val userSessionDao: UserSessionCrossR
|
||||
userSessionDao.delete(userSessionCrossRef)
|
||||
|
||||
override suspend fun deleteUserSessions(userId: Int) = userSessionDao.deleteByUserUid(userId)
|
||||
|
||||
suspend fun deleteSessionsByUid(sessionId: Int) = userSessionDao.deleteBySessionUid(sessionId)
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
package com.example.myapplication.database.entities.repository
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface OrderRepository {
|
||||
fun getAllOrders(userId: Int?): Flow<List<Order>>
|
||||
fun getOrder(orderId: Int?): Flow<List<SessionFromOrder>>
|
||||
fun getAllOrders(userId: Int?): Flow<PagingData<Order>>
|
||||
suspend fun getOrder(uid: Int): List<SessionFromOrder>
|
||||
suspend fun insertOrder(order: Order): Long
|
||||
suspend fun updateOrder(order: Order)
|
||||
suspend fun deleteOrder(order: Order)
|
||||
}
|
@ -4,7 +4,7 @@ import com.example.myapplication.database.entities.model.Session
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SessionRepository {
|
||||
fun getSession(uid: Int): Flow<Session?>
|
||||
suspend fun getSession(uid: Int): Session
|
||||
suspend fun insertSession(session: Session)
|
||||
suspend fun updateSession(session: Session)
|
||||
suspend fun deleteSession(session: Session)
|
||||
|
@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface UserRepository {
|
||||
fun getAllUsers(): Flow<List<User>>
|
||||
fun getCartByUser(userId: Int): Flow<List<SessionFromCart>>
|
||||
suspend fun getCartByUser(userId: Int): List<SessionFromCart>
|
||||
suspend fun insertUser(user: User)
|
||||
suspend fun updateUser(user: User)
|
||||
suspend fun deleteUser(user: User)
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.example.myapplication.database.remotekeys.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
|
||||
import com.example.myapplication.database.remotekeys.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)
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.example.myapplication.database.remotekeys.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
|
||||
enum class RemoteKeyType(private val type: String) {
|
||||
CINEMA(Cinema::class.simpleName ?: "Cinema"),
|
||||
ORDER(Order::class.simpleName ?: "Order"),
|
||||
SESSION(Session::class.simpleName ?: "Session");
|
||||
|
||||
@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?
|
||||
)
|
@ -0,0 +1,16 @@
|
||||
package com.example.myapplication.database.remotekeys.repository
|
||||
|
||||
import com.example.myapplication.database.remotekeys.dao.RemoteKeysDao
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
|
||||
import com.example.myapplication.database.remotekeys.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)
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.example.myapplication.database.remotekeys.repository
|
||||
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
|
||||
import com.example.myapplication.database.remotekeys.model.RemoteKeys
|
||||
|
||||
interface RemoteKeyRepository {
|
||||
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
|
||||
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>)
|
||||
suspend fun deleteRemoteKey(type: RemoteKeyType)
|
||||
}
|
6
app/src/main/res/xml/network_security_config.xml
Normal file
6
app/src/main/res/xml/network_security_config.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">192.168.154.166</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
@ -3,4 +3,5 @@ plugins {
|
||||
id("com.android.application") version "8.1.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.20" apply false
|
||||
id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.20" apply false
|
||||
}
|
42
server/.gitignore
vendored
Normal file
42
server/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
70088
server/.~data.json
Normal file
70088
server/.~data.json
Normal file
File diff suppressed because it is too large
Load Diff
70088
server/data.json
Normal file
70088
server/data.json
Normal file
File diff suppressed because it is too large
Load Diff
1335
server/package-lock.json
generated
Normal file
1335
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
server/package.json
Normal file
12
server/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "fake-db",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "json-server --watch data.json --host 0.0.0.0 -p 8079"
|
||||
},
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"json-server": "0.17.4"
|
||||
}
|
||||
}
|
26
server/readme.md
Normal file
26
server/readme.md
Normal file
@ -0,0 +1,26 @@
|
||||
Установка nodejs:
|
||||
1. Заходим на сайт https://nodejs.org/en/
|
||||
2. Скачиваем LTS версию
|
||||
3. Устанавливаем
|
||||
|
||||
Переход в каталог с сервером:
|
||||
```commandline
|
||||
cd ./server
|
||||
```
|
||||
|
||||
Установка зависимостей:
|
||||
```commandline
|
||||
npm install
|
||||
```
|
||||
|
||||
Запуск:
|
||||
```commandline
|
||||
npm start
|
||||
```
|
||||
|
||||
Примеры:
|
||||
- http://localhost:8079
|
||||
- http://localhost:8079/students
|
||||
- http://localhost:8079/students?_expand=group
|
||||
|
||||
Документация -- https://www.npmjs.com/package/json-server
|
Loading…
Reference in New Issue
Block a user