diff --git a/app/src/main/java/com/example/testapp/MainActivity.kt b/app/src/main/java/com/example/testapp/MainActivity.kt index 8f17288..34a39dd 100644 --- a/app/src/main/java/com/example/testapp/MainActivity.kt +++ b/app/src/main/java/com/example/testapp/MainActivity.kt @@ -6,9 +6,12 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.annotation.RequiresApi +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.rememberNavController +import com.example.testapp.designElem.SharedViewModel import com.example.testapp.graphs.RootNavigationGraph import com.example.testapp.ui.theme.TestAppTheme +import com.example.testapp.viewModels.AppViewModelProvider import com.example.testapp.viewModels.CurrentUserViewModel class MainActivity : ComponentActivity() { @@ -22,7 +25,8 @@ class MainActivity : ComponentActivity() { TestAppTheme { RootNavigationGraph( navController = rememberNavController(), - currentUserViewModel = currentUserViewModel + currentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), + sharedViewModel = SharedViewModel() ) } } diff --git a/app/src/main/java/com/example/testapp/designElem/ListItem.kt b/app/src/main/java/com/example/testapp/designElem/ListItem.kt index 6aa27b1..ec8bc45 100644 --- a/app/src/main/java/com/example/testapp/designElem/ListItem.kt +++ b/app/src/main/java/com/example/testapp/designElem/ListItem.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.layout.requiredWidth @@ -33,8 +35,8 @@ fun ListItem(modifier: Modifier = Modifier, coin : Coin, count: Float) { horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = modifier - .requiredWidth(width = 340.dp) - .requiredHeight(height = 36.dp) + .fillMaxWidth() + .fillMaxHeight() .clip(shape = RoundedCornerShape(8.dp)) .background(color = Color.White) .padding(end = 12.dp, diff --git a/app/src/main/java/com/example/testapp/designElem/NavBar.kt b/app/src/main/java/com/example/testapp/designElem/NavBar.kt index 169afb6..03be17e 100644 --- a/app/src/main/java/com/example/testapp/designElem/NavBar.kt +++ b/app/src/main/java/com/example/testapp/designElem/NavBar.kt @@ -84,7 +84,7 @@ fun AddItem( .clickable(onClick = { navController.navigate(screen.route) { popUpTo(navController.graph.findStartDestination().id) - launchSingleTop = true + launchSingleTop = false } }) .clip(shape = RoundedCornerShape(10.dp)) diff --git a/app/src/main/java/com/example/testapp/designElem/SharedViewModel.kt b/app/src/main/java/com/example/testapp/designElem/SharedViewModel.kt index 85c3274..1f5e737 100644 --- a/app/src/main/java/com/example/testapp/designElem/SharedViewModel.kt +++ b/app/src/main/java/com/example/testapp/designElem/SharedViewModel.kt @@ -14,7 +14,7 @@ class SharedViewModel : ViewModel() { argument_add_c.value = arg2 } - fun setArgumentEdit(arg: Any) { + fun setArgumentEdit(arg: Any?) { argument_edit.value = arg } diff --git a/app/src/main/java/com/example/testapp/graphs/HomeNavGraph.kt b/app/src/main/java/com/example/testapp/graphs/HomeNavGraph.kt index 1918efd..cd7d957 100644 --- a/app/src/main/java/com/example/testapp/graphs/HomeNavGraph.kt +++ b/app/src/main/java/com/example/testapp/graphs/HomeNavGraph.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import com.example.testapp.designElem.SharedViewModel import com.example.testapp.navigate.BottomBarScreen import com.example.testapp.screensMobile.AccountPage import com.example.testapp.screensMobile.CreateDeal @@ -27,7 +28,8 @@ fun HomeNavGraph( dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory), dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory), historyViewModel: HistoryViewModel = viewModel(factory = AppViewModelProvider.Factory), - walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory) + walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory), + sharedViewModel: SharedViewModel ) { NavHost( navController = navController, @@ -51,14 +53,16 @@ fun HomeNavGraph( DealList( navController = navController, currentUserViewModel = currentUserViewModel, - dealListViewModel = dealListViewModel + dealListViewModel = dealListViewModel, + sharedViewModel = sharedViewModel ) } composable(route = BottomBarScreen.CDEAL.route) { CreateDeal( navController = navController, currentUserViewModel = currentUserViewModel, - dealCreateViewModel = dealCreateViewModel + dealCreateViewModel = dealCreateViewModel, + sharedViewModel = sharedViewModel ) } composable(route = BottomBarScreen.History.route) { diff --git a/app/src/main/java/com/example/testapp/graphs/RootNavGraph.kt b/app/src/main/java/com/example/testapp/graphs/RootNavGraph.kt index 18a5855..3450123 100644 --- a/app/src/main/java/com/example/testapp/graphs/RootNavGraph.kt +++ b/app/src/main/java/com/example/testapp/graphs/RootNavGraph.kt @@ -8,6 +8,7 @@ import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument +import com.example.testapp.designElem.SharedViewModel import com.example.testapp.screensMobile.LoadScreen import com.example.testapp.viewModels.AppViewModelProvider import com.example.testapp.viewModels.CurrentUserViewModel @@ -30,7 +31,8 @@ fun RootNavigationGraph( entryScreenViewModel: EntryScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), historyViewModel: HistoryViewModel = viewModel(factory = AppViewModelProvider.Factory), registrationScreenViewModel: RegistrationScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), - walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory) + walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory), + sharedViewModel: SharedViewModel ) { NavHost( navController = navController, @@ -54,7 +56,8 @@ fun RootNavigationGraph( dealCreateViewModel = dealCreateViewModel, dealListViewModel = dealListViewModel, historyViewModel = historyViewModel, - walletViewModel = walletViewModel + walletViewModel = walletViewModel, + sharedViewModel = sharedViewModel ) } } diff --git a/app/src/main/java/com/example/testapp/room/dao/DealDao.kt b/app/src/main/java/com/example/testapp/room/dao/DealDao.kt index cf190ca..2391e40 100644 --- a/app/src/main/java/com/example/testapp/room/dao/DealDao.kt +++ b/app/src/main/java/com/example/testapp/room/dao/DealDao.kt @@ -3,6 +3,7 @@ package com.example.testapp.room.dao import androidx.room.Dao import androidx.room.Query import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.User import kotlinx.coroutines.flow.Flow @Dao @@ -10,7 +11,10 @@ interface DealDao : IDao { @Query("select * from deal") fun getAll(): Flow> - @Query("select * from deal ORDER BY id DESC LIMIT :limit OFFSET :offset") + @Query("select * from deal where deal.id = :id") + fun getById(id: Int): Deal + + @Query("select * from deal where deal.date IS NULL ORDER BY id DESC LIMIT :limit OFFSET :offset") fun getAll(limit: Int, offset: Int): List @Query("select * from deal where deal.buyerId = :idUser OR deal.sellerId = :idUser") diff --git a/app/src/main/java/com/example/testapp/room/dao/UserDao.kt b/app/src/main/java/com/example/testapp/room/dao/UserDao.kt index 1dc7c23..db15d73 100644 --- a/app/src/main/java/com/example/testapp/room/dao/UserDao.kt +++ b/app/src/main/java/com/example/testapp/room/dao/UserDao.kt @@ -13,7 +13,7 @@ interface UserDao : IDao { fun getAll(): Flow> @Query("select * from user where user.id = :idUser") - fun getUserById(idUser: Int): Flow + fun getUserById(idUser: Int): User @Query("select * from walletitem where walletitem.userId = :idUser") fun getUserWallet(idUser: Int): Flow> diff --git a/app/src/main/java/com/example/testapp/room/dao/WalletItemDao.kt b/app/src/main/java/com/example/testapp/room/dao/WalletItemDao.kt index 95f7a25..de8072d 100644 --- a/app/src/main/java/com/example/testapp/room/dao/WalletItemDao.kt +++ b/app/src/main/java/com/example/testapp/room/dao/WalletItemDao.kt @@ -2,6 +2,7 @@ package com.example.testapp.room.dao import androidx.room.Dao import androidx.room.Query +import com.example.testapp.room.models.Deal import com.example.testapp.room.models.WalletItem import kotlinx.coroutines.flow.Flow @@ -10,6 +11,9 @@ interface WalletItemDao : IDao { @Query("select * from walletitem") fun getAll(): Flow> + @Query("select * from walletitem where walletitem.userId = :id ORDER BY walletitem.coinId DESC LIMIT :limit OFFSET :offset") + fun getAll(limit: Int, offset: Int, id: Int): List + @Query("select * from walletitem where walletitem.userId = :id") fun getUserWallet(id: Int): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/database/CryptoDealDb.kt b/app/src/main/java/com/example/testapp/room/database/CryptoDealDb.kt index 1795a14..d0ffc3d 100644 --- a/app/src/main/java/com/example/testapp/room/database/CryptoDealDb.kt +++ b/app/src/main/java/com/example/testapp/room/database/CryptoDealDb.kt @@ -22,7 +22,7 @@ abstract class CryptoDealDb: RoomDatabase() { abstract fun walletItemDao(): WalletItemDao; companion object { - private const val DB_NAME: String = "crypto-deal2" + private const val DB_NAME: String = "crypto-deal7" @Volatile private var INSTANCE: CryptoDealDb? = null @@ -41,10 +41,16 @@ abstract class CryptoDealDb: RoomDatabase() { val c2 = Coin(1, "Edhereum"); val c3 = Coin(2, "CatCoin"); val c4 = Coin(3, "BuzCoin"); + val c5 = Coin(4, "LunaCoin"); + val c6 = Coin(5, "HopeCoin"); + val c7 = Coin(6, "DyrovCoin") coinDao.insert(c1); coinDao.insert(c2) coinDao.insert(c3) coinDao.insert(c4) + coinDao.insert(c5) + coinDao.insert(c6) + coinDao.insert(c7) val walletItemDao = database.walletItemDao(); val wi1 = WalletItem(0, 0, 0.5f); @@ -53,18 +59,28 @@ abstract class CryptoDealDb: RoomDatabase() { val wi4 = WalletItem(0, 1, 1000f); val wi5 = WalletItem(3, 1, 10f); val wi6 = WalletItem(2, 1, 1f); + val wi7 = WalletItem(4, 0, 1.1f); + val wi8 = WalletItem(5, 0, 1.1f); + val wi9 = WalletItem(6, 0, 1.1f); walletItemDao.insert(wi1); walletItemDao.insert(wi2); walletItemDao.insert(wi3); walletItemDao.insert(wi4); walletItemDao.insert(wi5); walletItemDao.insert(wi6); + walletItemDao.insert(wi7); + walletItemDao.insert(wi8); + walletItemDao.insert(wi9); val dealDao = database.dealDao(); val d1 = Deal(0, null, 0, 2, 0, 0.1f, 0.2f, "Buy", null, "TEST1") val d2 = Deal(1, null, 0, 0, 2, 0.1f, 0.2f, "Buy", null, "TEST2") + val d3 = Deal(2, null, 0, 2, 0, 0.1f, 0.2f, "Buy", null, "TEST3") + val d4 = Deal(3, null, 0, 0, 2, 0.1f, 0.2f, "Buy", null, "TEST4") dealDao.insert(d1); dealDao.insert(d2); + dealDao.insert(d3); + dealDao.insert(d4); } } @@ -83,6 +99,7 @@ abstract class CryptoDealDb: RoomDatabase() { } } }) + .allowMainThreadQueries() .build() .also { INSTANCE = it } } diff --git a/app/src/main/java/com/example/testapp/room/models/Deal.kt b/app/src/main/java/com/example/testapp/room/models/Deal.kt index cebb04b..4313989 100644 --- a/app/src/main/java/com/example/testapp/room/models/Deal.kt +++ b/app/src/main/java/com/example/testapp/room/models/Deal.kt @@ -8,7 +8,7 @@ import java.time.LocalDateTime class Deal ( @PrimaryKey(autoGenerate = true) val id: Int?, - val sellerId: Int?, + var sellerId: Int?, val buyerId: Int?, val sellerCoinId: Int, val buyerCoinId: Int, diff --git a/app/src/main/java/com/example/testapp/room/models/WalletItem.kt b/app/src/main/java/com/example/testapp/room/models/WalletItem.kt index 41cc486..a70437b 100644 --- a/app/src/main/java/com/example/testapp/room/models/WalletItem.kt +++ b/app/src/main/java/com/example/testapp/room/models/WalletItem.kt @@ -2,7 +2,6 @@ package com.example.testapp.room.models import androidx.room.Entity import androidx.room.ForeignKey -import androidx.room.PrimaryKey @Entity( primaryKeys = ["coinId", "userId"], @@ -14,5 +13,6 @@ import androidx.room.PrimaryKey class WalletItem ( val coinId: Int, val userId: Int, - val count: Float + var count: Float, +// val freeze: Float ) \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/pagination/DealPagination.kt b/app/src/main/java/com/example/testapp/room/pagination/DealPagination.kt index f04ff85..aa36e97 100644 --- a/app/src/main/java/com/example/testapp/room/pagination/DealPagination.kt +++ b/app/src/main/java/com/example/testapp/room/pagination/DealPagination.kt @@ -14,7 +14,6 @@ class DealPagination( val page = params.key ?: 0 return try { - Log.d("MainPagingSource", "load: $page") val entities = dao.getAll(params.loadSize, page * params.loadSize) if (page != 0) delay(1000) LoadResult.Page( diff --git a/app/src/main/java/com/example/testapp/room/pagination/WalletPagination.kt b/app/src/main/java/com/example/testapp/room/pagination/WalletPagination.kt new file mode 100644 index 0000000..808f953 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/pagination/WalletPagination.kt @@ -0,0 +1,40 @@ +package com.example.testapp.room.pagination + +import android.util.Log +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.example.testapp.room.dao.WalletItemDao +import com.example.testapp.room.models.WalletItem +import com.example.testapp.room.repository.basic.WalletItemRepository +import kotlinx.coroutines.delay + +class WalletPagination( + private val dao: WalletItemDao, + private val id: Int +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + val page = params.key ?: 0 + + return try { + Log.d("MainPagingSource", "load: $page") + val entities = dao.getAll(params.loadSize, page * params.loadSize, id) + if (page != 0) delay(1000) + LoadResult.Page( + data = entities, + prevKey = if (page == 0) null else page - 1, + nextKey = if (entities.isEmpty()) null else page + 1 + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } + + override val jumpingSupported: Boolean = true + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/basic/DealRepository.kt b/app/src/main/java/com/example/testapp/room/repository/basic/DealRepository.kt index e8569dc..c579f8d 100644 --- a/app/src/main/java/com/example/testapp/room/repository/basic/DealRepository.kt +++ b/app/src/main/java/com/example/testapp/room/repository/basic/DealRepository.kt @@ -1,9 +1,12 @@ package com.example.testapp.room.repository.basic +import androidx.paging.PagingData import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.WalletItem import kotlinx.coroutines.flow.Flow interface DealRepository : IRepository { fun getUserDeals(id: Int): Flow> fun completeDeal(deal: Deal, sellerId: Int, buyerId: Int): Boolean + fun pagingData(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/basic/IRepository.kt b/app/src/main/java/com/example/testapp/room/repository/basic/IRepository.kt index faf188e..4efa420 100644 --- a/app/src/main/java/com/example/testapp/room/repository/basic/IRepository.kt +++ b/app/src/main/java/com/example/testapp/room/repository/basic/IRepository.kt @@ -6,7 +6,6 @@ interface IRepository { suspend fun insert(x: T) suspend fun update(x: T) suspend fun delete(x: T) + fun getById(id: Int): T fun getAll(): Flow> - fun getById(id: Int): Flow - fun getAll(limit: Int, offset: Int): List } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/basic/WalletItemRepository.kt b/app/src/main/java/com/example/testapp/room/repository/basic/WalletItemRepository.kt index 31f7937..b595974 100644 --- a/app/src/main/java/com/example/testapp/room/repository/basic/WalletItemRepository.kt +++ b/app/src/main/java/com/example/testapp/room/repository/basic/WalletItemRepository.kt @@ -1,8 +1,13 @@ package com.example.testapp.room.repository.basic +import androidx.lifecycle.LiveData +import androidx.paging.PagingData +import com.example.testapp.room.models.Deal import com.example.testapp.room.models.WalletItem import kotlinx.coroutines.flow.Flow interface WalletItemRepository : IRepository { fun getUserWallet(id: Int): Flow> + fun pagingData(id: Int): Flow> + fun pagingDataTest(id: Int): LiveData> } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineCoinRepository.kt b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineCoinRepository.kt index 864cd79..7d867be 100644 --- a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineCoinRepository.kt +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineCoinRepository.kt @@ -1,7 +1,14 @@ package com.example.testapp.room.repository.offline +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import com.example.testapp.room.dao.CoinDao import com.example.testapp.room.models.Coin +import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.WalletItem +import com.example.testapp.room.pagination.DealPagination +import com.example.testapp.room.pagination.WalletPagination import com.example.testapp.room.repository.basic.CoinRepository import kotlinx.coroutines.flow.Flow @@ -9,6 +16,5 @@ class OfflineCoinRepository( private val coinDao: CoinDao ) : OfflineRepository(coinDao), CoinRepository { override fun getAll(): Flow> = coinDao.getAll() - override fun getAll(limit: Int, offset: Int): List = throw NotImplementedError() - override fun getById(id: Int): Flow = throw NotImplementedError() + override fun getById(id: Int): Coin = throw NotImplementedError() } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineDealRepository.kt b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineDealRepository.kt index b7eac54..9896e5d 100644 --- a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineDealRepository.kt +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineDealRepository.kt @@ -1,7 +1,13 @@ package com.example.testapp.room.repository.offline +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import com.example.testapp.room.dao.DealDao import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.WalletItem +import com.example.testapp.room.pagination.DealPagination +import com.example.testapp.room.pagination.WalletPagination import com.example.testapp.room.repository.basic.DealRepository import kotlinx.coroutines.flow.Flow @@ -9,35 +15,14 @@ class OfflineDealRepository( private val dealDao: DealDao ) : OfflineRepository(dealDao), DealRepository { override fun getAll(): Flow> = dealDao.getAll() - override fun getAll(limit: Int, offset: Int) = dealDao.getAll(limit, offset) - override fun getById(id: Int): Flow = throw NotImplementedError() + override fun getById(id: Int): Deal = dealDao.getById(id); override fun getUserDeals(id: Int): Flow> = dealDao.getUserDeals(id) override fun completeDeal(deal: Deal, sellerId: Int, buyerId: Int): Boolean { - // val userRepository = UserRepository() -// val buyer = deal.buyerId?.let { userRepository.getById(it) } -// val seller = userRepository.getById(id) -// -// val coin = seller.wallet[deal.sellerCoin] -// if (coin != null && coin >= deal.countSell) { -// if (buyer != null) { -// if (buyer.wallet.containsKey(deal.sellerCoin)) -// buyer.wallet[deal.sellerCoin] = buyer.wallet[deal.sellerCoin]!! + deal.countSell -// else buyer.wallet[deal.sellerCoin] = deal.countSell -// buyer.wallet[deal.buyerCoin] = buyer.wallet[deal.buyerCoin]!! - deal.countBuy -// } -// seller.wallet[deal.sellerCoin] = seller.wallet[deal.sellerCoin]!! - deal.countSell -// if (seller.wallet.containsKey(deal.buyerCoin)) -// seller.wallet[deal.buyerCoin] = seller.wallet[deal.buyerCoin]!! + deal.countBuy -// else seller.wallet[deal.buyerCoin] = deal.countSell -// } -// deal.date = LocalDateTime.now() -// deal.sellerId = id -// DealRepository().update(deal) -// navController.navigate(BottomBarScreen.Deals.route) { -// popUpTo(navController.graph.findStartDestination().id) -// launchSingleTop = true -// } return true; } + + override fun pagingData(): Flow> = Pager(config = PagingConfig( + pageSize = 4, jumpThreshold = 4, initialLoadSize = 4 + ), pagingSourceFactory = { DealPagination(dealDao) }, initialKey = 1).flow } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineUserRepository.kt b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineUserRepository.kt index cd3513d..9039da8 100644 --- a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineUserRepository.kt +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineUserRepository.kt @@ -11,8 +11,7 @@ class OfflineUserRepository( private val userDao: UserDao ) : OfflineRepository(userDao), UserRepository { override fun getAll(): Flow> = userDao.getAll() - override fun getAll(limit: Int, offset: Int) = throw NotImplementedError() - override fun getById(id: Int): Flow = userDao.getUserById(id) + override fun getById(id: Int): User = userDao.getUserById(id) override fun getUserWallet(id: Int): Flow> = userDao.getUserWallet(id) override fun getUserDeals(id: Int): Flow> = userDao.getUserDeals(id) } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineWalletItemRepository.kt b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineWalletItemRepository.kt index aff64d5..d7e2d7f 100644 --- a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineWalletItemRepository.kt +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineWalletItemRepository.kt @@ -1,7 +1,14 @@ package com.example.testapp.room.repository.offline +import androidx.lifecycle.LiveData +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.liveData import com.example.testapp.room.dao.WalletItemDao +import com.example.testapp.room.models.Deal import com.example.testapp.room.models.WalletItem +import com.example.testapp.room.pagination.WalletPagination import com.example.testapp.room.repository.basic.WalletItemRepository import kotlinx.coroutines.flow.Flow @@ -10,6 +17,13 @@ class OfflineWalletItemRepository( ) : OfflineRepository(walletItemDao), WalletItemRepository { override fun getUserWallet(id: Int): Flow> = walletItemDao.getUserWallet(id) override fun getAll(): Flow> = walletItemDao.getAll() - override fun getAll(limit: Int, offset: Int): List = throw NotImplementedError() - override fun getById(id: Int): Flow = throw NotImplementedError() + override fun getById(id: Int): WalletItem = throw NotImplementedError() + + override fun pagingData(id: Int): Flow> = Pager(config = PagingConfig( + pageSize = 10, jumpThreshold = 10, initialLoadSize = 10 + ), pagingSourceFactory = { WalletPagination(walletItemDao, id) }, initialKey = 1).flow + + override fun pagingDataTest(id: Int): LiveData> = Pager(config = PagingConfig( + pageSize = 1, jumpThreshold = 1, initialLoadSize = 1 + ), pagingSourceFactory = { WalletPagination(walletItemDao, id) }, initialKey = 1).liveData } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/screensMobile/AccountPage.kt b/app/src/main/java/com/example/testapp/screensMobile/AccountPage.kt index a10a36f..b5e01ba 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/AccountPage.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/AccountPage.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -47,8 +48,7 @@ fun AccountPage( navController: NavController, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory) ) { - val user by remember { mutableStateOf(currentUserViewModel.user) } - val coroutineScope = rememberCoroutineScope() + val user by currentUserViewModel.user.collectAsState() Box( modifier = modifier diff --git a/app/src/main/java/com/example/testapp/screensMobile/CreateDeal.kt b/app/src/main/java/com/example/testapp/screensMobile/CreateDeal.kt index 939c374..760ee27 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/CreateDeal.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/CreateDeal.kt @@ -21,11 +21,11 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -41,8 +41,11 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import com.example.testapp.designElem.DropDown import com.example.testapp.designElem.DropDownConfig +import com.example.testapp.designElem.SharedViewModel import com.example.testapp.navigate.BottomBarScreen +import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.WalletItem import com.example.testapp.viewModels.AppViewModelProvider import com.example.testapp.viewModels.CurrentUserViewModel import com.example.testapp.viewModels.DealCreateViewModel @@ -57,37 +60,60 @@ fun CreateDeal( modifier: Modifier = Modifier, navController: NavHostController, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), - dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory) + dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory), + sharedViewModel: SharedViewModel ) { - dealCreateViewModel.setupLists(currentUserViewModel.user!!.id.toString()) + val user by currentUserViewModel.user.collectAsState() - val isEdit = dealCreateViewModel.isEdit() - val editDeal: Deal? = dealCreateViewModel.deal - val id = if (isEdit) editDeal?.buyerId else currentUserViewModel.user!!.id + val editDeal = sharedViewModel.argument_edit.value as Deal? + val isEdit = editDeal != null + val id = if (isEdit) editDeal?.buyerId else user!!.id - val coins = mutableStateOf(dealCreateViewModel.coins) - val wallet = mutableStateOf(dealCreateViewModel.wallet) + val coins by dealCreateViewModel.coins.collectAsState() + val wallet by dealCreateViewModel.wallet.collectAsState() - - val coinsBuyer = wallet.value.filter { x -> x.userId == id }.toList() - var coinBuyer by rememberSaveable { - mutableStateOf(if (isEdit) coins.value.first { y -> y.id == editDeal?.buyerCoinId }.name else coinsBuyer[0]) + val coinsBuyer = wallet.filter { x -> x.userId == id }.toList() + var coinBuyer by remember { + mutableStateOf( + if (isEdit) coinsBuyer.first { y -> y.coinId == editDeal?.buyerCoinId } + else coinsBuyer[0] + ) } var buyCount by remember { - mutableFloatStateOf(if (isEdit) editDeal?.countBuy!! else 0f) - } - var tip by remember { - mutableStateOf(if (isEdit) editDeal?.tip else "") + mutableFloatStateOf( + if (isEdit) editDeal?.countBuy!! + else 0f + ) } - val coinsSeller = - wallet.value.map { x -> coins.value.first { y -> y.id == x.coinId }.name }.toList() - var coinSeller by rememberSaveable { - mutableStateOf(if (isEdit) coins.value.first { y -> y.id == editDeal?.sellerCoinId }.name else coinsSeller[0]) + val coinsSeller = coins + var coinSeller by remember { + mutableStateOf( + if (isEdit) coins.first { y -> y.id == editDeal?.sellerCoinId } + else coinsSeller[0] + ) } var sellCount by remember { - mutableFloatStateOf(if (isEdit) editDeal?.countSell!! else 0f) + mutableFloatStateOf( + if (isEdit) editDeal?.countSell!! + else 0f + ) + } + + var tip by remember { + mutableStateOf( + if (isEdit) editDeal?.tip + else "" + ) + } + + val dictionaryName: Map = coins.associate { x -> + x.name to coinsBuyer.firstOrNull { y -> y.coinId == x.id } + } + + val dictionaryId: Map = coins.associate { x -> + x.id to x.name } @@ -126,15 +152,18 @@ fun CreateDeal( ) Text( text = "-", - color = Color(0xff4bb2f9), + color = Color(0xFFF94B4B), textAlign = TextAlign.Center, lineHeight = 0.83.em, style = TextStyle( - fontSize = 24.sp, + fontSize = 20.sp, letterSpacing = 0.1.sp ), modifier = Modifier .clickable { + CoroutineScope(Dispatchers.IO).launch { + dealCreateViewModel.delete(editDeal!!) + } navController.navigate(route = BottomBarScreen.Deals.route) } .wrapContentHeight(align = Alignment.CenterVertically)) @@ -157,11 +186,10 @@ fun CreateDeal( DropDown( modifier = Modifier.padding(horizontal = 10.dp), downConfig = DropDownConfig( - values = coinsBuyer.map { x -> coins.value.first { y -> y.id == x.coinId }.name } - .toList(), + values = dictionaryName.filter { x -> x.value != null }.keys.toList(), title = "Select coin", - onValueChange = { x -> coinBuyer = x }, - selected = coinBuyer as String + onValueChange = { x -> coinBuyer = dictionaryName[x]!! }, + selected = dictionaryId[(coinBuyer as WalletItem).coinId] ) ) } @@ -169,15 +197,14 @@ fun CreateDeal( OutlinedTextField( value = buyCount.toString(), onValueChange = { - val value = - coinsBuyer.first { x -> coinBuyer == coins.value.first { y -> y.id == x.coinId } } + val value = (coinBuyer as WalletItem) if (it.toFloatOrNull() != null && it.toFloat() <= value.count) { buyCount = it.toFloat() } }, label = { Text( - text = "Fill count 0 -> ${coinsBuyer.first { x -> coinBuyer == coins.value.first { y -> y.id == x.coinId } }.count}", + text = "Fill count 0 -> ${(coinBuyer as WalletItem).count}", color = Color.Black, textAlign = TextAlign.Center, lineHeight = 1.43.em, @@ -243,10 +270,10 @@ fun CreateDeal( DropDown( modifier = Modifier.padding(horizontal = 10.dp), downConfig = DropDownConfig( - values = coinsSeller.filter { x -> x != (coinBuyer as String) }, + values = dictionaryName.keys.toList(), title = "Select coin", - onValueChange = { x -> coinSeller = x }, - selected = coinSeller + onValueChange = { x -> coinSeller = coins.first { y -> y.name == x } }, + selected = dictionaryId[(coinSeller as Coin).id] ) ) } @@ -285,9 +312,9 @@ fun CreateDeal( id = editDeal?.id, sellerId = editDeal?.sellerId, buyerId = id, - buyerCoinId = coins.value.first { x -> x.name == coinBuyer }.id!!, + buyerCoinId = coinBuyer.coinId, countBuy = buyCount, - sellerCoinId = coins.value.first { x -> x.name == coinSeller }.id!!, + sellerCoinId = coinSeller.id!!, countSell = sellCount, tip = tip!!, operation = "Buy", @@ -296,14 +323,16 @@ fun CreateDeal( CoroutineScope(Dispatchers.IO).launch { dealCreateViewModel.update(deal) } + sharedViewModel.setArgumentEdit(null); } else { + if (buyCount == 0f || sellCount == 0f) return@Button; val deal = Deal( id = null, sellerId = null, buyerId = id, - buyerCoinId = coins.value.first { x -> x.name == coinBuyer }.id!!, + buyerCoinId = coinBuyer.coinId, countBuy = buyCount, - sellerCoinId = coins.value.first { x -> x.name == coinSeller }.id!!, + sellerCoinId = coinSeller.id!!, countSell = sellCount, tip = tip!!, operation = "Buy", @@ -358,6 +387,7 @@ fun CreateDeal( item { Button( onClick = { + sharedViewModel.setArgumentEdit(null); navController.navigate(route = BottomBarScreen.Deals.route) }, shape = RoundedCornerShape(10.dp), diff --git a/app/src/main/java/com/example/testapp/screensMobile/DealList.kt b/app/src/main/java/com/example/testapp/screensMobile/DealList.kt index 4dfb794..5607c0a 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/DealList.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/DealList.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -19,11 +20,12 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -39,6 +41,9 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems +import com.example.testapp.designElem.SharedViewModel import com.example.testapp.navigate.BottomBarScreen import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal @@ -54,18 +59,20 @@ fun DealList( navController: NavHostController, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory), - dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory) + dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory), + sharedViewModel: SharedViewModel ) { - val id = currentUserViewModel.user!!.id + val user by currentUserViewModel.user.collectAsState() + val id = user!!.id dealListViewModel.setupLists() - val deals = remember { mutableStateOf(dealListViewModel.deals) } - val coins = remember { mutableStateOf(dealListViewModel.coins) } - + val deals = dealListViewModel.deals.collectAsLazyPagingItems() + val coins by dealListViewModel.coins.collectAsState() LazyColumn( verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), horizontalAlignment = Alignment.CenterHorizontally, + contentPadding = PaddingValues(bottom = 70.dp), modifier = modifier .background(color = Color(0xfff4f7fb)) .padding(vertical = 20.dp) @@ -74,19 +81,43 @@ fun DealList( item { PropertyDeal(navController = navController) } - item { - deals.value.filter { x -> x.date == null }.forEach { x -> + items(count = deals.itemCount) { x -> + val deal = deals[x] + if (deal != null) { DealItem( - deal = x, - coins = coins.value, + deal = deal, + coins = coins, modifier = Modifier.padding(vertical = 5.dp), id = id, navController = navController, dealCreateViewModel = dealCreateViewModel, - dealListViewModel = dealListViewModel + dealListViewModel = dealListViewModel, + sharedViewModel = sharedViewModel ) } } + deals.apply { + when{ + loadState.refresh is LoadState.Loading -> { + item { CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(0.5f), + color = Color(0xff423a99)) } + } + loadState.append is LoadState.Loading -> { + item { CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(0.5f), + color = Color(0xff423a99)) } + } + loadState.refresh is LoadState.Error -> { + val err = deals.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + loadState.append is LoadState.Error -> { + val err = deals.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } + } + } } } @@ -99,7 +130,8 @@ fun DealItem( id: Int?, navController: NavHostController, dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory), - dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory) + dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory), + sharedViewModel: SharedViewModel ) { Column( verticalArrangement = Arrangement.spacedBy(10.dp, Alignment.Top), @@ -195,16 +227,22 @@ fun DealItem( onClick = { if (id != null && id != deal.buyerId) { deal.buyerId?.let { dealListViewModel.submitDeal(deal, id, it) } + navController.navigate(route = BottomBarScreen.Deals.route) } else { - deal.id?.let { dealCreateViewModel.setupEdit(it) } + sharedViewModel.setArgumentEdit(deal); navController.navigate(BottomBarScreen.CDEAL.route) { popUpTo(navController.graph.findStartDestination().id) - launchSingleTop = true + launchSingleTop = false } } }, shape = RoundedCornerShape(10.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color(0xff5acb48)), + colors = ButtonDefaults.buttonColors( +// containerColor = Color(0xff5acb48) + containerColor = if (id != deal.buyerId) + Color(0xff5acb48) else + Color(0xFFCB48C9) + ), modifier = Modifier .fillMaxSize() .weight(weight = 0.5f) @@ -315,7 +353,7 @@ fun PropertyDeal(modifier: Modifier = Modifier, navController: NavHostController .clickable { navController.navigate(BottomBarScreen.CDEAL.route) { popUpTo(navController.graph.findStartDestination().id) - launchSingleTop = true + launchSingleTop = false } } .wrapContentHeight(align = Alignment.CenterVertically)) diff --git a/app/src/main/java/com/example/testapp/screensMobile/History.kt b/app/src/main/java/com/example/testapp/screensMobile/History.kt index 96687a8..ed69962 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/History.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/History.kt @@ -17,6 +17,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -39,6 +41,9 @@ import com.example.testapp.room.models.Deal import com.example.testapp.viewModels.AppViewModelProvider import com.example.testapp.viewModels.CurrentUserViewModel import com.example.testapp.viewModels.HistoryViewModel +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneOffset @RequiresApi(Build.VERSION_CODES.O) @Composable @@ -48,10 +53,11 @@ fun History( currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), historyViewModel: HistoryViewModel = viewModel(factory = AppViewModelProvider.Factory) ) { - val id = currentUserViewModel.user!!.id + val user by currentUserViewModel.user.collectAsState() + val id = user!!.id historyViewModel.setArgument(id.toString()); - val history = remember { mutableStateOf(historyViewModel.deals) } - val coins = remember { mutableStateOf(historyViewModel.coins) } + val history by historyViewModel.deals.collectAsState() + val coins by historyViewModel.coins.collectAsState() Column( verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), @@ -63,22 +69,23 @@ fun History( .verticalScroll(rememberScrollState()) ) { PropertyHistory() - history.value?.forEach { x -> - coins.value?.let { - HistoryCard( - deal = x, - modifier = Modifier.padding(vertical = 5.dp, horizontal = 5.dp), - coins = it - ) - } + history.forEach { x -> + HistoryCard( + deal = x, + modifier = Modifier.padding(vertical = 5.dp, horizontal = 5.dp), + coins = coins + ) } } } +@RequiresApi(Build.VERSION_CODES.O) @Composable fun HistoryCard(modifier: Modifier = Modifier, deal: Deal, coins: List) { val status = !(deal.buyerId == null || deal.sellerId == null) - val text = if (!status) "In work" else deal.date.toString() + val text = if (!status) "In work" + else LocalDateTime + .ofInstant(deal.date?.let { Instant.ofEpochMilli(it) }, ZoneOffset.UTC).toString() val color = if (!status) 0xfff96161 else 0xff5acb48 Column( @@ -114,6 +121,17 @@ fun HistoryCard(modifier: Modifier = Modifier, deal: Deal, coins: List) { modifier = Modifier .wrapContentHeight(align = Alignment.CenterVertically) ) + Text( + text = "->", + color = Color(0xFF009688), + lineHeight = 1.25.em, + style = TextStyle( + fontSize = 16.sp, + letterSpacing = 0.1.sp + ), + modifier = Modifier + .wrapContentHeight(align = Alignment.CenterVertically) + ) Text( text = "+${deal.countBuy} ${ coins.first { x -> x.id == deal.buyerCoinId }.shortName() diff --git a/app/src/main/java/com/example/testapp/screensMobile/Wallet.kt b/app/src/main/java/com/example/testapp/screensMobile/Wallet.kt index 10dae7f..58205cd 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/Wallet.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/Wallet.kt @@ -6,17 +6,20 @@ import androidx.annotation.RequiresApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -25,11 +28,14 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import com.example.pmulabs.designElem.NavBar import com.example.testapp.designElem.ListItem +import com.example.testapp.designElem.SharedViewModel import com.example.testapp.graphs.HomeNavGraph import com.example.testapp.viewModels.AppViewModelProvider import com.example.testapp.viewModels.CurrentUserViewModel @@ -37,6 +43,7 @@ import com.example.testapp.viewModels.DealCreateViewModel import com.example.testapp.viewModels.DealListViewModel import com.example.testapp.viewModels.HistoryViewModel import com.example.testapp.viewModels.WalletViewModel +import kotlinx.coroutines.flow.first @RequiresApi(Build.VERSION_CODES.O) @Composable @@ -46,13 +53,13 @@ fun Wallet( currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory) ) { - val id = currentUserViewModel.user!!.id - walletViewModel.setArgument(id.toString()) - val wallet = remember { mutableStateOf(walletViewModel.wallet) } - val coins = remember { mutableStateOf(walletViewModel.coins) } + val getUser by currentUserViewModel.user.collectAsState() + walletViewModel.setArgument(getUser!!.id.toString()) + val wallet = walletViewModel.wallet.collectAsLazyPagingItems() + val coins by walletViewModel.coins.collectAsState() LazyColumn( - verticalArrangement = Arrangement.spacedBy(5.dp, Alignment.Top), + verticalArrangement = Arrangement.spacedBy(15.dp, Alignment.Top), horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier .background(color = Color(0xfff4f7fb)) @@ -62,30 +69,54 @@ fun Wallet( item { PropertyWallet() } - item { - wallet.value?.forEach { coin -> - coins.value?.let { + items(count = wallet.itemCount) {x -> + val wi = wallet[x] + coins?.let { + if (wi != null) { ListItem( - coin = it.first { x -> x.id == coin.coinId }, - count = coin.count, - modifier = Modifier.padding(vertical = 5.dp) + coin = it.first { y -> y.id == wi.coinId }, + count = wi.count, + modifier = Modifier ) } } } + wallet.apply { + when{ + loadState.refresh is LoadState.Loading -> { + item { CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(0.5f), + color = Color(0xff423a99)) } + } + loadState.append is LoadState.Loading -> { + item { CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(0.5f), + color = Color(0xff423a99)) } + } + loadState.refresh is LoadState.Error -> { + val err = wallet.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + loadState.append is LoadState.Error -> { + val err = wallet.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } + } + } } } @RequiresApi(34) @Composable fun LoadScreen( + modifier: Modifier = Modifier, navController: NavHostController = rememberNavController(), - @SuppressLint("ModifierParameter") modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory), dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory), historyViewModel: HistoryViewModel = viewModel(factory = AppViewModelProvider.Factory), - walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory) + walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory), + sharedViewModel: SharedViewModel ) { Scaffold( bottomBar = { NavBar(navController = navController) }, @@ -98,7 +129,8 @@ fun LoadScreen( dealCreateViewModel, dealListViewModel, historyViewModel, - walletViewModel + walletViewModel, + sharedViewModel ) } } diff --git a/app/src/main/java/com/example/testapp/screensMobile/authScreens/EntryScreen.kt b/app/src/main/java/com/example/testapp/screensMobile/authScreens/EntryScreen.kt index 3417cb9..4aa183c 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/authScreens/EntryScreen.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/authScreens/EntryScreen.kt @@ -69,9 +69,7 @@ fun EntryScreen( var passwordValue by rememberSaveable { mutableStateOf("") } entryScreenViewModel.setupList() val users = mutableStateOf(entryScreenViewModel.userList) - val argument = currentUserViewModel.argument.value var passwordVisibility by rememberSaveable { mutableStateOf(false) } - val coroutineScope = rememberCoroutineScope() Box( modifier = modifier @@ -109,6 +107,12 @@ fun EntryScreen( TextField( value = emailValue, onValueChange = { emailValue = it }, + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White, + disabledContainerColor = Color.White, + ), + singleLine = true, label = { Text( text = "Email", @@ -127,6 +131,8 @@ fun EntryScreen( ), modifier = Modifier .fillMaxWidth() + .shadow(shape = RoundedCornerShape(5.dp), elevation = 5.dp) + .clip(shape = RoundedCornerShape(5.dp)), ) TextField( value = passwordValue, @@ -138,8 +144,8 @@ fun EntryScreen( ), modifier = modifier .fillMaxWidth() - .shadow(shape = RoundedCornerShape(40.dp), elevation = 5.dp) - .clip(shape = RoundedCornerShape(40.dp)), + .shadow(shape = RoundedCornerShape(5.dp), elevation = 5.dp) + .clip(shape = RoundedCornerShape(5.dp)), label = { Text("Password") }, singleLine = true, placeholder = { Text("Enter password") }, @@ -177,7 +183,7 @@ fun EntryScreen( btnConfig = btnConfig( onClick = { if (passwordValue.isNotEmpty() && isValidEmail(emailValue)) { - users.value.forEach { user -> + users.value.value.forEach { user -> if (user.password == passwordValue && user.email == emailValue) { currentUserViewModel.setArgument(user.id.toString()) navController.navigate(route = Graph.passUserId(user.id.toString())) diff --git a/app/src/main/java/com/example/testapp/viewModels/AppViewModelProvider.kt b/app/src/main/java/com/example/testapp/viewModels/AppViewModelProvider.kt index 9504eb3..2b50427 100644 --- a/app/src/main/java/com/example/testapp/viewModels/AppViewModelProvider.kt +++ b/app/src/main/java/com/example/testapp/viewModels/AppViewModelProvider.kt @@ -14,7 +14,8 @@ object AppViewModelProvider { initializer { DealListViewModel( application().container.dealRepository, - application().container.coinRepository + application().container.coinRepository, + application().container.walletItemRepository ) } initializer { diff --git a/app/src/main/java/com/example/testapp/viewModels/CurrentUserViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/CurrentUserViewModel.kt index b2aa04c..ecddd66 100644 --- a/app/src/main/java/com/example/testapp/viewModels/CurrentUserViewModel.kt +++ b/app/src/main/java/com/example/testapp/viewModels/CurrentUserViewModel.kt @@ -7,22 +7,23 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.testapp.room.models.User import com.example.testapp.room.repository.basic.UserRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch class CurrentUserViewModel(private val userRepository: UserRepository) : ViewModel() { - val argument = mutableStateOf(null) - private val id = mutableStateOf(null) - var user by mutableStateOf(null) + val argument = MutableStateFlow(null) + val id = MutableStateFlow(null) + var user = MutableStateFlow(null) fun setArgument(arg: String) { argument.value = arg id.value = arg.toInt() + viewModelScope.launch { - user = userRepository.getById(id.value!!) - .filterNotNull() - .first() + user.emit(userRepository.getById(id.value!!)) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/viewModels/DealCreateViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/DealCreateViewModel.kt index 3dc4398..e8453e0 100644 --- a/app/src/main/java/com/example/testapp/viewModels/DealCreateViewModel.kt +++ b/app/src/main/java/com/example/testapp/viewModels/DealCreateViewModel.kt @@ -5,13 +5,20 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal import com.example.testapp.room.models.WalletItem import com.example.testapp.room.repository.basic.CoinRepository import com.example.testapp.room.repository.basic.DealRepository import com.example.testapp.room.repository.basic.WalletItemRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch class DealCreateViewModel( @@ -19,38 +26,37 @@ class DealCreateViewModel( private val coinRepository: CoinRepository, private val walletItemRepository: WalletItemRepository ) : ViewModel() { - val argument = mutableStateOf(null) - private val user_id = mutableStateOf(null) - private val deal_id = mutableStateOf(null) + private val dealId = MutableStateFlow(null) + var id = 0 - var deal by mutableStateOf(null) - - var deals by mutableStateOf>(emptyList()) - var coins by mutableStateOf>(emptyList()) - var wallet by mutableStateOf>(emptyList()) + private var _deal = MutableStateFlow(null) + private var _coins = MutableStateFlow>(emptyList()) + private var _wallet = MutableStateFlow>(emptyList()) + var deal: StateFlow = _deal.asStateFlow() + var coins = _coins.asStateFlow() + var wallet = _wallet.asStateFlow() suspend fun update(deal: Deal) = dealRepository.update(deal) suspend fun create(deal: Deal) = dealRepository.insert(deal) + suspend fun delete(deal: Deal) = dealRepository.delete(deal) - fun setupLists(arg: String) { - argument.value = arg - user_id.value = arg.toInt() - - viewModelScope.launch { - deals = dealRepository.getAll().first(); - coins = coinRepository.getAll().first(); - wallet = walletItemRepository.getUserWallet(user_id.value!!).first(); + init { + viewModelScope.launch(Dispatchers.IO) { + _coins.emit( + coinRepository.getAll().first() + ) + _wallet.emit( + walletItemRepository.getAll().first() + ) } } fun isEdit(): Boolean { - return deal != null; + return deal.value != null; } - fun setupEdit(id: Int) { - deal_id.value = id; - viewModelScope.launch { - deal = dealRepository.getById(id).first(); - } + fun setupEdit(deal: Deal) { + dealId.value = deal.id + _deal.value = deal } } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/viewModels/DealListViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/DealListViewModel.kt index 8aede0d..ee134b2 100644 --- a/app/src/main/java/com/example/testapp/viewModels/DealListViewModel.kt +++ b/app/src/main/java/com/example/testapp/viewModels/DealListViewModel.kt @@ -1,32 +1,86 @@ package com.example.testapp.viewModels -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue +import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.WalletItem import com.example.testapp.room.repository.basic.CoinRepository import com.example.testapp.room.repository.basic.DealRepository +import com.example.testapp.room.repository.basic.WalletItemRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import java.util.Date class DealListViewModel( private val dealRepository: DealRepository, private val coinRepository: CoinRepository, + private val walletItemRepository: WalletItemRepository ) : ViewModel() { - var deals by mutableStateOf>(emptyList()) - var coins by mutableStateOf>(emptyList()) + var deals = MutableStateFlow>(PagingData.empty()) + var coins = MutableStateFlow>(emptyList()) fun setupLists() { - viewModelScope.launch { - deals = dealRepository.getAll().first(); - coins = coinRepository.getAll().first(); + viewModelScope.launch(Dispatchers.IO) { + coins.emit(coinRepository.getAll().first()) + deals.emit(dealRepository + .pagingData() + .cachedIn(viewModelScope) + .first()) } } + @RequiresApi(34) fun submitDeal(deal: Deal, sellerId: Int, buyerId: Int) { - dealRepository.completeDeal(deal, sellerId, buyerId) + viewModelScope.launch { + val walletSeller: List = walletItemRepository.getUserWallet(sellerId).first() + val walletBuyer: List = walletItemRepository.getUserWallet(buyerId).first() + + val seller: WalletItem? = walletSeller.firstOrNull { x -> x.coinId == deal.sellerCoinId } + val buyer: WalletItem? = walletBuyer.firstOrNull { x -> x.coinId == deal.buyerId } + + if (seller == null || buyer == null) return@launch + // Check what seller have buyer COIN or we need to insert new value to database: + // Case if need update + if (walletSeller.any { x -> x.coinId == deal.buyerCoinId }) { + val item = walletSeller.first { x -> x.coinId == deal.buyerCoinId } + item.count += deal.countBuy + walletItemRepository.update(item) + } + // Case if need insert + else { + val item = WalletItem(deal.buyerCoinId, sellerId, deal.countBuy) + walletItemRepository.insert(item) + } + + // Check what buyer have buyer COIN or we need to insert new value to database: + // Case if need update + if (walletBuyer.any { x -> x.coinId == deal.sellerCoinId }) { + val item = walletBuyer.first { x -> x.coinId == deal.sellerCoinId } + item.count += deal.countSell + walletItemRepository.update(item) + } + // Case if need insert + else { + val item = WalletItem(deal.sellerCoinId, buyerId, deal.countSell) + walletItemRepository.insert(item) + } + + seller.count -= deal.countSell + buyer.count -= deal.countBuy + + walletItemRepository.update(seller); + walletItemRepository.update(buyer); + + deal.sellerId = sellerId + deal.date = Date().time; + dealRepository.update(deal) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/viewModels/EntryScreenViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/EntryScreenViewModel.kt index 757518a..c80c4ed 100644 --- a/app/src/main/java/com/example/testapp/viewModels/EntryScreenViewModel.kt +++ b/app/src/main/java/com/example/testapp/viewModels/EntryScreenViewModel.kt @@ -7,16 +7,18 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.testapp.room.models.User import com.example.testapp.room.repository.basic.UserRepository +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch class EntryScreenViewModel( private val userRepository: UserRepository ) : ViewModel() { - var userList by mutableStateOf>(emptyList()) + var userList = MutableStateFlow>(emptyList()) + fun setupList() { viewModelScope.launch { - userList = userRepository.getAll().first() + userList.emit(userRepository.getAll().first()) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/viewModels/HistoryViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/HistoryViewModel.kt index 05682ba..c9d6fb6 100644 --- a/app/src/main/java/com/example/testapp/viewModels/HistoryViewModel.kt +++ b/app/src/main/java/com/example/testapp/viewModels/HistoryViewModel.kt @@ -9,22 +9,26 @@ import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal import com.example.testapp.room.repository.basic.CoinRepository import com.example.testapp.room.repository.basic.DealRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -class HistoryViewModel(private val dealRepository: DealRepository, - private val coinRepository: CoinRepository) : ViewModel() { - val argument = mutableStateOf(null) - private val id = mutableStateOf(null) - var deals by mutableStateOf?>(emptyList()) - var coins by mutableStateOf?>(emptyList()) +class HistoryViewModel( + private val dealRepository: DealRepository, + private val coinRepository: CoinRepository +) : ViewModel() { + val argument = MutableStateFlow(null) + private val id = MutableStateFlow(null) + var deals = MutableStateFlow>(emptyList()) + var coins = MutableStateFlow>(emptyList()) fun setArgument(arg: String) { argument.value = arg id.value = arg.toInt() - viewModelScope.launch { - deals = dealRepository.getUserDeals(id.value!!).first() - coins = coinRepository.getAll().first() + viewModelScope.launch(Dispatchers.IO) { + deals.emit(dealRepository.getAll().first()) + coins.emit(coinRepository.getAll().first()) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/viewModels/WalletViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/WalletViewModel.kt index eb40885..dbbd624 100644 --- a/app/src/main/java/com/example/testapp/viewModels/WalletViewModel.kt +++ b/app/src/main/java/com/example/testapp/viewModels/WalletViewModel.kt @@ -3,12 +3,22 @@ package com.example.testapp.viewModels import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn +import androidx.paging.filter import com.example.testapp.room.models.Coin import com.example.testapp.room.models.WalletItem import com.example.testapp.room.repository.basic.CoinRepository import com.example.testapp.room.repository.basic.WalletItemRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -16,17 +26,21 @@ class WalletViewModel( private val walletItemRepository: WalletItemRepository, private val coinRepository: CoinRepository ) : ViewModel() { - val argument = mutableStateOf(null) - private val id = mutableStateOf(null) - var wallet by mutableStateOf?>(emptyList()) - var coins by mutableStateOf?>(emptyList()) + val argument = MutableStateFlow(null) + private val id = MutableStateFlow(null) + var _wallet = MutableStateFlow>(PagingData.empty()) + var wallet = _wallet.asStateFlow(); + var coins = MutableStateFlow?>(emptyList()) fun setArgument(arg: String) { argument.value = arg id.value = arg.toInt() viewModelScope.launch { - wallet = walletItemRepository.getUserWallet(id.value!!).first() - coins = coinRepository.getAll().first() + coins.emit(coinRepository.getAll().first()) + _wallet.emit(walletItemRepository + .pagingData(id.value!!) + .cachedIn(viewModelScope) + .first()) } } } \ No newline at end of file