lw5
This commit is contained in:
parent
e4bc55da4e
commit
f499c56d14
@ -2,6 +2,7 @@ plugins {
|
|||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
id("com.google.devtools.ksp")
|
id("com.google.devtools.ksp")
|
||||||
|
id("org.jetbrains.kotlin.plugin.serialization")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -57,11 +58,6 @@ dependencies {
|
|||||||
implementation("io.github.vanpra.compose-material-dialogs:datetime:0.8.1-rc")
|
implementation("io.github.vanpra.compose-material-dialogs:datetime:0.8.1-rc")
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
|
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
|
// Core
|
||||||
implementation("androidx.core:core-ktx:1.9.0")
|
implementation("androidx.core:core-ktx:1.9.0")
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
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-graphics")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.material3:material3:1.1.2")
|
implementation("androidx.compose.material3:material3:1.1.2")
|
||||||
|
implementation("androidx.compose.material:material:1.4.3")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
val room_version = "2.5.2"
|
val room_version = "2.5.2"
|
||||||
@ -83,6 +80,14 @@ dependencies {
|
|||||||
implementation("androidx.room:room-ktx:$room_version")
|
implementation("androidx.room:room-ktx:$room_version")
|
||||||
implementation("androidx.room:room-paging:$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
|
// Tests
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".CinemaApplication"
|
android:name=".CinemaApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -12,7 +14,8 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Pmudemo"
|
android:theme="@style/Theme.Pmudemo"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainComposeActivity"
|
android:name=".MainComposeActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
@ -13,15 +13,12 @@ import com.example.myapplication.composeui.navigation.MainNavbar
|
|||||||
import com.example.myapplication.datastore.DataStoreManager
|
import com.example.myapplication.datastore.DataStoreManager
|
||||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||||
|
|
||||||
//import com.jakewharton.threetenabp.AndroidThreeTen
|
|
||||||
|
|
||||||
class MainComposeActivity : ComponentActivity() {
|
class MainComposeActivity : ComponentActivity() {
|
||||||
private val dataStoreManager = DataStoreManager(this)
|
private val dataStoreManager = DataStoreManager(this)
|
||||||
private val isDarkTheme = mutableStateOf(true)
|
private val isDarkTheme = mutableStateOf(true)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
application.deleteDatabase("pmy-db")
|
application.deleteDatabase("pmy-db")
|
||||||
//AndroidThreeTen.init(this)
|
|
||||||
setContent {
|
setContent {
|
||||||
PmudemoTheme(darkTheme = isDarkTheme.value) {
|
PmudemoTheme(darkTheme = isDarkTheme.value) {
|
||||||
LaunchedEffect(key1 = true) {
|
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.Text
|
||||||
import androidx.compose.material3.rememberDismissState
|
import androidx.compose.material3.rememberDismissState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
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.Session
|
||||||
import com.example.myapplication.database.entities.model.SessionFromCart
|
import com.example.myapplication.database.entities.model.SessionFromCart
|
||||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.threeten.bp.format.DateTimeFormatter
|
import org.threeten.bp.format.DateTimeFormatter
|
||||||
|
|
||||||
@ -66,7 +67,11 @@ fun Cart(
|
|||||||
viewModel: CartViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
viewModel: CartViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val cartUiState by viewModel.cartUiState.collectAsState()
|
val cartUiState = viewModel.cartUiState
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.refreshState()
|
||||||
|
}
|
||||||
|
|
||||||
Cart(
|
Cart(
|
||||||
cartUiState = cartUiState,
|
cartUiState = cartUiState,
|
||||||
@ -276,7 +281,13 @@ private fun SessionListItem(
|
|||||||
)
|
)
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { onChangeCount(session, 1, ++currentCount) }
|
onClick = {
|
||||||
|
onChangeCount(
|
||||||
|
session,
|
||||||
|
1,
|
||||||
|
if (currentCount != session.availableCount) ++currentCount else currentCount
|
||||||
|
)
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Add,
|
imageVector = Icons.Default.Add,
|
||||||
|
@ -1,49 +1,101 @@
|
|||||||
package com.example.myapplication.database
|
package com.example.myapplication.database
|
||||||
|
|
||||||
import android.content.Context
|
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.OfflineCinemaRepository
|
||||||
import com.example.myapplication.database.entities.repository.OfflineOrderRepository
|
import com.example.myapplication.database.entities.repository.OfflineOrderRepository
|
||||||
import com.example.myapplication.database.entities.repository.OfflineOrderSessionRepository
|
import com.example.myapplication.database.entities.repository.OfflineOrderSessionRepository
|
||||||
import com.example.myapplication.database.entities.repository.OfflineSessionRepository
|
import com.example.myapplication.database.entities.repository.OfflineSessionRepository
|
||||||
import com.example.myapplication.database.entities.repository.OfflineUserRepository
|
import com.example.myapplication.database.entities.repository.OfflineUserRepository
|
||||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
|
||||||
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
|
|
||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val cinemaRepository: CinemaRepository
|
val cinemaRestRepository: RestCinemaRepository
|
||||||
val orderRepository: OrderRepository
|
val sessionRestRepository: RestSessionRepository
|
||||||
val orderSessionRepository: OrderSessionRepository
|
val userRestRepository: RestUserRepository
|
||||||
val sessionRepository: SessionRepository
|
val orderRestRepository: RestOrderRepository
|
||||||
val userRepository: UserRepository
|
val orderSessionRestRepository: RestOrderSessionRepository
|
||||||
val userSessionRepository: UserSessionRepository
|
val userSessionRestRepository: RestUserSessionRepository
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TIMEOUT = 5000L
|
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.Session
|
||||||
import com.example.myapplication.database.entities.model.User
|
import com.example.myapplication.database.entities.model.User
|
||||||
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -27,8 +29,15 @@ import org.threeten.bp.LocalDateTime
|
|||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [Cinema::class, Session::class, Order::class,
|
entities = [
|
||||||
OrderSessionCrossRef::class, User::class, UserSessionCrossRef::class],
|
Cinema::class,
|
||||||
|
Session::class,
|
||||||
|
Order::class,
|
||||||
|
OrderSessionCrossRef::class,
|
||||||
|
User::class,
|
||||||
|
UserSessionCrossRef::class,
|
||||||
|
RemoteKeys::class
|
||||||
|
],
|
||||||
version = 1,
|
version = 1,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
@ -40,6 +49,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract fun orderSessionCrossRefDao(): OrderSessionCrossRefDao
|
abstract fun orderSessionCrossRefDao(): OrderSessionCrossRefDao
|
||||||
abstract fun userDao(): UserDao
|
abstract fun userDao(): UserDao
|
||||||
abstract fun userSessionCrossRefDao(): UserSessionCrossRefDao
|
abstract fun userSessionCrossRefDao(): UserSessionCrossRefDao
|
||||||
|
abstract fun remoteKeysDao(): RemoteKeysDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val DB_NAME: String = "pmy-db"
|
private const val DB_NAME: String = "pmy-db"
|
||||||
@ -51,11 +61,9 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
INSTANCE?.let { database ->
|
INSTANCE?.let { database ->
|
||||||
// Users
|
// Users
|
||||||
val userDao = database.userDao()
|
val userDao = database.userDao()
|
||||||
val user1 = User(1, "Login", "password")
|
val user1 = User(1, "login", "password")
|
||||||
val user2 = User(2, "Login123", "password123")
|
|
||||||
userDao.insert(user1)
|
userDao.insert(user1)
|
||||||
userDao.insert(user2)
|
/*// Cinemas
|
||||||
// Cinemas
|
|
||||||
val cinemaDao = database.cinemaDao()
|
val cinemaDao = database.cinemaDao()
|
||||||
val cinema1 =
|
val cinema1 =
|
||||||
Cinema(1, "a", "Desc1", createColoredImage(android.graphics.Color.BLUE), 2023)
|
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 userSessionCrossRef1 = UserSessionCrossRef(1, 1, 5)
|
||||||
val userSessionCrossRef2 = UserSessionCrossRef(1, 3, 15)
|
val userSessionCrossRef2 = UserSessionCrossRef(1, 3, 15)
|
||||||
userSessionCrossRefDao.insert(userSessionCrossRef1)
|
userSessionCrossRefDao.insert(userSessionCrossRef1)
|
||||||
userSessionCrossRefDao.insert(userSessionCrossRef2)
|
userSessionCrossRefDao.insert(userSessionCrossRef2)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,15 +173,15 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
return stream.toByteArray()
|
return stream.toByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRandomColorInt(): Int {
|
private fun getRandomColorInt(): Int {
|
||||||
val red = (0..255).random()
|
val red = (0..255).random()
|
||||||
val green = (0..255).random()
|
val green = (0..255).random()
|
||||||
val blue = (0..255).random()
|
val blue = (0..255).random()
|
||||||
return (0xFF shl 24) or (red shl 16) or (green shl 8) or blue
|
return (0xFF shl 24) or (red shl 16) or (green shl 8) or blue
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateCinemaName(index: Int): String {
|
private fun generateCinemaName(index: Int): String {
|
||||||
val base = 'a'.toInt()
|
val base = 'a'.code
|
||||||
val alphabetSize = 26
|
val alphabetSize = 26
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
var remainder = index
|
var remainder = index
|
||||||
|
@ -12,49 +12,49 @@ import com.example.myapplication.database.entities.composeui.edit.SessionEditVie
|
|||||||
object AppViewModelProvider {
|
object AppViewModelProvider {
|
||||||
val Factory = viewModelFactory {
|
val Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
CinemaListViewModel(cinemaApplication().container.cinemaRepository)
|
CinemaListViewModel(cinemaApplication().container.cinemaRestRepository)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
CinemaEditViewModel(
|
CinemaEditViewModel(
|
||||||
this.createSavedStateHandle(),
|
this.createSavedStateHandle(),
|
||||||
cinemaApplication().container.cinemaRepository
|
cinemaApplication().container.cinemaRestRepository
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
CinemaViewModel(
|
CinemaViewModel(
|
||||||
this.createSavedStateHandle(),
|
this.createSavedStateHandle(),
|
||||||
cinemaApplication().container.cinemaRepository,
|
cinemaApplication().container.cinemaRestRepository,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
SessionListViewModel(
|
SessionListViewModel(
|
||||||
cinemaApplication().container.sessionRepository,
|
cinemaApplication().container.sessionRestRepository,
|
||||||
cinemaApplication().container.userSessionRepository,
|
cinemaApplication().container.userSessionRestRepository,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
SessionEditViewModel(
|
SessionEditViewModel(
|
||||||
this.createSavedStateHandle(),
|
this.createSavedStateHandle(),
|
||||||
cinemaApplication().container.sessionRepository,
|
cinemaApplication().container.sessionRestRepository,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
CartViewModel(
|
CartViewModel(
|
||||||
cinemaApplication().container.userSessionRepository,
|
cinemaApplication().container.userSessionRestRepository,
|
||||||
cinemaApplication().container.orderRepository,
|
cinemaApplication().container.orderRestRepository,
|
||||||
cinemaApplication().container.orderSessionRepository,
|
cinemaApplication().container.orderSessionRestRepository,
|
||||||
cinemaApplication().container.userRepository,
|
cinemaApplication().container.userRestRepository,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
OrderListViewModel(
|
OrderListViewModel(
|
||||||
cinemaApplication().container.orderRepository,
|
cinemaApplication().container.orderRestRepository,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
OrderViewModel(
|
OrderViewModel(
|
||||||
this.createSavedStateHandle(),
|
this.createSavedStateHandle(),
|
||||||
cinemaApplication().container.orderRepository,
|
cinemaApplication().container.orderRestRepository,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package com.example.myapplication.database.entities.composeui
|
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.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.Order
|
||||||
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
||||||
import com.example.myapplication.database.entities.model.Session
|
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.OrderSessionRepository
|
||||||
import com.example.myapplication.database.entities.repository.UserRepository
|
import com.example.myapplication.database.entities.repository.UserRepository
|
||||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
|
|
||||||
class CartViewModel(
|
class CartViewModel(
|
||||||
private val userSessionRepository: UserSessionRepository,
|
private val userSessionRepository: UserSessionRepository,
|
||||||
@ -24,14 +22,13 @@ class CartViewModel(
|
|||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val userUid: Int = 1
|
private val userUid: Int = 1
|
||||||
|
var cartUiState by mutableStateOf(CartUiState())
|
||||||
|
private set
|
||||||
|
|
||||||
val cartUiState: StateFlow<CartUiState> = userRepository.getCartByUser(userUid).map {
|
suspend fun refreshState() {
|
||||||
CartUiState(it)
|
val cart = userRepository.getCartByUser(userUid)
|
||||||
}.stateIn(
|
cartUiState = CartUiState(cart)
|
||||||
scope = viewModelScope,
|
}
|
||||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
|
||||||
initialValue = CartUiState()
|
|
||||||
)
|
|
||||||
|
|
||||||
suspend fun addToOrder(userId: Int, sessions: List<SessionFromCart>) {
|
suspend fun addToOrder(userId: Int, sessions: List<SessionFromCart>) {
|
||||||
if (sessions.isEmpty())
|
if (sessions.isEmpty())
|
||||||
@ -48,10 +45,12 @@ class CartViewModel(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
userSessionRepository.deleteUserSessions(userId)
|
userSessionRepository.deleteUserSessions(userId)
|
||||||
|
refreshState()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun removeFromCart(user: Int, session: Session, count: Int = 1) {
|
suspend fun removeFromCart(user: Int, session: Session, count: Int = 1) {
|
||||||
userSessionRepository.deleteUserSession(UserSessionCrossRef(user, session.uid, count))
|
userSessionRepository.deleteUserSession(UserSessionCrossRef(user, session.uid, count))
|
||||||
|
refreshState()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateFromCart(userId: Int, session: Session, count: Int, availableCount: Int)
|
suspend fun updateFromCart(userId: Int, session: Session, count: Int, availableCount: Int)
|
||||||
@ -63,6 +62,7 @@ class CartViewModel(
|
|||||||
if (count > availableCount)
|
if (count > availableCount)
|
||||||
return false
|
return false
|
||||||
userSessionRepository.updateUserSession(UserSessionCrossRef(userId, session.uid, count))
|
userSessionRepository.updateUserSession(UserSessionCrossRef(userId, session.uid, count))
|
||||||
|
refreshState()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,17 +46,7 @@ fun CinemaList(
|
|||||||
viewModel: CinemaListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
viewModel: CinemaListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val cinemaPagingItems = viewModel.cinemaListUiState.collectAsLazyPagingItems()
|
||||||
val cinemaPagingItems = viewModel.cinemaPagerState.cinemaPagingData.collectAsLazyPagingItems()
|
|
||||||
|
|
||||||
fun findCinemas() {
|
|
||||||
coroutineScope.launch {
|
|
||||||
viewModel.findCinemas()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LaunchedEffect(1) {
|
|
||||||
findCinemas()
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {},
|
topBar = {},
|
||||||
@ -132,7 +122,6 @@ private fun CinemaList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,29 +15,9 @@ import kotlinx.coroutines.flow.emptyFlow
|
|||||||
class CinemaListViewModel(
|
class CinemaListViewModel(
|
||||||
private val cinemaRepository: CinemaRepository
|
private val cinemaRepository: CinemaRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val pagingConfig = PagingConfig(
|
val cinemaListUiState: Flow<PagingData<Cinema>> = cinemaRepository.getAllCinemas()
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun deleteCinema(cinema: Cinema) {
|
suspend fun deleteCinema(cinema: Cinema) {
|
||||||
cinemaRepository.deleteCinema(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.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
@ -38,7 +37,11 @@ fun CinemaView(
|
|||||||
navController: NavController,
|
navController: NavController,
|
||||||
viewModel: CinemaViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
viewModel: CinemaViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||||
) {
|
) {
|
||||||
val cinemaUiState by viewModel.cinemaUiState.collectAsState()
|
val cinemaUiState = viewModel.cinemaUiState
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.refreshState()
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -131,7 +134,7 @@ fun CinemaView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cinemaUiState.cinemaWithSessions != null) {
|
if (cinemaUiState.cinemaWithSessions != null) {
|
||||||
SessionList(cinemaUiState.cinemaWithSessions!!, navController)
|
SessionList(viewModel, navController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,45 @@
|
|||||||
package com.example.myapplication.database.entities.composeui
|
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.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
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.model.CinemaWithSessions
|
||||||
import com.example.myapplication.database.entities.repository.CinemaRepository
|
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(
|
class CinemaViewModel(
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle, private val cinemaRepository: CinemaRepository
|
||||||
private val cinemaRepository: CinemaRepository
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val cinemaUid: Int = checkNotNull(savedStateHandle["id"])
|
private val cinemaUid: Int = checkNotNull(savedStateHandle["id"])
|
||||||
|
|
||||||
val cinemaUiState: StateFlow<CinemaUiState> = cinemaRepository.getCinema(
|
var cinemaUiState by mutableStateOf(CinemaUiState())
|
||||||
cinemaUid
|
private set
|
||||||
).map {
|
|
||||||
CinemaUiState(it)
|
suspend fun refreshState() {
|
||||||
}.stateIn(
|
if (cinemaUid > 0) {
|
||||||
scope = viewModelScope,
|
cinemaUiState = CinemaUiState(cinemaRepository.getCinema(cinemaUid))
|
||||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
}
|
||||||
initialValue = CinemaUiState()
|
}
|
||||||
)
|
|
||||||
|
// 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)
|
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.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
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.composeui.navigation.Screen
|
||||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||||
|
|
||||||
@ -35,14 +38,17 @@ fun OrderList(
|
|||||||
viewModel: OrderListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
viewModel: OrderListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val ordersUiState by viewModel.orderListUiState.collectAsState()
|
val ordersUiState = viewModel.orderListUiState.collectAsLazyPagingItems()
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(all = 10.dp)
|
.padding(all = 10.dp)
|
||||||
) {
|
) {
|
||||||
items(ordersUiState.orderList) { order ->
|
items(count = ordersUiState.itemCount,
|
||||||
val orderId = Screen.OrderView.route.replace("{id}", order.uid.toString())
|
key = ordersUiState.itemKey(),
|
||||||
|
contentType = ordersUiState.itemContentType()) { index ->
|
||||||
|
val order = ordersUiState[index]
|
||||||
|
val orderId = Screen.OrderView.route.replace("{id}", order!!.uid.toString())
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package com.example.myapplication.database.entities.composeui
|
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.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.paging.PagingData
|
||||||
import com.example.myapplication.database.AppDataContainer
|
import com.example.myapplication.database.AppDataContainer
|
||||||
import com.example.myapplication.database.entities.model.Order
|
import com.example.myapplication.database.entities.model.Order
|
||||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class OrderListViewModel(
|
class OrderListViewModel(
|
||||||
private val orderRepository: OrderRepository
|
private val orderRepository: OrderRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val orderListUiState: StateFlow<OrderListUiState> = orderRepository.getAllOrders(1).map {
|
val orderListUiState: Flow<PagingData<Order>> = orderRepository.getAllOrders(1)
|
||||||
OrderListUiState(it)
|
|
||||||
}.stateIn(
|
|
||||||
scope = viewModelScope,
|
|
||||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
|
||||||
initialValue = OrderListUiState()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class OrderListUiState(val orderList: List<Order> = listOf())
|
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.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.myapplication.database.AppContainer
|
||||||
import com.example.myapplication.database.AppDataContainer
|
import com.example.myapplication.database.AppDataContainer
|
||||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
@ -16,11 +18,12 @@ class OrderViewModel(
|
|||||||
private val orderRepository: OrderRepository
|
private val orderRepository: OrderRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val orderUid: Int = checkNotNull(savedStateHandle["id"])
|
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)
|
OrderUiState(it)
|
||||||
}.stateIn(
|
}.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppContainer.TIMEOUT),
|
||||||
initialValue = OrderUiState()
|
initialValue = OrderUiState()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -33,17 +33,18 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.example.myapplication.R
|
import com.example.myapplication.R
|
||||||
import com.example.myapplication.composeui.navigation.Screen
|
import com.example.myapplication.composeui.navigation.Screen
|
||||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.threeten.bp.format.DateTimeFormatter
|
import org.threeten.bp.format.DateTimeFormatter
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SessionList(
|
fun SessionList(
|
||||||
cinemaWithSessions: CinemaWithSessions,
|
cinemaWithSessionsViewModel: CinemaViewModel,
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
viewModel: SessionListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
viewModel: SessionListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val cinemaWithSessions = cinemaWithSessionsViewModel.cinemaUiState.cinemaWithSessions!!
|
||||||
|
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
if (cinemaWithSessions.sessions.isEmpty()) {
|
if (cinemaWithSessions.sessions.isEmpty()) {
|
||||||
item {
|
item {
|
||||||
@ -107,7 +108,8 @@ fun SessionList(
|
|||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.addSessionInCart(sessionId = session.uid)
|
if (session.availableCount != 0)
|
||||||
|
viewModel.addSessionInCart(sessionId = session.uid)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@ -122,6 +124,7 @@ fun SessionList(
|
|||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.deleteSession(session = session)
|
viewModel.deleteSession(session = session)
|
||||||
|
cinemaWithSessionsViewModel.refreshState()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
@ -27,8 +27,6 @@ class CinemaEditViewModel(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (cinemaUid > 0) {
|
if (cinemaUid > 0) {
|
||||||
cinemaUiState = cinemaRepository.getCinema(cinemaUid)
|
cinemaUiState = cinemaRepository.getCinema(cinemaUid)
|
||||||
.filterNotNull()
|
|
||||||
.first()
|
|
||||||
.toUiState(true)
|
.toUiState(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,6 @@ class SessionEditViewModel(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (sessionUid > 0) {
|
if (sessionUid > 0) {
|
||||||
sessionUiState = sessionRepository.getSession(sessionUid)
|
sessionUiState = sessionRepository.getSession(sessionUid)
|
||||||
.filterNotNull()
|
|
||||||
.first()
|
|
||||||
.toUiState(true)
|
.toUiState(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,10 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
@Dao
|
@Dao
|
||||||
interface CinemaDao {
|
interface CinemaDao {
|
||||||
@Query("select * from cinemas order by name")
|
@Query("select * from cinemas order by name")
|
||||||
fun getAll(): Flow<List<Cinema>>
|
fun getAll(): PagingSource<Int, Cinema>
|
||||||
|
|
||||||
@Query("select * from cinemas order by name")
|
|
||||||
fun getAllCinemasPaged(): PagingSource<Int, Cinema>
|
|
||||||
|
|
||||||
@Query(
|
@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 " +
|
"FROM cinemas AS c " +
|
||||||
"LEFT JOIN sessions AS s ON s.cinema_id = c.uid " +
|
"LEFT JOIN sessions AS s ON s.cinema_id = c.uid " +
|
||||||
"LEFT JOIN orders_sessions AS os ON os.session_id = s.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>>>
|
fun getByUid(cinemaId: Int?): Flow<Map<Cinema, List<SessionFromCinema>>>
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(cinema: Cinema)
|
suspend fun insert(vararg cinema: Cinema)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(cinema: Cinema)
|
suspend fun update(cinema: Cinema)
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(cinema: Cinema)
|
suspend fun delete(cinema: Cinema)
|
||||||
|
|
||||||
|
@Query("DELETE FROM cinemas")
|
||||||
|
suspend fun deleteAll()
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.example.myapplication.database.entities.dao
|
package com.example.myapplication.database.entities.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
@ -7,12 +8,11 @@ import androidx.room.Query
|
|||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import com.example.myapplication.database.entities.model.Order
|
import com.example.myapplication.database.entities.model.Order
|
||||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface OrderDao {
|
interface OrderDao {
|
||||||
@Query("select * from orders where user_id = :userId")
|
@Query("select * from orders where user_id = :userId")
|
||||||
fun getAll(userId: Int?): Flow<List<Order>>
|
fun getAll(userId: Int?): PagingSource<Int, Order>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"SELECT o.*, s.*, os.count, os.frozen_price " +
|
"SELECT o.*, s.*, os.count, os.frozen_price " +
|
||||||
@ -21,14 +21,17 @@ interface OrderDao {
|
|||||||
"JOIN sessions AS s ON s.uid = os.session_id " +
|
"JOIN sessions AS s ON s.uid = os.session_id " +
|
||||||
"WHERE o.uid = :orderId"
|
"WHERE o.uid = :orderId"
|
||||||
)
|
)
|
||||||
fun getByUid(orderId: Int?): Flow<List<SessionFromOrder>>
|
fun getByUid(orderId: Int?): List<SessionFromOrder>
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(order: Order): Long
|
suspend fun insert(vararg order: Order): List<Long>
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(order: Order)
|
suspend fun update(order: Order)
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(order: Order)
|
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.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface OrderSessionCrossRefDao {
|
interface OrderSessionCrossRefDao {
|
||||||
@Insert
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insert(orderSessionCrossRef: OrderSessionCrossRef)
|
suspend fun insert(orderSessionCrossRef: OrderSessionCrossRef)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
@ -16,4 +18,10 @@ interface OrderSessionCrossRefDao {
|
|||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(orderSessionCrossRef: OrderSessionCrossRef)
|
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.Query
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import com.example.myapplication.database.entities.model.Session
|
import com.example.myapplication.database.entities.model.Session
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface SessionDao {
|
interface SessionDao {
|
||||||
@Query("select * from sessions where sessions.uid = :uid")
|
@Query("select * from sessions where sessions.uid = :uid")
|
||||||
fun getByUid(uid: Int): Flow<Session>
|
suspend fun getByUid(uid: Int): Session
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(session: Session)
|
suspend fun insert(vararg session: Session)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(session: Session)
|
suspend fun update(session: Session)
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(session: Session)
|
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 " +
|
"where users_sessions.user_id = :userId " +
|
||||||
"GROUP BY users_sessions.session_id "
|
"GROUP BY users_sessions.session_id "
|
||||||
)
|
)
|
||||||
fun getCartByUid(userId: Int): Flow<List<SessionFromCart>>
|
suspend fun getCartByUid(userId: Int): List<SessionFromCart>
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(user: User)
|
suspend fun insert(vararg user: User)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(user: User)
|
suspend fun update(user: User)
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(user: User)
|
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.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface UserSessionCrossRefDao {
|
interface UserSessionCrossRefDao {
|
||||||
@Insert
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insert(userSessionCrossRef: UserSessionCrossRef)
|
suspend fun insert(userSessionCrossRef: UserSessionCrossRef)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
@ -20,4 +21,7 @@ interface UserSessionCrossRefDao {
|
|||||||
|
|
||||||
@Query("DELETE FROM users_sessions where users_sessions.user_id = :userId")
|
@Query("DELETE FROM users_sessions where users_sessions.user_id = :userId")
|
||||||
suspend fun deleteByUserUid(userId: Int)
|
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 (name != other.name) return false
|
||||||
if (description != other.description) return false
|
if (description != other.description) return false
|
||||||
if (year != other.year) return false
|
if (year != other.year) return false
|
||||||
return true
|
return image.contentEquals(other.image)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
@ -12,6 +12,8 @@ data class SessionFromCinema(
|
|||||||
val price: Double,
|
val price: Double,
|
||||||
@ColumnInfo(name = "available_count")
|
@ColumnInfo(name = "available_count")
|
||||||
val availableCount: Int,
|
val availableCount: Int,
|
||||||
|
@ColumnInfo(name = "cinema_id")
|
||||||
|
val cinemaId: Int,
|
||||||
) {
|
) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@ -22,6 +24,7 @@ data class SessionFromCinema(
|
|||||||
if (dateFormatter.format(dateTime) != dateFormatter.format(other.dateTime)) return false
|
if (dateFormatter.format(dateTime) != dateFormatter.format(other.dateTime)) return false
|
||||||
if (price != other.price) return false
|
if (price != other.price) return false
|
||||||
if (availableCount != other.availableCount) return false
|
if (availableCount != other.availableCount) return false
|
||||||
|
if (cinemaId != other.cinemaId) return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +33,15 @@ data class SessionFromCinema(
|
|||||||
result = 31 * result + dateTime.hashCode()
|
result = 31 * result + dateTime.hashCode()
|
||||||
result = 31 * result + price.hashCode()
|
result = 31 * result + price.hashCode()
|
||||||
result = 31 * result + availableCount.hashCode()
|
result = 31 * result + availableCount.hashCode()
|
||||||
|
result = 31 * result + cinemaId.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun SessionFromCinema.toSession(): Session = Session (
|
||||||
|
uid,
|
||||||
|
dateTime,
|
||||||
|
price,
|
||||||
|
availableCount,
|
||||||
|
cinemaId
|
||||||
|
)
|
@ -6,14 +6,14 @@ import org.threeten.bp.LocalDateTime
|
|||||||
|
|
||||||
data class SessionFromOrder(
|
data class SessionFromOrder(
|
||||||
@ColumnInfo(name = "uid")
|
@ColumnInfo(name = "uid")
|
||||||
val uid: Int?,
|
val uid: Int = 0,
|
||||||
@ColumnInfo(name = "date_time")
|
@ColumnInfo(name = "date_time")
|
||||||
val dateTime: LocalDateTime,
|
val dateTime: LocalDateTime,
|
||||||
@ColumnInfo(name = "frozen_price")
|
@ColumnInfo(name = "frozen_price")
|
||||||
val frozenPrice: Double,
|
val frozenPrice: Double,
|
||||||
val count: Int,
|
val count: Int,
|
||||||
@ColumnInfo(name = "cinema_id")
|
@ColumnInfo(name = "cinema_id")
|
||||||
val cinemaId: Int?,
|
val cinemaId: Int = 0,
|
||||||
@Relation(
|
@Relation(
|
||||||
parentColumn = "cinema_id",
|
parentColumn = "cinema_id",
|
||||||
entity = Cinema::class,
|
entity = Cinema::class,
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package com.example.myapplication.database.entities.repository
|
package com.example.myapplication.database.entities.repository
|
||||||
|
|
||||||
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import com.example.myapplication.database.entities.model.Cinema
|
import com.example.myapplication.database.entities.model.Cinema
|
||||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface CinemaRepository {
|
interface CinemaRepository {
|
||||||
fun getAllCinemas(): Flow<List<Cinema>>
|
fun getAllCinemas(): Flow<PagingData<Cinema>>
|
||||||
fun getAllCinemasPaged(): PagingSource<Int, Cinema>
|
suspend fun getCinema(uid: Int): CinemaWithSessions
|
||||||
fun getCinema(uid: Int): Flow<CinemaWithSessions>
|
|
||||||
suspend fun insertCinema(cinema: Cinema)
|
suspend fun insertCinema(cinema: Cinema)
|
||||||
suspend fun updateCinema(cinema: Cinema)
|
suspend fun updateCinema(cinema: Cinema)
|
||||||
suspend fun deleteCinema(cinema: Cinema)
|
suspend fun deleteCinema(cinema: Cinema)
|
||||||
|
@ -1,28 +1,38 @@
|
|||||||
package com.example.myapplication.database.entities.repository
|
package com.example.myapplication.database.entities.repository
|
||||||
|
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
|
import com.example.myapplication.database.AppContainer
|
||||||
import com.example.myapplication.database.entities.dao.CinemaDao
|
import com.example.myapplication.database.entities.dao.CinemaDao
|
||||||
import com.example.myapplication.database.entities.model.Cinema
|
import com.example.myapplication.database.entities.model.Cinema
|
||||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||||
import kotlinx.coroutines.flow.Flow
|
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 {
|
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 suspend fun getCinema(uid: Int): CinemaWithSessions {
|
||||||
|
val item = cinemaDao.getByUid(uid)
|
||||||
override fun getCinema(uid: Int): Flow<CinemaWithSessions> {
|
.map { map ->
|
||||||
return flow {
|
map.firstNotNullOf {
|
||||||
cinemaDao.getByUid(uid).collect {
|
|
||||||
emit(it.firstNotNullOf {
|
|
||||||
CinemaWithSessions(
|
CinemaWithSessions(
|
||||||
cinema = it.key,
|
cinema = it.key,
|
||||||
sessions = it.value
|
sessions = it.value
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
.first()
|
||||||
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun insertCinema(cinema: Cinema) = cinemaDao.insert(cinema)
|
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 updateCinema(cinema: Cinema) = cinemaDao.update(cinema)
|
||||||
|
|
||||||
override suspend fun deleteCinema(cinema: Cinema) = cinemaDao.delete(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
|
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.dao.OrderDao
|
||||||
import com.example.myapplication.database.entities.model.Order
|
import com.example.myapplication.database.entities.model.Order
|
||||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class OfflineOrderRepository(private val orderDao: OrderDao) : OrderRepository {
|
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) =
|
override suspend fun deleteOrderSession(orderSessionCrossRef: OrderSessionCrossRef) =
|
||||||
orderSessionDao.delete(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.dao.SessionDao
|
||||||
import com.example.myapplication.database.entities.model.Session
|
import com.example.myapplication.database.entities.model.Session
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
class OfflineSessionRepository(private val sessionDao: SessionDao) : SessionRepository {
|
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 insertSession(session: Session) = sessionDao.insert(session)
|
||||||
|
|
||||||
override suspend fun updateSession(session: Session) = sessionDao.update(session)
|
override suspend fun updateSession(session: Session) = sessionDao.update(session)
|
||||||
|
|
||||||
override suspend fun deleteSession(session: Session) = sessionDao.delete(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 {
|
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
||||||
override fun getAllUsers(): Flow<List<User>> = userDao.getAll()
|
override fun getAllUsers(): Flow<List<User>> = userDao.getAll()
|
||||||
|
|
||||||
override fun getCartByUser(userId: Int): Flow<List<SessionFromCart>> =
|
override suspend fun getCartByUser(userId: Int): List<SessionFromCart> =
|
||||||
userDao.getCartByUid(userId)
|
userDao.getCartByUid(userId)
|
||||||
|
|
||||||
override suspend fun insertUser(user: User) = userDao.insert(user)
|
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 updateUser(user: User) = userDao.update(user)
|
||||||
|
|
||||||
override suspend fun deleteUser(user: User) = userDao.delete(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)
|
userSessionDao.delete(userSessionCrossRef)
|
||||||
|
|
||||||
override suspend fun deleteUserSessions(userId: Int) = userSessionDao.deleteByUserUid(userId)
|
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
|
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.Order
|
||||||
import com.example.myapplication.database.entities.model.SessionFromOrder
|
import com.example.myapplication.database.entities.model.SessionFromOrder
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface OrderRepository {
|
interface OrderRepository {
|
||||||
fun getAllOrders(userId: Int?): Flow<List<Order>>
|
fun getAllOrders(userId: Int?): Flow<PagingData<Order>>
|
||||||
fun getOrder(orderId: Int?): Flow<List<SessionFromOrder>>
|
suspend fun getOrder(uid: Int): List<SessionFromOrder>
|
||||||
suspend fun insertOrder(order: Order): Long
|
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
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface SessionRepository {
|
interface SessionRepository {
|
||||||
fun getSession(uid: Int): Flow<Session?>
|
suspend fun getSession(uid: Int): Session
|
||||||
suspend fun insertSession(session: Session)
|
suspend fun insertSession(session: Session)
|
||||||
suspend fun updateSession(session: Session)
|
suspend fun updateSession(session: Session)
|
||||||
suspend fun deleteSession(session: Session)
|
suspend fun deleteSession(session: Session)
|
||||||
|
@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
|
|
||||||
interface UserRepository {
|
interface UserRepository {
|
||||||
fun getAllUsers(): Flow<List<User>>
|
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 insertUser(user: User)
|
||||||
suspend fun updateUser(user: User)
|
suspend fun updateUser(user: User)
|
||||||
suspend fun deleteUser(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("com.android.application") version "8.1.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.8.20" 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("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