diff --git a/app/src/main/java/com/example/testapp/CryptoDealApplication.kt b/app/src/main/java/com/example/testapp/CryptoDealApplication.kt new file mode 100644 index 0000000..9bfabaa --- /dev/null +++ b/app/src/main/java/com/example/testapp/CryptoDealApplication.kt @@ -0,0 +1,14 @@ +package com.example.testapp + +import android.app.Application +import com.example.testapp.room.AppContainer +import com.example.testapp.room.AppDataContainer + +class CryptoDealApplication : Application() { + lateinit var container: AppContainer + + override fun onCreate() { + super.onCreate() + container = AppDataContainer(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/MainActivity.kt b/app/src/main/java/com/example/testapp/MainActivity.kt index 11779e7..8f17288 100644 --- a/app/src/main/java/com/example/testapp/MainActivity.kt +++ b/app/src/main/java/com/example/testapp/MainActivity.kt @@ -2,26 +2,19 @@ package com.example.testapp import android.annotation.SuppressLint import android.os.Bundle -import android.widget.TextView import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview +import androidx.annotation.RequiresApi 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 org.w3c.dom.Text +import com.example.testapp.viewModels.CurrentUserViewModel class MainActivity : ComponentActivity() { - private val sharedViewModel: SharedViewModel by viewModels() + private val currentUserViewModel: CurrentUserViewModel by viewModels() + @RequiresApi(34) @SuppressLint("MissingInflatedId", "SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -29,7 +22,7 @@ class MainActivity : ComponentActivity() { TestAppTheme { RootNavigationGraph( navController = rememberNavController(), - sharedViewModel = sharedViewModel + currentUserViewModel = currentUserViewModel ) } } diff --git a/app/src/main/java/com/example/testapp/graphs/AuthNavGraph.kt b/app/src/main/java/com/example/testapp/graphs/AuthNavGraph.kt index 35d37ad..59bf672 100644 --- a/app/src/main/java/com/example/testapp/graphs/AuthNavGraph.kt +++ b/app/src/main/java/com/example/testapp/graphs/AuthNavGraph.kt @@ -5,25 +5,42 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import androidx.navigation.navigation -import com.example.testapp.designElem.SharedViewModel -import com.example.testapp.screensMobile.EntryScreen -import com.example.testapp.screensMobile.RegisterScreen +import com.example.testapp.screensMobile.authScreens.EntryScreen +import com.example.testapp.screensMobile.authScreens.RegisterScreen +import com.example.testapp.viewModels.CurrentUserViewModel +import com.example.testapp.viewModels.EntryScreenViewModel +import com.example.testapp.viewModels.RegistrationScreenViewModel -fun NavGraphBuilder.authNavGraph(navController: NavHostController, sharedViewModel: SharedViewModel){ +fun NavGraphBuilder.authNavGraph( + navController: NavHostController, + currentUserViewModel: CurrentUserViewModel, + entryScreenViewModel: EntryScreenViewModel, + registrationScreenViewModel: RegistrationScreenViewModel +) { navigation( - route=Graph.AUTHENTICATION, + route = Graph.AUTHENTICATION, startDestination = AuthScreen.Entry.route - ){ - composable(route=AuthScreen.Entry.route){ - EntryScreen(navController = navController, modifier = Modifier, sharedViewModel = sharedViewModel) + ) { + composable(route = AuthScreen.Entry.route) { + EntryScreen( + navController, + Modifier, + currentUserViewModel, + entryScreenViewModel + ) } - composable(route=AuthScreen.Register.route){ - RegisterScreen(navController = navController, modifier = Modifier, sharedViewModel = sharedViewModel) + composable(route = AuthScreen.Register.route) { + RegisterScreen( + navController, + Modifier, + currentUserViewModel, + registrationScreenViewModel + ) } } } -sealed class AuthScreen(val route: String){ - object Entry : AuthScreen(route="ENTRY") - object Register : AuthScreen(route="REGISTER") +sealed class AuthScreen(val route: String) { + object Entry : AuthScreen(route = "ENTRY") + object Register : AuthScreen(route = "REGISTER") } \ No newline at end of file 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 da68798..1918efd 100644 --- a/app/src/main/java/com/example/testapp/graphs/HomeNavGraph.kt +++ b/app/src/main/java/com/example/testapp/graphs/HomeNavGraph.kt @@ -1,44 +1,72 @@ package com.example.testapp.graphs -import android.os.Build import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController -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.navigate.BottomBarScreen import com.example.testapp.screensMobile.AccountPage import com.example.testapp.screensMobile.CreateDeal import com.example.testapp.screensMobile.DealList import com.example.testapp.screensMobile.History import com.example.testapp.screensMobile.Wallet +import com.example.testapp.viewModels.AppViewModelProvider +import com.example.testapp.viewModels.CurrentUserViewModel +import com.example.testapp.viewModels.DealCreateViewModel +import com.example.testapp.viewModels.DealListViewModel +import com.example.testapp.viewModels.HistoryViewModel +import com.example.testapp.viewModels.WalletViewModel @RequiresApi(34) @Composable -fun HomeNavGraph(navController: NavHostController, sharedViewModel: SharedViewModel){ +fun HomeNavGraph( + navController: NavHostController, + 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) +) { NavHost( navController = navController, route = Graph.MAIN, startDestination = BottomBarScreen.Wallet.route - ){ - composable(route=BottomBarScreen.Wallet.route){ - Wallet(sharedViewModel = sharedViewModel) + ) { + composable(route = BottomBarScreen.Wallet.route) { + Wallet( + navController = navController, + currentUserViewModel = currentUserViewModel, + walletViewModel = walletViewModel + ) } - composable(route=BottomBarScreen.Profile.route){ - AccountPage(sharedViewModel = sharedViewModel, navController = navController) + composable(route = BottomBarScreen.Profile.route) { + AccountPage( + navController = navController, + currentUserViewModel = currentUserViewModel + ) } - composable(route=BottomBarScreen.Deals.route){ - DealList(navController = navController, sharedViewModel = sharedViewModel) + composable(route = BottomBarScreen.Deals.route) { + DealList( + navController = navController, + currentUserViewModel = currentUserViewModel, + dealListViewModel = dealListViewModel + ) } - composable(route=BottomBarScreen.CDEAL.route) { - CreateDeal(sharedViewModel = sharedViewModel, navController = navController) + composable(route = BottomBarScreen.CDEAL.route) { + CreateDeal( + navController = navController, + currentUserViewModel = currentUserViewModel, + dealCreateViewModel = dealCreateViewModel + ) } - composable(route=BottomBarScreen.History.route){ - History(sharedViewModel = sharedViewModel) + composable(route = BottomBarScreen.History.route) { + History( + navController = navController, + currentUserViewModel = currentUserViewModel, + historyViewModel = historyViewModel + ) } } } 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 9f6e6d3..18a5855 100644 --- a/app/src/main/java/com/example/testapp/graphs/RootNavGraph.kt +++ b/app/src/main/java/com/example/testapp/graphs/RootNavGraph.kt @@ -1,42 +1,71 @@ package com.example.testapp.graphs -import android.os.Build import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController 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 +import com.example.testapp.viewModels.DealCreateViewModel +import com.example.testapp.viewModels.DealListViewModel +import com.example.testapp.viewModels.EntryScreenViewModel +import com.example.testapp.viewModels.HistoryViewModel +import com.example.testapp.viewModels.RegistrationScreenViewModel +import com.example.testapp.viewModels.WalletViewModel -const val USERID_ARGUMENT="userId" +const val USERID_ARGUMENT = "userId" @RequiresApi(34) @Composable -fun RootNavigationGraph(navController: NavHostController, sharedViewModel: SharedViewModel){ +fun RootNavigationGraph( + navController: NavHostController, + currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), + dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory), + dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory), + entryScreenViewModel: EntryScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), + historyViewModel: HistoryViewModel = viewModel(factory = AppViewModelProvider.Factory), + registrationScreenViewModel: RegistrationScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), + walletViewModel: WalletViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { NavHost( - navController=navController, + navController = navController, route = Graph.ROOT, startDestination = Graph.AUTHENTICATION - ){ - authNavGraph(navController=navController,sharedViewModel) - composable(route=Graph.MAIN, - arguments = listOf(navArgument(USERID_ARGUMENT){ - type= NavType.StringType - })){ - LoadScreen(sharedViewModel = sharedViewModel) + ) { + authNavGraph( + navController = navController, + currentUserViewModel, + entryScreenViewModel, + registrationScreenViewModel + ) + composable( + route = Graph.MAIN, + arguments = listOf(navArgument(USERID_ARGUMENT) { + type = NavType.StringType + }) + ) { + LoadScreen( + currentUserViewModel = currentUserViewModel, + dealCreateViewModel = dealCreateViewModel, + dealListViewModel = dealListViewModel, + historyViewModel = historyViewModel, + walletViewModel = walletViewModel + ) } } } -object Graph{ - const val ROOT="root_graph" - const val AUTHENTICATION="auth_graph" - const val MAIN="main_graph/{$USERID_ARGUMENT}" +object Graph { + const val ROOT = "root_graph" + const val AUTHENTICATION = "auth_graph" + const val MAIN = "main_graph/{$USERID_ARGUMENT}" - fun passUserId(userId: String): String{ + fun passUserId(userId: String): String { return "main_graph/$userId" } } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/AppContainer.kt b/app/src/main/java/com/example/testapp/room/AppContainer.kt new file mode 100644 index 0000000..0da2099 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/AppContainer.kt @@ -0,0 +1,40 @@ +package com.example.testapp.room + +import android.content.Context +import com.example.testapp.room.database.CryptoDealDb +import com.example.testapp.room.repository.basic.CoinRepository +import com.example.testapp.room.repository.basic.DealRepository +import com.example.testapp.room.repository.basic.UserRepository +import com.example.testapp.room.repository.basic.WalletItemRepository +import com.example.testapp.room.repository.offline.OfflineCoinRepository +import com.example.testapp.room.repository.offline.OfflineDealRepository +import com.example.testapp.room.repository.offline.OfflineUserRepository +import com.example.testapp.room.repository.offline.OfflineWalletItemRepository + +interface AppContainer { + val userRepository: UserRepository + val dealRepository: DealRepository + val coinRepository: CoinRepository + val walletItemRepository: WalletItemRepository +} + +class AppDataContainer( + private val context: Context +) : AppContainer { + companion object { + const val TIMEOUT = 5000L + } + + override val userRepository: UserRepository by lazy { + OfflineUserRepository(CryptoDealDb.getInstance(context).userDao()) + } + override val dealRepository: DealRepository by lazy { + OfflineDealRepository(CryptoDealDb.getInstance(context).dealDao()) + } + override val coinRepository: CoinRepository by lazy { + OfflineCoinRepository(CryptoDealDb.getInstance(context).coinDao()) + } + override val walletItemRepository: WalletItemRepository by lazy { + OfflineWalletItemRepository(CryptoDealDb.getInstance(context).walletItemDao()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/dao/CoinDao.kt b/app/src/main/java/com/example/testapp/room/dao/CoinDao.kt index 16c17ed..cf5273e 100644 --- a/app/src/main/java/com/example/testapp/room/dao/CoinDao.kt +++ b/app/src/main/java/com/example/testapp/room/dao/CoinDao.kt @@ -1,21 +1,12 @@ package com.example.testapp.room.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update import com.example.testapp.room.models.Coin import kotlinx.coroutines.flow.Flow @Dao -interface CoinDao{ +interface CoinDao : IDao { @Query("select * from coin") fun getAll(): Flow> - @Insert - suspend fun insert(obj: Coin) - @Update - suspend fun update(obj: Coin) - @Delete - suspend fun delete(obj: Coin) } \ No newline at end of file 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 17ec4ac..cf190ca 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 @@ -1,24 +1,18 @@ package com.example.testapp.room.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update -import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal import kotlinx.coroutines.flow.Flow @Dao -interface DealDao{ +interface DealDao : IDao { @Query("select * from deal") fun getAll(): Flow> + + @Query("select * from deal 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") fun getUserDeals(idUser: Int): Flow> - @Insert - suspend fun insert(obj: Deal) - @Update - suspend fun update(obj: Deal) - @Delete - suspend fun delete(obj: Deal) } \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/dao/IDao.kt b/app/src/main/java/com/example/testapp/room/dao/IDao.kt new file mode 100644 index 0000000..a2298a3 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/dao/IDao.kt @@ -0,0 +1,18 @@ +package com.example.testapp.room.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Update + +@Dao +interface IDao { + @Insert + suspend fun insert(obj: T) + + @Update + suspend fun update(obj: T) + + @Delete + suspend fun delete(obj: T) +} \ No newline at end of file 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 ace904f..1dc7c23 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 @@ -1,29 +1,23 @@ package com.example.testapp.room.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update import com.example.testapp.room.models.Deal import com.example.testapp.room.models.User import com.example.testapp.room.models.WalletItem import kotlinx.coroutines.flow.Flow @Dao -interface UserDao { +interface UserDao : IDao { @Query("select * from user") fun getAll(): Flow> + @Query("select * from user where user.id = :idUser") fun getUserById(idUser: Int): Flow + @Query("select * from walletitem where walletitem.userId = :idUser") fun getUserWallet(idUser: Int): Flow> + @Query("select * from deal where deal.buyerId = :idUser OR deal.sellerId = :idUser") fun getUserDeals(idUser: Int): Flow> - @Insert - fun insert(obj: User) - @Update - fun update(obj: User) - @Delete - fun delete(obj: User) } \ No newline at end of file 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 aa37858..95f7a25 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 @@ -1,23 +1,15 @@ package com.example.testapp.room.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update -import com.example.testapp.room.models.Deal -import com.example.testapp.room.models.User import com.example.testapp.room.models.WalletItem import kotlinx.coroutines.flow.Flow @Dao -interface WalletItemDao { +interface WalletItemDao : IDao { @Query("select * from walletitem") fun getAll(): Flow> - @Insert - suspend fun insert(obj: WalletItem) - @Update - suspend fun update(obj: WalletItem) - @Delete - suspend fun delete(obj: WalletItem) + + @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/pagination/DealPagination.kt b/app/src/main/java/com/example/testapp/room/pagination/DealPagination.kt new file mode 100644 index 0000000..f04ff85 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/pagination/DealPagination.kt @@ -0,0 +1,38 @@ +package com.example.testapp.room.pagination + +import android.util.Log +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.example.testapp.room.dao.DealDao +import com.example.testapp.room.models.Deal +import kotlinx.coroutines.delay + +class DealPagination( + private val dao: DealDao +) : 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) + 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/CoinRepository.kt b/app/src/main/java/com/example/testapp/room/repository/basic/CoinRepository.kt new file mode 100644 index 0000000..8fa6d5d --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/basic/CoinRepository.kt @@ -0,0 +1,6 @@ +package com.example.testapp.room.repository.basic + +import com.example.testapp.room.models.Coin + +interface CoinRepository : IRepository { +} \ 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 new file mode 100644 index 0000000..e8569dc --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/basic/DealRepository.kt @@ -0,0 +1,9 @@ +package com.example.testapp.room.repository.basic + +import com.example.testapp.room.models.Deal +import kotlinx.coroutines.flow.Flow + +interface DealRepository : IRepository { + fun getUserDeals(id: Int): Flow> + fun completeDeal(deal: Deal, sellerId: Int, buyerId: Int): Boolean +} \ 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 new file mode 100644 index 0000000..faf188e --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/basic/IRepository.kt @@ -0,0 +1,12 @@ +package com.example.testapp.room.repository.basic + +import kotlinx.coroutines.flow.Flow + +interface IRepository { + suspend fun insert(x: T) + suspend fun update(x: T) + suspend fun delete(x: 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/UserRepository.kt b/app/src/main/java/com/example/testapp/room/repository/basic/UserRepository.kt new file mode 100644 index 0000000..672f8d9 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/basic/UserRepository.kt @@ -0,0 +1,11 @@ +package com.example.testapp.room.repository.basic + +import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.User +import com.example.testapp.room.models.WalletItem +import kotlinx.coroutines.flow.Flow + +interface UserRepository : IRepository { + fun getUserWallet(id: Int): Flow> + fun getUserDeals(id: Int): Flow> +} \ 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 new file mode 100644 index 0000000..31f7937 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/basic/WalletItemRepository.kt @@ -0,0 +1,8 @@ +package com.example.testapp.room.repository.basic + +import com.example.testapp.room.models.WalletItem +import kotlinx.coroutines.flow.Flow + +interface WalletItemRepository : IRepository { + fun getUserWallet(id: Int): Flow> +} \ 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 new file mode 100644 index 0000000..864cd79 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineCoinRepository.kt @@ -0,0 +1,14 @@ +package com.example.testapp.room.repository.offline + +import com.example.testapp.room.dao.CoinDao +import com.example.testapp.room.models.Coin +import com.example.testapp.room.repository.basic.CoinRepository +import kotlinx.coroutines.flow.Flow + +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() +} \ 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 new file mode 100644 index 0000000..b7eac54 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineDealRepository.kt @@ -0,0 +1,43 @@ +package com.example.testapp.room.repository.offline + +import com.example.testapp.room.dao.DealDao +import com.example.testapp.room.models.Deal +import com.example.testapp.room.repository.basic.DealRepository +import kotlinx.coroutines.flow.Flow + +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 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; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/room/repository/offline/OfflineRepository.kt b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineRepository.kt new file mode 100644 index 0000000..f2eee79 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineRepository.kt @@ -0,0 +1,12 @@ +package com.example.testapp.room.repository.offline + +import com.example.testapp.room.dao.IDao +import com.example.testapp.room.repository.basic.IRepository + +abstract class OfflineRepository ( + private val dao: IDao +) : IRepository { + override suspend fun insert(x: T) = dao.insert(x) + override suspend fun update(x: T) = dao.update(x) + override suspend fun delete(x: T) = dao.delete(x) +} \ 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 new file mode 100644 index 0000000..cd3513d --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineUserRepository.kt @@ -0,0 +1,18 @@ +package com.example.testapp.room.repository.offline + +import com.example.testapp.room.dao.UserDao +import com.example.testapp.room.models.Deal +import com.example.testapp.room.models.User +import com.example.testapp.room.models.WalletItem +import com.example.testapp.room.repository.basic.UserRepository +import kotlinx.coroutines.flow.Flow + +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 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 new file mode 100644 index 0000000..aff64d5 --- /dev/null +++ b/app/src/main/java/com/example/testapp/room/repository/offline/OfflineWalletItemRepository.kt @@ -0,0 +1,15 @@ +package com.example.testapp.room.repository.offline + +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.flow.Flow + +class OfflineWalletItemRepository( + private val walletItemDao: WalletItemDao +) : 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() +} \ 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 24407bf..a10a36f 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/AccountPage.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/AccountPage.kt @@ -19,44 +19,36 @@ 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.LaunchedEffect -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.NavGraph.Companion.findStartDestination -import com.example.testapp.designElem.SharedViewModel import com.example.testapp.graphs.AuthScreen -import com.example.testapp.room.database.CryptoDealDb -import com.example.testapp.room.models.User -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import com.example.testapp.viewModels.AppViewModelProvider +import com.example.testapp.viewModels.CurrentUserViewModel @SuppressLint("CoroutineCreationDuringComposition") @Composable -fun AccountPage(modifier: Modifier = Modifier, sharedViewModel: SharedViewModel, navController: NavController) { - val argument = sharedViewModel.argument.value - val context = LocalContext.current - val users = remember { mutableStateListOf() } - var user: User? = null; - CoroutineScope(Dispatchers.IO).launch { - CryptoDealDb.getInstance(context).userDao().getUserById(argument!!.toInt()).collect { data -> - user = data - } - } +fun AccountPage( + modifier: Modifier = Modifier, + navController: NavController, + currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + val user by remember { mutableStateOf(currentUserViewModel.user) } + val coroutineScope = rememberCoroutineScope() Box( modifier = modifier @@ -86,7 +78,9 @@ fun AccountPage(modifier: Modifier = Modifier, sharedViewModel: SharedViewModel, if (user != null) { Container( name = user!!.email.substring(0, 2), - modifier = Modifier.align(alignment = Alignment.TopStart).offset(15.dp, 15.dp) + modifier = Modifier + .align(alignment = Alignment.TopStart) + .offset(15.dp, 15.dp) ) } } @@ -107,13 +101,16 @@ fun AccountPage(modifier: Modifier = Modifier, sharedViewModel: SharedViewModel, lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp)) + letterSpacing = 0.1.sp + ) + ) } } Box( modifier = Modifier .requiredWidth(width = 17.dp) - .requiredHeight(height = 4.dp)) + .requiredHeight(height = 4.dp) + ) } } } @@ -135,7 +132,8 @@ fun AccountPage(modifier: Modifier = Modifier, sharedViewModel: SharedViewModel, .offset( x = 0.dp, y = 24.dp - )) + ) + ) } } @@ -169,10 +167,12 @@ fun Property1Switch(modifier: Modifier = Modifier) { lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier .weight(weight = 1f) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) Viewchecked() } } @@ -194,9 +194,11 @@ fun Container(modifier: Modifier = Modifier, name: String) { textAlign = TextAlign.Center, style = TextStyle( fontSize = 24.sp, - fontWeight = FontWeight.Medium), + fontWeight = FontWeight.Medium + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } } @@ -216,12 +218,14 @@ fun Viewchecked(modifier: Modifier = Modifier) { modifier = Modifier .fillMaxSize() .clip(shape = RoundedCornerShape(100.dp)) - .background(color = Color(0xff4bb2f9))) + .background(color = Color(0xff4bb2f9)) + ) Box( modifier = Modifier .fillMaxSize() .clip(shape = CircleShape) - .background(color = Color.White)) + .background(color = Color.White) + ) } } } @@ -256,10 +260,12 @@ fun Property1Arrow(modifier: Modifier = Modifier) { lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier .weight(weight = 1f) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } } @@ -269,7 +275,7 @@ fun Property1Clear(modifier: Modifier = Modifier, navController: NavController) modifier = modifier .fillMaxSize() .clip(shape = RoundedCornerShape(10.dp)) - .clickable{ + .clickable { navController.navigate(route = AuthScreen.Entry.route) { popUpTo(navController.graph.findStartDestination().id) launchSingleTop = true @@ -302,7 +308,9 @@ fun Property1Clear(modifier: Modifier = Modifier, navController: NavController) style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp)) + letterSpacing = 0.1.sp + ) + ) } } } @@ -343,9 +351,11 @@ fun Property1(modifier: Modifier = Modifier) { lineHeight = 1.14.em, style = TextStyle( fontSize = 14.sp, - fontWeight = FontWeight.Medium), + fontWeight = FontWeight.Medium + ), modifier = Modifier - .fillMaxWidth()) + .fillMaxWidth() + ) } } } 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 e283367..939c374 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/CreateDeal.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/CreateDeal.kt @@ -1,5 +1,6 @@ package com.example.testapp.screensMobile +import android.annotation.SuppressLint import androidx.annotation.RequiresApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -20,11 +21,8 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable @@ -33,66 +31,47 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController 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.database.CryptoDealDb -import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal -import com.example.testapp.room.models.User -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 import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +@SuppressLint("UnrememberedMutableState") @RequiresApi(34) @Composable -fun CreateDeal(modifier: Modifier = Modifier, - sharedViewModel: SharedViewModel, - navController: NavHostController) { - val argument = sharedViewModel.argument.value - val editDeal: Deal? = sharedViewModel.argument_edit.value as? Deal - val isEdit = editDeal != null - val id = if (isEdit) editDeal?.buyerId else argument?.toInt() +fun CreateDeal( + modifier: Modifier = Modifier, + navController: NavHostController, + currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), + dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + dealCreateViewModel.setupLists(currentUserViewModel.user!!.id.toString()) - val context = LocalContext.current - val coins = remember { mutableStateListOf() } - SideEffect() { - CoroutineScope(Dispatchers.IO).launch { - CryptoDealDb.getInstance(context).coinDao().getAll().collect { data -> - coins.clear() - coins.addAll(data) - } - } - } + val isEdit = dealCreateViewModel.isEdit() + val editDeal: Deal? = dealCreateViewModel.deal + val id = if (isEdit) editDeal?.buyerId else currentUserViewModel.user!!.id - val wallet = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - if (id != null) { - CryptoDealDb.getInstance(context).userDao().getUserWallet(id).collect{data -> - wallet.clear(); - wallet.addAll(data); - } - } - } - } + val coins = mutableStateOf(dealCreateViewModel.coins) + val wallet = mutableStateOf(dealCreateViewModel.wallet) - val coinsBuyer = wallet.filter { x -> x.userId == id }.toList() + + val coinsBuyer = wallet.value.filter { x -> x.userId == id }.toList() var coinBuyer by rememberSaveable { - mutableStateOf(if (isEdit) coins.first{y -> y.id == editDeal?.buyerCoinId }.name else coinsBuyer[0]) + mutableStateOf(if (isEdit) coins.value.first { y -> y.id == editDeal?.buyerCoinId }.name else coinsBuyer[0]) } var buyCount by remember { @@ -102,9 +81,10 @@ fun CreateDeal(modifier: Modifier = Modifier, mutableStateOf(if (isEdit) editDeal?.tip else "") } - val coinsSeller = wallet.map { x -> coins.first{y -> y.id == x.coinId}.name }.toList() + val coinsSeller = + wallet.value.map { x -> coins.value.first { y -> y.id == x.coinId }.name }.toList() var coinSeller by rememberSaveable { - mutableStateOf(if (isEdit) coins.first{y -> y.id == editDeal?.sellerCoinId }.name else coinsSeller[0]) + mutableStateOf(if (isEdit) coins.value.first { y -> y.id == editDeal?.sellerCoinId }.name else coinsSeller[0]) } var sellCount by remember { mutableFloatStateOf(if (isEdit) editDeal?.countSell!! else 0f) @@ -141,7 +121,8 @@ fun CreateDeal(modifier: Modifier = Modifier, lineHeight = 1.14.em, style = TextStyle( fontSize = 14.sp, - fontWeight = FontWeight.Medium) + fontWeight = FontWeight.Medium + ) ) Text( text = "-", @@ -150,7 +131,8 @@ fun CreateDeal(modifier: Modifier = Modifier, lineHeight = 0.83.em, style = TextStyle( fontSize = 24.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier .clickable { navController.navigate(route = BottomBarScreen.Deals.route) @@ -165,15 +147,18 @@ fun CreateDeal(modifier: Modifier = Modifier, lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } item { DropDown( modifier = Modifier.padding(horizontal = 10.dp), downConfig = DropDownConfig( - values = coinsBuyer.map { x -> coins.first{y -> y.id == x.coinId}.name }.toList(), + values = coinsBuyer.map { x -> coins.value.first { y -> y.id == x.coinId }.name } + .toList(), title = "Select coin", onValueChange = { x -> coinBuyer = x }, selected = coinBuyer as String @@ -184,22 +169,25 @@ fun CreateDeal(modifier: Modifier = Modifier, OutlinedTextField( value = buyCount.toString(), onValueChange = { - val value = coinsBuyer.first{ x -> coinBuyer == coins.first{y -> y.id == x.coinId} } - if (it.toFloatOrNull() != null && it.toFloat() <= value.count) { - buyCount = it.toFloat() - } + val value = + coinsBuyer.first { x -> coinBuyer == coins.value.first { y -> y.id == x.coinId } } + if (it.toFloatOrNull() != null && it.toFloat() <= value.count) { + buyCount = it.toFloat() + } }, label = { Text( - text = "Fill count 0 -> ${coinsBuyer.first{ x -> coinBuyer == coins.first{y -> y.id == x.coinId} }.count}", + text = "Fill count 0 -> ${coinsBuyer.first { x -> coinBuyer == coins.value.first { y -> y.id == x.coinId } }.count}", color = Color.Black, textAlign = TextAlign.Center, lineHeight = 1.43.em, style = TextStyle( fontSize = 14.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) }, modifier = Modifier .fillMaxWidth() @@ -207,7 +195,8 @@ fun CreateDeal(modifier: Modifier = Modifier, .padding( horizontal = 10.dp, vertical = 2.dp - )) + ) + ) } item { OutlinedTextField( @@ -221,9 +210,11 @@ fun CreateDeal(modifier: Modifier = Modifier, lineHeight = 1.43.em, style = TextStyle( fontSize = 14.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) }, modifier = Modifier .fillMaxWidth() @@ -232,7 +223,8 @@ fun CreateDeal(modifier: Modifier = Modifier, .padding( horizontal = 10.dp, vertical = 2.dp - )) + ) + ) } item { Text( @@ -241,9 +233,11 @@ fun CreateDeal(modifier: Modifier = Modifier, lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } item { DropDown( @@ -268,9 +262,11 @@ fun CreateDeal(modifier: Modifier = Modifier, lineHeight = 1.43.em, style = TextStyle( fontSize = 14.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) }, modifier = Modifier .fillMaxWidth() @@ -278,7 +274,8 @@ fun CreateDeal(modifier: Modifier = Modifier, .padding( horizontal = 10.dp, vertical = 2.dp - )) + ) + ) } item { Button( @@ -288,32 +285,32 @@ fun CreateDeal(modifier: Modifier = Modifier, id = editDeal?.id, sellerId = editDeal?.sellerId, buyerId = id, - buyerCoinId = coins.first { x -> x.name == coinBuyer }.id!!, + buyerCoinId = coins.value.first { x -> x.name == coinBuyer }.id!!, countBuy = buyCount, - sellerCoinId = coins.first { x -> x.name == coinSeller }.id!!, + sellerCoinId = coins.value.first { x -> x.name == coinSeller }.id!!, countSell = sellCount, tip = tip!!, operation = "Buy", date = null, ) CoroutineScope(Dispatchers.IO).launch { - CryptoDealDb.getInstance(context).dealDao().update(deal) + dealCreateViewModel.update(deal) } } else { val deal = Deal( id = null, sellerId = null, buyerId = id, - buyerCoinId = coins.first { x -> x.name == coinBuyer }.id!!, + buyerCoinId = coins.value.first { x -> x.name == coinBuyer }.id!!, countBuy = buyCount, - sellerCoinId = coins.first { x -> x.name == coinSeller }.id!!, + sellerCoinId = coins.value.first { x -> x.name == coinSeller }.id!!, countSell = sellCount, tip = tip!!, operation = "Buy", date = null, ) CoroutineScope(Dispatchers.IO).launch { - CryptoDealDb.getInstance(context).dealDao().insert(deal) + dealCreateViewModel.create(deal) } } navController.navigate(route = BottomBarScreen.Deals.route) @@ -350,7 +347,9 @@ fun CreateDeal(modifier: Modifier = Modifier, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp)) + letterSpacing = 0.1.sp + ) + ) } } } @@ -393,23 +392,13 @@ fun CreateDeal(modifier: Modifier = Modifier, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp)) + letterSpacing = 0.1.sp + ) + ) } } } } } } -} - -@RequiresApi(34) -@Preview -@Composable -private fun Frame20Preview() { - val sharedViewModel = SharedViewModel() - sharedViewModel.setArgument("0") - CreateDeal(Modifier, - navController = rememberNavController(), - sharedViewModel = sharedViewModel - ) } \ No newline at end of file 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 195cd2f..4dfb794 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/DealList.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/DealList.kt @@ -1,37 +1,28 @@ package com.example.testapp.screensMobile -import android.annotation.SuppressLint import android.os.Build import androidx.annotation.RequiresApi -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background -import androidx.compose.foundation.border 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.Row -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -39,73 +30,40 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Dialog +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import com.example.testapp.R -import com.example.testapp.designElem.AlertDialogExample -import com.example.testapp.designElem.SharedViewModel -import com.example.testapp.graphs.Graph import com.example.testapp.navigate.BottomBarScreen -import com.example.testapp.room.database.CryptoDealDb import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal -import com.example.testapp.room.models.WalletItem -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.time.LocalDate -import java.time.LocalDateTime +import com.example.testapp.viewModels.AppViewModelProvider +import com.example.testapp.viewModels.CurrentUserViewModel +import com.example.testapp.viewModels.DealCreateViewModel +import com.example.testapp.viewModels.DealListViewModel @RequiresApi(Build.VERSION_CODES.O) @Composable -fun DealList(navController: NavHostController, - sharedViewModel: SharedViewModel, - @SuppressLint("ModifierParameter") modifier: Modifier = Modifier +fun DealList( + modifier: Modifier = Modifier, + navController: NavHostController, + currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), + dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory), + dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory) ) { - val argument = sharedViewModel.argument.value - val id = argument?.toInt() + val id = currentUserViewModel.user!!.id + dealListViewModel.setupLists() - val context = LocalContext.current - val deals = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).dealDao().getAll().collect { data -> - deals.clear() - deals.addAll(data) - } - } - } - val wallet = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - if (id != null) { - CryptoDealDb.getInstance(context).userDao().getUserWallet(id).collect{data -> - wallet.clear(); - wallet.addAll(data); - } - } - } - } + val deals = remember { mutableStateOf(dealListViewModel.deals) } + val coins = remember { mutableStateOf(dealListViewModel.coins) } - val coins = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).coinDao().getAll().collect { data -> - coins.clear() - coins.addAll(data) - } - } - } - LazyColumn ( + LazyColumn( verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier @@ -117,27 +75,32 @@ fun DealList(navController: NavHostController, PropertyDeal(navController = navController) } item { - deals.filter { x -> x.date == null }.forEach{ x -> Deal( - deal = x, - coins = coins, - modifier = Modifier.padding(vertical = 5.dp), - id = id, - navController = navController, - sharedViewModel = sharedViewModel, - wallet = wallet)} + deals.value.filter { x -> x.date == null }.forEach { x -> + DealItem( + deal = x, + coins = coins.value, + modifier = Modifier.padding(vertical = 5.dp), + id = id, + navController = navController, + dealCreateViewModel = dealCreateViewModel, + dealListViewModel = dealListViewModel + ) + } } } } @RequiresApi(Build.VERSION_CODES.O) @Composable -fun Deal(modifier: Modifier = Modifier, - deal: Deal, - coins: List, - id: Int?, - navController: NavHostController, - sharedViewModel: SharedViewModel, - wallet: List) { +fun DealItem( + modifier: Modifier = Modifier, + deal: Deal, + coins: List, + id: Int?, + navController: NavHostController, + dealCreateViewModel: DealCreateViewModel = viewModel(factory = AppViewModelProvider.Factory), + dealListViewModel: DealListViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { Column( verticalArrangement = Arrangement.spacedBy(10.dp, Alignment.Top), horizontalAlignment = Alignment.CenterHorizontally, @@ -177,14 +140,16 @@ fun Deal(modifier: Modifier = Modifier, ) ) { Text( - text = coins.first{x -> x.id == deal.buyerCoinId}.shortName(), + text = coins.first { x -> x.id == deal.buyerCoinId }.shortName(), color = Color.White, lineHeight = 1.em, style = TextStyle( fontSize = 20.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } Text( text = deal.countBuy.toString(), @@ -193,10 +158,12 @@ fun Deal(modifier: Modifier = Modifier, lineHeight = 1.em, style = TextStyle( fontSize = 20.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier .fillMaxWidth() - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } Text( text = deal.tip, @@ -206,11 +173,13 @@ fun Deal(modifier: Modifier = Modifier, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier .fillMaxWidth() .requiredHeight(height = 40.dp) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) Row( horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally), verticalAlignment = Alignment.CenterVertically, @@ -225,33 +194,9 @@ fun Deal(modifier: Modifier = Modifier, Button( onClick = { if (id != null && id != deal.buyerId) { -// 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 -// } + deal.buyerId?.let { dealListViewModel.submitDeal(deal, id, it) } } else { - sharedViewModel.setArgumentEdit(deal) - sharedViewModel.setArgumentAdd(wallet, coins) + deal.id?.let { dealCreateViewModel.setupEdit(it) } navController.navigate(BottomBarScreen.CDEAL.route) { popUpTo(navController.graph.findStartDestination().id) launchSingleTop = true @@ -265,7 +210,10 @@ fun Deal(modifier: Modifier = Modifier, .weight(weight = 0.5f) ) { Row( - horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally), + horizontalArrangement = Arrangement.spacedBy( + 10.dp, + Alignment.CenterHorizontally + ), verticalAlignment = Alignment.CenterVertically, modifier = Modifier .requiredWidth(width = 168.dp) @@ -278,9 +226,11 @@ fun Deal(modifier: Modifier = Modifier, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .requiredWidth(width = 29.dp)) + .requiredWidth(width = 29.dp) + ) } } Row( @@ -294,14 +244,16 @@ fun Deal(modifier: Modifier = Modifier, .padding(horizontal = 10.dp) ) { Text( - text = coins.first{x -> x.id == deal.sellerCoinId}.shortName(), + text = coins.first { x -> x.id == deal.sellerCoinId }.shortName(), color = Color.White, textAlign = TextAlign.End, lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp)) + letterSpacing = 0.1.sp + ) + ) Text( text = "${deal.countSell}\n", color = Color.White, @@ -310,10 +262,12 @@ fun Deal(modifier: Modifier = Modifier, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier .fillMaxWidth() - .requiredHeight(height = 20.dp)) + .requiredHeight(height = 20.dp) + ) } } } @@ -345,7 +299,9 @@ fun PropertyDeal(modifier: Modifier = Modifier, navController: NavHostController lineHeight = 1.14.em, style = TextStyle( fontSize = 14.sp, - fontWeight = FontWeight.Medium)) + fontWeight = FontWeight.Medium + ) + ) Text( text = "+", color = Color(0xff4bb2f9), @@ -353,7 +309,8 @@ fun PropertyDeal(modifier: Modifier = Modifier, navController: NavHostController lineHeight = 0.83.em, style = TextStyle( fontSize = 24.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier .clickable { navController.navigate(BottomBarScreen.CDEAL.route) { diff --git a/app/src/main/java/com/example/testapp/screensMobile/EntryScreen.kt b/app/src/main/java/com/example/testapp/screensMobile/EntryScreen.kt deleted file mode 100644 index 3d31801..0000000 --- a/app/src/main/java/com/example/testapp/screensMobile/EntryScreen.kt +++ /dev/null @@ -1,189 +0,0 @@ -package com.example.testapp.screensMobile - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredHeight -import androidx.compose.foundation.layout.requiredWidth -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf -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 -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.em -import androidx.compose.ui.unit.sp -import androidx.navigation.NavController -import com.example.testapp.designElem.Btn -import com.example.testapp.designElem.SharedViewModel -import com.example.testapp.designElem.btnConfig -import com.example.testapp.graphs.AuthScreen -import com.example.testapp.graphs.Graph -import com.example.testapp.room.database.CryptoDealDb -import com.example.testapp.room.models.User -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -fun isValidEmail(email: String): Boolean { - val emailRegex = "[a-zA-Z0-9._-]+@[a-z]+\\.+[a-z]+".toRegex() - return email.matches(emailRegex) -} - -@Composable -fun EntryScreen(navController: NavController, - modifier: Modifier = Modifier, - sharedViewModel: SharedViewModel) { - var emailValue by rememberSaveable { mutableStateOf("") } - var passwordValue by rememberSaveable { mutableStateOf("") } - val context = LocalContext.current - val users = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).userDao().getAll().collect { data -> - users.clear() - users.addAll(data) - } - } - } - - Box( - modifier = modifier - .fillMaxSize() - .background(color = Color.White) - ) { - Column( - verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset(x = 0.dp, - y = 40.dp) - ) { - Text( - text = "Sign On", - color = Color(0xff0b1f33), - textAlign = TextAlign.Center, - style = TextStyle( - fontSize = 24.sp, - fontWeight = FontWeight.Medium), - modifier = Modifier - .fillMaxSize()) - } - Column( - verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset(x = 0.dp, y = 278.dp) - .padding(start = 5.dp, end = 5.dp) - ) { - TextField( - value = emailValue, - onValueChange = { emailValue = it }, - label = { - Text( - text = "Email", - color = Color(0xff66727f), - lineHeight = 1.33.em, - style = TextStyle( - fontSize = 15.sp, - letterSpacing = 0.2.sp) - ) - }, - placeholder = { Text("Enter email") }, - textStyle = TextStyle( - fontSize = 16.sp, - letterSpacing = 0.1.sp), - modifier = Modifier - .fillMaxWidth()) - TextField( - value = passwordValue, - onValueChange = { passwordValue = it }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - label = { - Text( - text = "Password", - color = Color(0xff66727f), - lineHeight = 1.33.em, - style = TextStyle( - fontSize = 15.sp, - letterSpacing = 0.2.sp)) - }, - placeholder = { Text("Enter password") }, - textStyle = TextStyle( - fontSize = 16.sp, - letterSpacing = 0.1.sp), - modifier = Modifier - .fillMaxWidth()) - } - Row( - horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset(x = 0.dp, - y = 602.dp) - .padding(horizontal = 5.dp, - vertical = 5.dp) - .fillMaxWidth() - ) { - Btn(btnConfig = btnConfig( - onClick = { - if (passwordValue.isNotEmpty() && isValidEmail(emailValue)) { - users.forEach { user -> - if (user.password == passwordValue && user.email == emailValue) { - sharedViewModel.setArgument(user.id.toString()) - navController.navigate(route = Graph.passUserId(user.id.toString())) - } - } - }}, - text = "Sign In", - color = Color(0xff85c3ff), - offsetX = 0.dp, - offsetY = 0.dp - ), modifier = Modifier.fillMaxWidth()) - } - Row( - horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset(x = 0.dp, - y = 656.dp) - .padding(horizontal = 5.dp, - vertical = 5.dp) - .fillMaxWidth() - ) { - Btn(btnConfig = btnConfig( - onClick = { - navController.navigate(route = AuthScreen.Register.route) - { - popUpTo(AuthScreen.Register.route) - } - }, - text = "Sign On", - color = Color(0xff85c3f3), - offsetX = 0.dp, - offsetY = 0.dp - ), modifier = Modifier.fillMaxWidth()) - } - } -} \ No newline at end of file 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 aa64ffb..96687a8 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/History.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/History.kt @@ -1,6 +1,5 @@ package com.example.testapp.screensMobile -import android.annotation.SuppressLint import android.os.Build import androidx.annotation.RequiresApi import androidx.compose.foundation.background @@ -18,14 +17,12 @@ 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.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString @@ -34,47 +31,27 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController -import com.example.testapp.designElem.SharedViewModel -import com.example.testapp.room.database.CryptoDealDb import com.example.testapp.room.models.Coin import com.example.testapp.room.models.Deal -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import com.example.testapp.viewModels.AppViewModelProvider +import com.example.testapp.viewModels.CurrentUserViewModel +import com.example.testapp.viewModels.HistoryViewModel @RequiresApi(Build.VERSION_CODES.O) @Composable -fun History(navController: NavHostController = rememberNavController(), - @SuppressLint("ModifierParameter") modifier: Modifier = Modifier, - sharedViewModel: SharedViewModel +fun History( + modifier: Modifier = Modifier, + navController: NavHostController = rememberNavController(), + currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), + historyViewModel: HistoryViewModel = viewModel(factory = AppViewModelProvider.Factory) ) { - val argument = sharedViewModel.argument.value - val id = argument?.toInt() - val context = LocalContext.current - val deals = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).dealDao().getAll().collect { data -> - deals.clear() - deals.addAll(data) - } - } - } - - val coins = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).coinDao().getAll().collect { data -> - coins.clear() - coins.addAll(data) - } - } - } - var history: List = listOf(); - if (id != null) { - history = deals.filter { x -> x.buyerId == id || x.sellerId == id } - } + val id = currentUserViewModel.user!!.id + historyViewModel.setArgument(id.toString()); + val history = remember { mutableStateOf(historyViewModel.deals) } + val coins = remember { mutableStateOf(historyViewModel.coins) } Column( verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), @@ -86,8 +63,14 @@ fun History(navController: NavHostController = rememberNavController(), .verticalScroll(rememberScrollState()) ) { PropertyHistory() - history.forEach { - x -> HistoryCard(deal = x, modifier = Modifier.padding( vertical = 5.dp, horizontal = 5.dp), coins = coins) + history.value?.forEach { x -> + coins.value?.let { + HistoryCard( + deal = x, + modifier = Modifier.padding(vertical = 5.dp, horizontal = 5.dp), + coins = it + ) + } } } } @@ -119,23 +102,31 @@ fun HistoryCard(modifier: Modifier = Modifier, deal: Deal, coins: List) { .padding(all = 10.dp) ) { Text( - text = "-${deal.countSell} ${coins.first{x -> x.id == deal.sellerCoinId}.shortName()}", + text = "-${deal.countSell} ${ + coins.first { x -> x.id == deal.sellerCoinId }.shortName() + }", color = Color(0xfff96161), lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) Text( - text = "+${deal.countBuy} ${coins.first{x -> x.id == deal.buyerCoinId}.shortName()}", + text = "+${deal.countBuy} ${ + coins.first { x -> x.id == deal.buyerCoinId }.shortName() + }", color = Color(0xff5acb48), lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } Column( verticalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterVertically), @@ -152,22 +143,31 @@ fun HistoryCard(modifier: Modifier = Modifier, deal: Deal, coins: List) { lineHeight = 1.25.em, style = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } Text( lineHeight = 1.sp, text = buildAnnotatedString { - withStyle(style = SpanStyle( - color = Color(0xff1e1e1e), - fontSize = 16.sp) - ) {append("Status: ")} - withStyle(style = SpanStyle( - color = Color(color), - fontSize = 16.sp)) {append(text)}}, + withStyle( + style = SpanStyle( + color = Color(0xff1e1e1e), + fontSize = 16.sp + ) + ) { append("Status: ") } + withStyle( + style = SpanStyle( + color = Color(color), + fontSize = 16.sp + ) + ) { append(text) } + }, modifier = Modifier - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } } @@ -197,9 +197,11 @@ fun PropertyHistory(modifier: Modifier = Modifier) { lineHeight = 1.14.em, style = TextStyle( fontSize = 14.sp, - fontWeight = FontWeight.Medium), + fontWeight = FontWeight.Medium + ), modifier = Modifier - .fillMaxWidth()) + .fillMaxWidth() + ) } } } 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 511047a..10dae7f 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/Wallet.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/Wallet.kt @@ -11,79 +11,45 @@ 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 import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle 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.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.room.database.CryptoDealDb -import com.example.testapp.room.models.Coin -import com.example.testapp.room.models.User -import com.example.testapp.room.models.WalletItem -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import com.example.testapp.viewModels.AppViewModelProvider +import com.example.testapp.viewModels.CurrentUserViewModel +import com.example.testapp.viewModels.DealCreateViewModel +import com.example.testapp.viewModels.DealListViewModel +import com.example.testapp.viewModels.HistoryViewModel +import com.example.testapp.viewModels.WalletViewModel @RequiresApi(Build.VERSION_CODES.O) @Composable -fun Wallet(navController: NavHostController = rememberNavController(), - @SuppressLint("ModifierParameter") modifier: Modifier = Modifier, - sharedViewModel: SharedViewModel) { - val argument = sharedViewModel.argument.value - val id = argument?.toInt() - val context = LocalContext.current - val users = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).userDao().getAll().collect { data -> - users.clear() - users.addAll(data) - } - } - } - var wallet: List = listOf() - val walletItem = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - if (id != null) { - CryptoDealDb.getInstance(context).walletItemDao().getAll().collect{ data -> - walletItem.clear() - walletItem.addAll(data) - } - } - } - } - - wallet = walletItem.filter { x -> x.userId == id }; - - val coins = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).coinDao().getAll().collect { data -> - coins.clear() - coins.addAll(data) - } - } - } +fun Wallet( + modifier: Modifier = Modifier, + navController: NavHostController = rememberNavController(), + 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) } LazyColumn( verticalArrangement = Arrangement.spacedBy(5.dp, Alignment.Top), @@ -97,12 +63,14 @@ fun Wallet(navController: NavHostController = rememberNavController(), PropertyWallet() } item { - wallet.forEach { coin -> - ListItem( - coin = coins.first{ x -> x.id == coin.coinId }, - count = coin.count, - modifier = Modifier.padding( vertical = 5.dp) - ) + wallet.value?.forEach { coin -> + coins.value?.let { + ListItem( + coin = it.first { x -> x.id == coin.coinId }, + count = coin.count, + modifier = Modifier.padding(vertical = 5.dp) + ) + } } } } @@ -110,15 +78,28 @@ fun Wallet(navController: NavHostController = rememberNavController(), @RequiresApi(34) @Composable -fun LoadScreen(navController: NavHostController = rememberNavController(), - @SuppressLint("ModifierParameter") modifier: Modifier = Modifier, - sharedViewModel: SharedViewModel) { +fun LoadScreen( + 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) +) { Scaffold( bottomBar = { NavBar(navController = navController) }, ) { Modifier .padding(it) - HomeNavGraph(navController, sharedViewModel) + HomeNavGraph( + navController, + currentUserViewModel, + dealCreateViewModel, + dealListViewModel, + historyViewModel, + walletViewModel + ) } } @@ -147,9 +128,11 @@ fun PropertyWallet(modifier: Modifier = Modifier) { lineHeight = 1.14.em, style = TextStyle( fontSize = 14.sp, - fontWeight = FontWeight.Medium), + fontWeight = FontWeight.Medium + ), modifier = Modifier - .fillMaxWidth()) + .fillMaxWidth() + ) } } } 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 new file mode 100644 index 0000000..3417cb9 --- /dev/null +++ b/app/src/main/java/com/example/testapp/screensMobile/authScreens/EntryScreen.kt @@ -0,0 +1,226 @@ +package com.example.testapp.screensMobile.authScreens + +import android.annotation.SuppressLint +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import com.example.testapp.designElem.Btn +import com.example.testapp.designElem.btnConfig +import com.example.testapp.graphs.AuthScreen +import com.example.testapp.graphs.Graph +import com.example.testapp.viewModels.AppViewModelProvider +import com.example.testapp.viewModels.CurrentUserViewModel +import com.example.testapp.viewModels.EntryScreenViewModel + +fun isValidEmail(email: String): Boolean { + val emailRegex = "[a-zA-Z0-9._-]+@[a-z]+\\.+[a-z]+".toRegex() + return email.matches(emailRegex) +} + +@OptIn(ExperimentalMaterial3Api::class) +@SuppressLint("UnrememberedMutableState") +@Composable +fun EntryScreen( + navController: NavController, + modifier: Modifier = Modifier, + currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), + entryScreenViewModel: EntryScreenViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + var emailValue by rememberSaveable { mutableStateOf("") } + 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 + .fillMaxSize() + .background(color = Color.White) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), + modifier = Modifier + .align(alignment = Alignment.TopStart) + .offset( + x = 0.dp, + y = 40.dp + ) + ) { + Text( + text = "Sign On", + color = Color(0xff0b1f33), + textAlign = TextAlign.Center, + style = TextStyle( + fontSize = 24.sp, + fontWeight = FontWeight.Medium + ), + modifier = Modifier + .fillMaxSize() + ) + } + Column( + verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), + modifier = Modifier + .align(alignment = Alignment.TopStart) + .offset(x = 0.dp, y = 278.dp) + .padding(start = 5.dp, end = 5.dp) + ) { + TextField( + value = emailValue, + onValueChange = { emailValue = it }, + label = { + Text( + text = "Email", + color = Color(0xff66727f), + lineHeight = 1.33.em, + style = TextStyle( + fontSize = 15.sp, + letterSpacing = 0.2.sp + ) + ) + }, + placeholder = { Text("Enter email") }, + textStyle = TextStyle( + fontSize = 16.sp, + letterSpacing = 0.1.sp + ), + modifier = Modifier + .fillMaxWidth() + ) + TextField( + value = passwordValue, + onValueChange = { passwordValue = it }, + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White, + disabledContainerColor = Color.White, + ), + modifier = modifier + .fillMaxWidth() + .shadow(shape = RoundedCornerShape(40.dp), elevation = 5.dp) + .clip(shape = RoundedCornerShape(40.dp)), + label = { Text("Password") }, + singleLine = true, + placeholder = { Text("Enter password") }, + visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + trailingIcon = { + val image = if (passwordVisibility) + Icons.Filled.Visibility + else Icons.Filled.VisibilityOff + + val description = + if (passwordVisibility) "Hide password" else "Show password" + + IconButton(onClick = { passwordVisibility = !passwordVisibility }) { + Icon(imageVector = image, description) + } + }) + } + Row( + horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .align(alignment = Alignment.TopStart) + .offset( + x = 0.dp, + y = 602.dp + ) + .padding( + horizontal = 5.dp, + vertical = 5.dp + ) + .fillMaxWidth() + ) { + Btn( + btnConfig = btnConfig( + onClick = { + if (passwordValue.isNotEmpty() && isValidEmail(emailValue)) { + users.value.forEach { user -> + if (user.password == passwordValue && user.email == emailValue) { + currentUserViewModel.setArgument(user.id.toString()) + navController.navigate(route = Graph.passUserId(user.id.toString())) + } + } + } + }, + text = "Sign In", + color = Color(0xff85c3ff), + offsetX = 0.dp, + offsetY = 0.dp + ), modifier = Modifier.fillMaxWidth() + ) + } + Row( + horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .align(alignment = Alignment.TopStart) + .offset( + x = 0.dp, + y = 656.dp + ) + .padding( + horizontal = 5.dp, + vertical = 5.dp + ) + .fillMaxWidth() + ) { + Btn( + btnConfig = btnConfig( + onClick = { + navController.navigate(route = AuthScreen.Register.route) + { + popUpTo(AuthScreen.Register.route) + } + }, + text = "Sign On", + color = Color(0xff85c3f3), + offsetX = 0.dp, + offsetY = 0.dp + ), modifier = Modifier.fillMaxWidth() + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/screensMobile/RegisterScreen.kt b/app/src/main/java/com/example/testapp/screensMobile/authScreens/RegisterScreen.kt similarity index 70% rename from app/src/main/java/com/example/testapp/screensMobile/RegisterScreen.kt rename to app/src/main/java/com/example/testapp/screensMobile/authScreens/RegisterScreen.kt index 245ff2b..99d64d7 100644 --- a/app/src/main/java/com/example/testapp/screensMobile/RegisterScreen.kt +++ b/app/src/main/java/com/example/testapp/screensMobile/authScreens/RegisterScreen.kt @@ -1,5 +1,7 @@ -package com.example.testapp.screensMobile +package com.example.testapp.screensMobile.authScreens +import android.annotation.SuppressLint +import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -18,18 +20,15 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -37,34 +36,32 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import com.example.testapp.designElem.SharedViewModel import com.example.testapp.graphs.AuthScreen import com.example.testapp.graphs.Graph -import com.example.testapp.room.database.CryptoDealDb import com.example.testapp.room.models.User -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import com.example.testapp.viewModels.CurrentUserViewModel +import com.example.testapp.viewModels.RegistrationScreenViewModel +import kotlinx.coroutines.async import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +@SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun RegisterScreen(navController: NavController, - modifier: Modifier = Modifier, - sharedViewModel: SharedViewModel) { +fun RegisterScreen( + navController: NavController, + modifier: Modifier = Modifier, + currentUserViewModel: CurrentUserViewModel, + registrationScreenViewModel: RegistrationScreenViewModel +) { var emailValue by rememberSaveable { mutableStateOf("") } var passwordValue by rememberSaveable { mutableStateOf("") } var passwordConfirmValue by rememberSaveable { mutableStateOf("") } - val context = LocalContext.current - val users = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - CryptoDealDb.getInstance(context).userDao().getAll().collect { data -> - users.clear() - users.addAll(data) - } - } + registrationScreenViewModel.setUserList() + val users = mutableStateOf>(emptyList()) + registrationScreenViewModel.users.observeForever { userList -> + users.value = userList } + val coroutineScope = rememberCoroutineScope() Box( modifier = modifier @@ -75,8 +72,10 @@ fun RegisterScreen(navController: NavController, verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Top), modifier = Modifier .align(alignment = Alignment.TopStart) - .offset(x = 0.dp, - y = 40.dp) + .offset( + x = 0.dp, + y = 40.dp + ) ) { Text( text = "Sign On", @@ -95,8 +94,10 @@ fun RegisterScreen(navController: NavController, modifier = Modifier .align(alignment = Alignment.TopStart) .padding(start = 5.dp, end = 5.dp) - .offset(x = 0.dp, - y = 232.dp) + .offset( + x = 0.dp, + y = 232.dp + ) ) { TextField( value = emailValue, @@ -108,14 +109,18 @@ fun RegisterScreen(navController: NavController, lineHeight = 1.33.em, style = TextStyle( fontSize = 15.sp, - letterSpacing = 0.2.sp)) + letterSpacing = 0.2.sp + ) + ) }, placeholder = { Text("Enter email") }, textStyle = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .fillMaxWidth()) + .fillMaxWidth() + ) TextField( value = passwordValue, onValueChange = { passwordValue = it }, @@ -126,14 +131,18 @@ fun RegisterScreen(navController: NavController, lineHeight = 1.33.em, style = TextStyle( fontSize = 15.sp, - letterSpacing = 0.2.sp)) + letterSpacing = 0.2.sp + ) + ) }, placeholder = { Text("Enter password") }, textStyle = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .fillMaxWidth()) + .fillMaxWidth() + ) TextField( value = passwordConfirmValue, onValueChange = { passwordConfirmValue = it }, @@ -144,40 +153,49 @@ fun RegisterScreen(navController: NavController, lineHeight = 1.33.em, style = TextStyle( fontSize = 15.sp, - letterSpacing = 0.2.sp)) + letterSpacing = 0.2.sp + ) + ) }, placeholder = { Text("Confirm password") }, textStyle = TextStyle( fontSize = 16.sp, - letterSpacing = 0.1.sp), + letterSpacing = 0.1.sp + ), modifier = Modifier - .fillMaxWidth()) + .fillMaxWidth() + ) } TextButton( onClick = { var isExist = false; if (passwordValue.isNotEmpty() && isValidEmail(emailValue)) { - users.forEach { user -> + users.value.forEach { user -> if (user.email == emailValue) { + Log.d("User already exist. User id: ", user.id.toString()) isExist = true } } - if (!isExist) { val newUser = User(null, emailValue, passwordValue) - CoroutineScope(Dispatchers.IO).launch { - CryptoDealDb.getInstance(context).userDao().insert(newUser) - CryptoDealDb.getInstance(context).userDao().getAll() - .collect { data -> - data.forEach { user -> - if ((user.password == passwordValue) && (user.email == emailValue)) { - withContext(Dispatchers.Main) { - sharedViewModel.setArgument(user.id.toString()) - navController.navigate(route = Graph.passUserId(user.id.toString())) - } - } + coroutineScope.launch { + val insertResult = async { + registrationScreenViewModel.insert(newUser) + } + + insertResult.await() + + registrationScreenViewModel.setUserList() + registrationScreenViewModel.users.observeForever { userList -> + users.value = userList + Log.println(Log.ASSERT, "UsersList", users.value.toString()) + users.value.forEach { user -> + if (user.password == passwordValue && user.email == emailValue) { + currentUserViewModel.setArgument(user.id.toString()) + navController.navigate(route = Graph.passUserId(user.id.toString())) } } + } } } } @@ -185,8 +203,10 @@ fun RegisterScreen(navController: NavController, colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), modifier = Modifier .align(alignment = Alignment.TopStart) - .offset(x = 0.dp, - y = 656.dp) + .offset( + x = 0.dp, + y = 656.dp + ) ) { Column( verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top) @@ -206,8 +226,10 @@ fun RegisterScreen(navController: NavController, modifier = Modifier .align(alignment = Alignment.TopStart) .fillMaxWidth() - .offset(x = 0.dp, - y = 602.dp) + .offset( + x = 0.dp, + y = 602.dp + ) ) { Row( horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally), @@ -220,7 +242,9 @@ fun RegisterScreen(navController: NavController, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp)) + letterSpacing = 0.1.sp + ) + ) } } } @@ -240,8 +264,10 @@ fun Property1Primary(modifier: Modifier = Modifier) { verticalAlignment = Alignment.CenterVertically, modifier = Modifier .align(alignment = Alignment.Center) - .offset(x = 0.dp, - y = 0.dp) + .offset( + x = 0.dp, + y = 0.dp + ) ) { Column( horizontalAlignment = Alignment.CenterHorizontally @@ -253,7 +279,9 @@ fun Property1Primary(modifier: Modifier = Modifier) { style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - letterSpacing = 0.1.sp)) + letterSpacing = 0.1.sp + ) + ) } } } diff --git a/app/src/main/java/com/example/testapp/viewModels/AppViewModelProvider.kt b/app/src/main/java/com/example/testapp/viewModels/AppViewModelProvider.kt new file mode 100644 index 0000000..9504eb3 --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/AppViewModelProvider.kt @@ -0,0 +1,49 @@ +package com.example.testapp.viewModels + +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.example.testapp.CryptoDealApplication + +object AppViewModelProvider { + val Factory = viewModelFactory { + initializer { + CurrentUserViewModel(application().container.userRepository) + } + initializer { + DealListViewModel( + application().container.dealRepository, + application().container.coinRepository + ) + } + initializer { + DealCreateViewModel( + application().container.dealRepository, + application().container.coinRepository, + application().container.walletItemRepository + ) + } + initializer { + EntryScreenViewModel(application().container.userRepository) + } + initializer { + HistoryViewModel( + application().container.dealRepository, + application().container.coinRepository + ) + } + initializer { + RegistrationScreenViewModel(application().container.userRepository) + } + initializer { + WalletViewModel( + application().container.walletItemRepository, + application().container.coinRepository + ) + } + } +} + +fun CreationExtras.application(): CryptoDealApplication = + (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as CryptoDealApplication) \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/viewModels/CurrentUserViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/CurrentUserViewModel.kt new file mode 100644 index 0000000..b2aa04c --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/CurrentUserViewModel.kt @@ -0,0 +1,28 @@ +package com.example.testapp.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.testapp.room.models.User +import com.example.testapp.room.repository.basic.UserRepository +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) + + fun setArgument(arg: String) { + argument.value = arg + id.value = arg.toInt() + viewModelScope.launch { + user = userRepository.getById(id.value!!) + .filterNotNull() + .first() + } + } +} \ 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 new file mode 100644 index 0000000..3dc4398 --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/DealCreateViewModel.kt @@ -0,0 +1,56 @@ +package com.example.testapp.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.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.flow.first +import kotlinx.coroutines.launch + +class DealCreateViewModel( + private val dealRepository: DealRepository, + 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) + + var deal by mutableStateOf(null) + + var deals by mutableStateOf>(emptyList()) + var coins by mutableStateOf>(emptyList()) + var wallet by mutableStateOf>(emptyList()) + + suspend fun update(deal: Deal) = dealRepository.update(deal) + suspend fun create(deal: Deal) = dealRepository.insert(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(); + } + } + + fun isEdit(): Boolean { + return deal != null; + } + + fun setupEdit(id: Int) { + deal_id.value = id; + viewModelScope.launch { + deal = dealRepository.getById(id).first(); + } + } +} \ 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 new file mode 100644 index 0000000..8aede0d --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/DealListViewModel.kt @@ -0,0 +1,32 @@ +package com.example.testapp.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.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.flow.first +import kotlinx.coroutines.launch + +class DealListViewModel( + private val dealRepository: DealRepository, + private val coinRepository: CoinRepository, +) : ViewModel() { + var deals by mutableStateOf>(emptyList()) + var coins by mutableStateOf>(emptyList()) + + fun setupLists() { + viewModelScope.launch { + deals = dealRepository.getAll().first(); + coins = coinRepository.getAll().first(); + } + } + + fun submitDeal(deal: Deal, sellerId: Int, buyerId: Int) { + dealRepository.completeDeal(deal, sellerId, buyerId) + } +} \ 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 new file mode 100644 index 0000000..757518a --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/EntryScreenViewModel.kt @@ -0,0 +1,22 @@ +package com.example.testapp.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.testapp.room.models.User +import com.example.testapp.room.repository.basic.UserRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +class EntryScreenViewModel( + private val userRepository: UserRepository +) : ViewModel() { + var userList by mutableStateOf>(emptyList()) + fun setupList() { + viewModelScope.launch { + userList = 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 new file mode 100644 index 0000000..05682ba --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/HistoryViewModel.kt @@ -0,0 +1,30 @@ +package com.example.testapp.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.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.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()) + + fun setArgument(arg: String) { + argument.value = arg + id.value = arg.toInt() + viewModelScope.launch { + deals = dealRepository.getUserDeals(id.value!!).first() + coins = coinRepository.getAll().first() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/testapp/viewModels/RegistrationScreenViewModel.kt b/app/src/main/java/com/example/testapp/viewModels/RegistrationScreenViewModel.kt new file mode 100644 index 0000000..44a7673 --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/RegistrationScreenViewModel.kt @@ -0,0 +1,25 @@ +package com.example.testapp.viewModels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +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.first +import kotlinx.coroutines.launch + +class RegistrationScreenViewModel(private val userRepository: UserRepository) : ViewModel() { + private val _users = MutableLiveData>() + val users: LiveData> get() = _users + + fun setUserList() { + viewModelScope.launch { + _users.value = userRepository.getAll().first() + } + } + + suspend fun insert(user: User) { + userRepository.insert(user) + } +} \ 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 new file mode 100644 index 0000000..eb40885 --- /dev/null +++ b/app/src/main/java/com/example/testapp/viewModels/WalletViewModel.kt @@ -0,0 +1,32 @@ +package com.example.testapp.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.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.flow.first +import kotlinx.coroutines.launch + +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()) + + fun setArgument(arg: String) { + argument.value = arg + id.value = arg.toInt() + viewModelScope.launch { + wallet = walletItemRepository.getUserWallet(id.value!!).first() + coins = coinRepository.getAll().first() + } + } +} \ No newline at end of file