From 8b47320daafb489aedf40266802c209d6bbd11fa Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Tue, 13 Jun 2023 22:58:33 +0700 Subject: [PATCH 01/65] Create composables --- .../groceriesstore/presentation/shop/ProductItem.kt | 9 +++++++++ .../groceriesstore/presentation/shop/ShopScreen.kt | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt create mode 100644 app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt new file mode 100644 index 00000000..7ec290ff --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt @@ -0,0 +1,9 @@ +package com.hieuwu.groceriesstore.presentation.shop + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun ProductItem(modifier: Modifier = Modifier) { + +} \ No newline at end of file diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt new file mode 100644 index 00000000..f15dddbb --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -0,0 +1,9 @@ +package com.hieuwu.groceriesstore.presentation.shop + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun ShopScreen(modifier: Modifier = Modifier) { + +} \ No newline at end of file From 64f031452f85e2ded377ba921b3ca06c17ebb183 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Tue, 13 Jun 2023 23:05:39 +0700 Subject: [PATCH 02/65] Implement product item --- .../presentation/shop/ProductItem.kt | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt index 7ec290ff..70079617 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt @@ -1,9 +1,44 @@ package com.hieuwu.groceriesstore.presentation.shop +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.Button +import androidx.compose.material.Card +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.hieuwu.groceriesstore.R @Composable fun ProductItem(modifier: Modifier = Modifier) { - + Card(modifier = modifier) { + Column(modifier = modifier) { + Image( + painterResource(R.drawable.car_ui_app_styled_view_background), + contentDescription = "", + contentScale = ContentScale.Crop, + modifier = modifier.width(120.dp) + ) + Text(text = "product name") + Text(text = "product description") + Row(modifier = modifier) { + Text(text = "23.4$") + Button( + onClick = { /*TODO*/ }, + modifier = modifier.size(48.dp) + ) { + + } + + } + } + + } + } \ No newline at end of file From 0e1cf13cb0282be0fa1ac8e29677b527e31d4c80 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Mon, 19 Jun 2023 22:45:36 +0700 Subject: [PATCH 03/65] Update model --- .../presentation/shop/ProductItem.kt | 14 +++++++++----- local.properties | 10 +++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt index 70079617..811c0841 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt @@ -14,21 +14,25 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.hieuwu.groceriesstore.R +import com.hieuwu.groceriesstore.domain.models.ProductModel @Composable -fun ProductItem(modifier: Modifier = Modifier) { +fun ProductItem( + modifier: Modifier = Modifier, + product: ProductModel +) { Card(modifier = modifier) { Column(modifier = modifier) { Image( - painterResource(R.drawable.car_ui_app_styled_view_background), + painterResource(R.drawable.colorful_carrot), contentDescription = "", contentScale = ContentScale.Crop, modifier = modifier.width(120.dp) ) - Text(text = "product name") - Text(text = "product description") + Text(text = product.name ?: "") + Text(text = product.description ?: "") Row(modifier = modifier) { - Text(text = "23.4$") + Text(text = "${product.price}$") Button( onClick = { /*TODO*/ }, modifier = modifier.size(48.dp) diff --git a/local.properties b/local.properties index d16ae2a2..537bd005 100644 --- a/local.properties +++ b/local.properties @@ -1,3 +1,11 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Mon Jun 19 22:39:27 ICT 2023 +sdk.dir=/Users/hieuvu/Library/Android/sdk API_KEY=YOUR_SUPABASE_KEY -SECRET=YOUR_SUPABASE_SECRET SUPABASE_URL=YOUR_SUPABASE_URL +SECRET=YOUR_SUPABASE_SECRET From 70612c07a0ee58e4ad5d9e0363bdc0c09e11dece Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:20:31 +0700 Subject: [PATCH 04/65] Remove unused files & create new ones --- .../groceriesstore/ExampleInstrumentedTest.kt | 23 --- .../GroceriesStoreDatabaseTest.kt | 148 ------------------ .../hieuwu/groceriesstore/LiveDataTestUtil.kt | 57 ------- .../presentation/shop/composables/Carousel.kt | 2 + .../shop/composables/ProductCatalogue.kt | 2 + .../shop/{ => composables}/ProductItem.kt | 0 6 files changed, 4 insertions(+), 228 deletions(-) delete mode 100644 app/src/androidTest/java/com/hieuwu/groceriesstore/ExampleInstrumentedTest.kt delete mode 100644 app/src/androidTest/java/com/hieuwu/groceriesstore/GroceriesStoreDatabaseTest.kt delete mode 100644 app/src/androidTest/java/com/hieuwu/groceriesstore/LiveDataTestUtil.kt create mode 100644 app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt create mode 100644 app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt rename app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/{ => composables}/ProductItem.kt (100%) diff --git a/app/src/androidTest/java/com/hieuwu/groceriesstore/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/hieuwu/groceriesstore/ExampleInstrumentedTest.kt deleted file mode 100644 index ca1f8a16..00000000 --- a/app/src/androidTest/java/com/hieuwu/groceriesstore/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,23 +0,0 @@ -// package com.hieuwu.groceriesstore -// -// import androidx.test.InstrumentationRegistry -// import androidx.test.runner.AndroidJUnit4 -// import org.junit.Test -// import org.junit.runner.RunWith -// -// import org.junit.Assert.* -// -// /** -// * Instrumented test, which will execute on an Android device. -// * -// * See [testing documentation](http://d.android.com/tools/testing). -// */ -// @RunWith(AndroidJUnit4::class) -// class ExampleInstrumentedTest { -// @Test -// fun useAppContext() { -// // Context of the app under test. -// val appContext = InstrumentationRegistry.getInstrumentation().targetContext -// assertEquals("com.hieuwu.groceriesstore", appContext.packageName) -// } -// } diff --git a/app/src/androidTest/java/com/hieuwu/groceriesstore/GroceriesStoreDatabaseTest.kt b/app/src/androidTest/java/com/hieuwu/groceriesstore/GroceriesStoreDatabaseTest.kt deleted file mode 100644 index 561df9f9..00000000 --- a/app/src/androidTest/java/com/hieuwu/groceriesstore/GroceriesStoreDatabaseTest.kt +++ /dev/null @@ -1,148 +0,0 @@ -package com.hieuwu.groceriesstore - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.lifecycle.asLiveData -import androidx.room.Room -import androidx.test.InstrumentationRegistry -import androidx.test.runner.AndroidJUnit4 -import com.hieuwu.groceriesstore.data.database.GroceriesStoreDatabase -import com.hieuwu.groceriesstore.data.database.dao.LineItemDao -import com.hieuwu.groceriesstore.data.database.dao.OrderDao -import com.hieuwu.groceriesstore.data.database.dao.ProductDao -import com.hieuwu.groceriesstore.data.database.entities.LineItem -import com.hieuwu.groceriesstore.data.database.entities.Order -import com.hieuwu.groceriesstore.data.database.entities.Product -import com.hieuwu.groceriesstore.utilities.OrderStatus -import java.io.IOException -import junit.framework.Assert.assertEquals -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class GroceriesStoreDatabaseTest { - @get:Rule - val instantTaskExecutorRule = InstantTaskExecutorRule() - - private lateinit var productDao: ProductDao - private lateinit var linetItemDao: LineItemDao - private lateinit var orderDao: OrderDao - - private lateinit var db: GroceriesStoreDatabase - - @Before - fun createDb() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - // Using an in-memory database because the information stored here disappears when the - // process is killed. - db = Room.inMemoryDatabaseBuilder(context, GroceriesStoreDatabase::class.java) - // Allowing main thread queries, just for testing. - .allowMainThreadQueries() - .build() - productDao = db.productDao() - linetItemDao = db.lineItemDao() - orderDao = db.orderDao() - } - - @After - @Throws(IOException::class) - fun closeDb() { - db.close() - } - - @Test - @Throws(Exception::class) - suspend fun insertAndGetProduct() { - val aProduct = Product("123", "Product Test", "Test", 12.0, "", "", "") - withContext(Dispatchers.IO) { - productDao.insert(aProduct) - } - var product = productDao.getById(aProduct.id).asLiveData() - assertEquals(product.getOrAwaitValue().price, 12.0) - } - - @Test - @Throws(Exception::class) - suspend fun insertAndGetLineItem() { - val aProduct = Product("123", "Product Test", "Test", 12.0, "", "", "") - withContext(Dispatchers.IO) { - productDao.insert(aProduct) - } - val lineItem = LineItem(1, aProduct.id, "123", 4, 5.6) - linetItemDao.insert(lineItem) - var lineItemAfter = linetItemDao.getById(lineItem.id).asLiveData() - - assertEquals(lineItemAfter.getOrAwaitValue().productId, "123") - assertEquals(lineItemAfter.getOrAwaitValue().quantity, 4) - assertEquals(lineItemAfter.getOrAwaitValue().subtotal, 5.6) - } - - @Test - @Throws(Exception::class) - suspend fun insertAndGetOrderWithItems() { - val firstProduct = Product("1", "First Product", "Test", 12.0, "empty", "", "") - val secondProduct = Product("2", "Second Product", "Test", 13.0, "empty", "", "") - withContext(Dispatchers.IO) { - productDao.insert(firstProduct) - productDao.insert(secondProduct) - } - - val order = Order("12", OrderStatus.IN_CART.value, "") - - val firstLineItem = LineItem(1, firstProduct.id, "12", 4, 5.6) - val secondLineItem = LineItem(2, secondProduct.id, "12", 5, 7.0) - - orderDao.insert(order) - linetItemDao.insert(firstLineItem) - linetItemDao.insert(secondLineItem) - - var completedOrder = orderDao.getById("12") - var a = completedOrder - assertEquals(completedOrder, null) - } - - @Test - @Throws(Exception::class) - fun insertAndGetCurrentCart() { - val order = Order("12", OrderStatus.IN_CART.value, "") - orderDao.insert(order) - - var cart = orderDao.getCart(OrderStatus.IN_CART.value).asLiveData() - var a = cart.getOrAwaitValue() - assertEquals(true, true) - } - - @Test - @Throws(Exception::class) - suspend fun removeItem() { - val firstProduct = Product("1", "First Product", "Test", 12.0, "empty", "", "") - val secondProduct = Product("2", "Second Product", "Test", 13.0, "empty", "", "") - - withContext(Dispatchers.IO) { - productDao.insert(firstProduct) - productDao.insert(secondProduct) - } - - val order = Order("12", OrderStatus.IN_CART.value, "") - - val firstLineItem = LineItem(1, firstProduct.id, "12", 4, 5.6) - val secondLineItem = LineItem(2, secondProduct.id, "12", 5, 7.0) - - orderDao.insert(order) - linetItemDao.insert(firstLineItem) - linetItemDao.insert(secondLineItem) - var cart = orderDao.getCart(OrderStatus.IN_CART.value).asLiveData() - - withContext(Dispatchers.IO) { - linetItemDao.remove(firstLineItem) - } - - var list = linetItemDao.getAll().asLiveData() - var data = list.getOrAwaitValue() - assertEquals(null, null) - } -} diff --git a/app/src/androidTest/java/com/hieuwu/groceriesstore/LiveDataTestUtil.kt b/app/src/androidTest/java/com/hieuwu/groceriesstore/LiveDataTestUtil.kt deleted file mode 100644 index c1e48d19..00000000 --- a/app/src/androidTest/java/com/hieuwu/groceriesstore/LiveDataTestUtil.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.hieuwu.groceriesstore - -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException - -/** - * Gets the value of a [LiveData] or waits for it to have one, with a timeout. - * - * Use this extension from host-side (JVM) tests. It's recommended to use it alongside - * `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously. - */ -fun LiveData.getOrAwaitValue( - time: Long = 2, - timeUnit: TimeUnit = TimeUnit.SECONDS, - afterObserve: () -> Unit = {} -): T { - var data: T? = null - val latch = CountDownLatch(1) - val observer = object : Observer { - override fun onChanged(o: T?) { - data = o - latch.countDown() - this@getOrAwaitValue.removeObserver(this) - } - } - this.observeForever(observer) - - afterObserve.invoke() - - // Don't wait indefinitely if the LiveData is not set. - if (!latch.await(time, timeUnit)) { - this.removeObserver(observer) - throw TimeoutException("LiveData value was never set.") - } - - @Suppress("UNCHECKED_CAST") - return data as T -} diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt new file mode 100644 index 00000000..5d6de762 --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt @@ -0,0 +1,2 @@ +package com.hieuwu.groceriesstore.presentation.shop.composables + diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt new file mode 100644 index 00000000..5d6de762 --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt @@ -0,0 +1,2 @@ +package com.hieuwu.groceriesstore.presentation.shop.composables + diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt similarity index 100% rename from app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ProductItem.kt rename to app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt From 4deb4848d37d641d8a2c085543008decb1f3ea5b Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:20:43 +0700 Subject: [PATCH 05/65] Replace view with composable --- .../presentation/shop/ShopFragment.kt | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt index 45ed392b..b9522111 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt @@ -8,6 +8,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout +import androidx.compose.ui.platform.ComposeView import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment @@ -23,6 +24,7 @@ import com.hieuwu.groceriesstore.databinding.FragmentShopBinding import com.hieuwu.groceriesstore.domain.models.ProductModel import com.hieuwu.groceriesstore.presentation.adapters.GridListItemAdapter import com.hieuwu.groceriesstore.presentation.adapters.ViewPagerAdapter +import com.hieuwu.groceriesstore.presentation.orderhistory.OrderHistoryScreen import com.hieuwu.groceriesstore.utilities.showMessageSnackBar import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -43,25 +45,30 @@ class ShopFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - binding = DataBindingUtil.inflate( - inflater, R.layout.fragment_shop, container, false - ) - - binding.viewModel = viewModel - binding.lifecycleOwner = this - nonActiveDot = - ContextCompat.getDrawable(requireContext(), R.drawable.non_active_dot_shape)!! - activeDot = ContextCompat.getDrawable(requireContext(), R.drawable.active_dot_shape)!! - setObserver() - setUpRecyclerView() - drawSliderDotSymbols() - setEventListener() - - val inflater = TransitionInflater.from(requireContext()) - exitTransition = inflater.inflateTransition(R.transition.fade) - - - return binding.root + return ComposeView(requireContext()).apply { + setContent { + ShopScreen() + } + } +// binding = DataBindingUtil.inflate( +// inflater, R.layout.fragment_shop, container, false +// ) +// +// binding.viewModel = viewModel +// binding.lifecycleOwner = this +// nonActiveDot = +// ContextCompat.getDrawable(requireContext(), R.drawable.non_active_dot_shape)!! +// activeDot = ContextCompat.getDrawable(requireContext(), R.drawable.active_dot_shape)!! +// setObserver() +// setUpRecyclerView() +// drawSliderDotSymbols() +// setEventListener() +// +// val inflater = TransitionInflater.from(requireContext()) +// exitTransition = inflater.inflateTransition(R.transition.fade) +// +// +// return binding.root } private fun drawSliderDotSymbols() { From a12dd4807a81cbcc51a8c08593a98e0afbf19a9d Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:20:53 +0700 Subject: [PATCH 06/65] Fix wrong update value --- .../presentation/onboarding/OnboardingViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/onboarding/OnboardingViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/onboarding/OnboardingViewModel.kt index 1e4406bf..a3e9df45 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/onboarding/OnboardingViewModel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/onboarding/OnboardingViewModel.kt @@ -23,8 +23,8 @@ class OnboardingViewModel @Inject constructor( try { viewModelScope.launch { refreshAppDataUseCase.execute(Unit) + updateSyncStatus(true) } - updateSyncStatus(true) } catch (e: Exception) { updateSyncStatus(false) } From 7587f00acf3c4dae302c4ec066dd85a96cc5ca5f Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:21:02 +0700 Subject: [PATCH 07/65] Add preview tooling --- app/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 00354b81..1aca5070 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,10 +113,13 @@ dependencies { implementation "androidx.compose.ui:ui-tooling" implementation "com.google.accompanist:accompanist-themeadapter-material:0.28.0" implementation("androidx.hilt:hilt-navigation-compose:1.0.0") + implementation "com.github.bumptech.glide:compose:1.0.0-alpha.1" - + debugImplementation "androidx.compose.ui:ui-tooling:1.4.2" + implementation "androidx.compose.ui:ui-tooling-preview:1.4.2" def nav_version = "2.5.3" implementation("androidx.navigation:navigation-compose:$nav_version") + implementation "androidx.compose.material:material-icons-extended:1.0.0" implementation libs.caruilib From a29c2029fe1c4f388e4f3c1fef14004ff43b9a05 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:21:13 +0700 Subject: [PATCH 08/65] Create catalogue --- .../shop/composables/ProductCatalogue.kt | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt index 5d6de762..c3a46bf3 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt @@ -1,2 +1,88 @@ package com.hieuwu.groceriesstore.presentation.shop.composables +import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.hieuwu.groceriesstore.domain.models.ProductModel + +@Composable +fun ProductCatalogue( + modifier: Modifier, + products: List, + title: String +) { + Column(modifier = modifier.fillMaxWidth()) { + Row( + modifier = modifier + .padding(12.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(text = title) + Text(text = "Show all", modifier = modifier.clickable { + + }) + } + + LazyRow( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + contentPadding = PaddingValues(8.dp) + ) { + items(products) { item -> + ProductItem(product = item) + } + } + } +} + + +@Preview( + uiMode = Configuration.UI_MODE_NIGHT_NO +) +@Composable +fun ProductCataloguePreview(modifier: Modifier = Modifier) { + ProductCatalogue( + modifier = modifier, + title = "Best seller", + products = listOf( + ProductModel( + id = "fsdfsdds", + name = "Groeceries 1", + price = 2.4, + image = "", + ), + ProductModel( + id = "fsdfsdds", + name = "Groeceries 2", + price = 2.6, + image = "", + ), + ProductModel( + id = "fsdfsdds", + name = "Groeceries 2", + price = 2.6, + image = "", + ), + ProductModel( + id = "fsdfsdds", + name = "Groeceries 2", + price = 2.6, + image = "", + ) + ) + ) +} \ No newline at end of file From fb16cdf367b390d7ed1018a67eded640ad59cac8 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:21:18 +0700 Subject: [PATCH 09/65] Create carousel --- .../presentation/shop/composables/Carousel.kt | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt index 5d6de762..8e0e3e69 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt @@ -1,2 +1,99 @@ package com.hieuwu.groceriesstore.presentation.shop.composables +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage +import com.hieuwu.groceriesstore.R + +@OptIn(ExperimentalGlideComposeApi::class, ExperimentalFoundationApi::class) +@Composable +fun Carousel(modifier: Modifier = Modifier) { + Column( + modifier + .fillMaxWidth() + ) { + val sectionItemListState = rememberLazyListState() + val currentVisibleItemIndex = + remember { derivedStateOf { sectionItemListState.firstVisibleItemIndex } } + val currentVisibleItemScrollOffset = remember { + derivedStateOf { sectionItemListState.firstVisibleItemScrollOffset } + } + LaunchedEffect(currentVisibleItemIndex, currentVisibleItemScrollOffset) { + println("currentVisibleItemIndex") + } + LazyRow( + modifier = Modifier + .fillMaxWidth() + .height(180.dp), + state = sectionItemListState + ) { + items(bannerImages) { image -> + GlideImage( + contentScale = ContentScale.Crop, + model = image, + contentDescription = null, + modifier = modifier + .fillParentMaxWidth() + .animateItemPlacement() + ) + } + } + LazyRow( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + itemsIndexed(bannerImages) { curentIndex, image -> + IndicatorDot() + } + } + } +} + +private val bannerImages = listOf( + "https://firebasestorage.googleapis.com/v0/b/shopee-93233.appspot.com/o/product_image1664736648315.png?alt=media&token=2ca8be3a-37c3-4c73-b966-dcf3129958fd", + "https://firebasestorage.googleapis.com/v0/b/shopee-93233.appspot.com/o/product_image1664737376188.png?alt=media&token=01123878-812e-4b2b-be91-d9c05b1e9b98", + "https://firebasestorage.googleapis.com/v0/b/shopee-93233.appspot.com/o/product_image1664737421330.png?alt=media&token=66f45505-ce68-4583-b018-2d2855aa714e" +) + +@Preview +@Composable +fun CarouselPreview(modifier: Modifier = Modifier) { + Carousel(modifier = modifier.fillMaxWidth()) +} + +@Composable +fun IndicatorDot(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .padding(4.dp) + .size(12.dp) + .clip(CircleShape) + .background(colorResource(id = R.color.primary_button)) + ) +} \ No newline at end of file From ed09b12d851a299061bbcfe4cee385b6ec888452 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:21:24 +0700 Subject: [PATCH 10/65] Implement shop screen --- .../presentation/shop/ShopScreen.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt index f15dddbb..320e3a28 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -1,9 +1,24 @@ package com.hieuwu.groceriesstore.presentation.shop +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.hieuwu.groceriesstore.presentation.shop.composables.Carousel +import com.hieuwu.groceriesstore.presentation.shop.composables.ProductCatalogue @Composable -fun ShopScreen(modifier: Modifier = Modifier) { - +fun ShopScreen( + modifier: Modifier = Modifier, + viewModel: ShopViewModel = hiltViewModel() +) { + val products = viewModel.productList.collectAsState() + Column(modifier = modifier.padding(20.dp).fillMaxSize()) { + Carousel(modifier = modifier) + ProductCatalogue(modifier = modifier, products = products.value, title = "Best seller") + } } \ No newline at end of file From 5651bec2e55d1346a8a264f8fca1268413653cd4 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 12 Aug 2023 22:21:33 +0700 Subject: [PATCH 11/65] Implement product item --- .../shop/composables/ProductItem.kt | 74 +++++++++++++++---- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt index 811c0841..8dab699b 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt @@ -1,48 +1,94 @@ -package com.hieuwu.groceriesstore.presentation.shop +package com.hieuwu.groceriesstore.presentation.shop.composables +import android.content.res.Configuration import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults import androidx.compose.material.Card +import androidx.compose.material.Icon import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage import com.hieuwu.groceriesstore.R import com.hieuwu.groceriesstore.domain.models.ProductModel +@OptIn(ExperimentalGlideComposeApi::class) @Composable fun ProductItem( modifier: Modifier = Modifier, product: ProductModel ) { - Card(modifier = modifier) { - Column(modifier = modifier) { - Image( - painterResource(R.drawable.colorful_carrot), - contentDescription = "", + Card( + modifier = modifier + .width(180.dp) + .padding(12.dp) + ) { + Column( + modifier = modifier + .fillMaxWidth() + ) { + GlideImage( contentScale = ContentScale.Crop, - modifier = modifier.width(120.dp) + model = product.image, + contentDescription = null, + modifier = modifier.fillMaxWidth().height(80.dp) ) - Text(text = product.name ?: "") - Text(text = product.description ?: "") - Row(modifier = modifier) { + Text(modifier = modifier.fillMaxWidth(), text = product.name ?: "") + Text(modifier = modifier.fillMaxWidth(), text = product.description ?: "") + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { Text(text = "${product.price}$") Button( onClick = { /*TODO*/ }, - modifier = modifier.size(48.dp) + modifier = Modifier.size(48.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.colorPrimary) + ) ) { - + Icon( + imageVector = Icons.Filled.Add, + contentDescription = null, + tint = Color.White, + ) } - } } - } +} +@Preview( + uiMode = Configuration.UI_MODE_NIGHT_NO or Configuration.UI_MODE_TYPE_NORMAL, + showSystemUi = true, showBackground = false +) +@Composable +private fun ProductItemPreview(modifier: Modifier = Modifier) { + ProductItem( + modifier = modifier, + product = ProductModel( + id = "fsdfsdds", + name = "Groeceries", + price = 2.4, + image = "", + ) + ) } \ No newline at end of file From c03b0f9c852e1e7984ad0be7e265e9855c548a6a Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 13 Aug 2023 21:28:36 +0700 Subject: [PATCH 12/65] Implement carousel --- .../presentation/shop/composables/Carousel.kt | 144 ++++++++++++++---- 1 file changed, 117 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt index 8e0e3e69..0e16ee16 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt @@ -2,10 +2,12 @@ package com.hieuwu.groceriesstore.presentation.shop.composables import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior +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.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -15,14 +17,18 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBackIosNew +import androidx.compose.material.icons.filled.ArrowForwardIos import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable +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.layout.ContentScale import androidx.compose.ui.res.colorResource import androidx.compose.ui.tooling.preview.Preview @@ -30,6 +36,7 @@ import androidx.compose.ui.unit.dp import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi import com.bumptech.glide.integration.compose.GlideImage import com.hieuwu.groceriesstore.R +import kotlinx.coroutines.launch @OptIn(ExperimentalGlideComposeApi::class, ExperimentalFoundationApi::class) @Composable @@ -39,29 +46,107 @@ fun Carousel(modifier: Modifier = Modifier) { .fillMaxWidth() ) { val sectionItemListState = rememberLazyListState() - val currentVisibleItemIndex = - remember { derivedStateOf { sectionItemListState.firstVisibleItemIndex } } - val currentVisibleItemScrollOffset = remember { - derivedStateOf { sectionItemListState.firstVisibleItemScrollOffset } - } - LaunchedEffect(currentVisibleItemIndex, currentVisibleItemScrollOffset) { - println("currentVisibleItemIndex") - } - LazyRow( + val currentVisibleIndex = remember { mutableStateOf(0) } + val coroutineScope = rememberCoroutineScope() + Box( modifier = Modifier .fillMaxWidth() - .height(180.dp), - state = sectionItemListState + .height(180.dp) ) { - items(bannerImages) { image -> - GlideImage( - contentScale = ContentScale.Crop, - model = image, - contentDescription = null, - modifier = modifier - .fillParentMaxWidth() - .animateItemPlacement() - ) + + LazyRow( + modifier = Modifier + .padding(12.dp) + .fillMaxWidth() + .height(180.dp), + state = sectionItemListState, + ) { + items(bannerImages) { image -> +// val isLeftScrolling = sectionItemListState.isScrollingUp() +// LaunchedEffect(key1 = isLeftScrolling) { +// if (currentVisibleIndex.value != 0) { +// +// if (isLeftScrolling) { +// currentVisibleIndex.value += 1 +// } else { +// currentVisibleIndex.value -= 1 +// } +// sectionItemListState.animateScrollToItem(currentVisibleIndex.value) +// } +// } + GlideImage( + contentScale = ContentScale.Crop, + model = image, + contentDescription = null, + modifier = modifier + .fillParentMaxWidth() + .animateItemPlacement() + ) + } + } + + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .fillMaxSize() + ) { + Box( + modifier = Modifier + .clip( + CircleShape + ) + .size(42.dp) + .background(colorResource(id = R.color.primary_button)) + .clickable { + coroutineScope.launch { + if (currentVisibleIndex.value != 0) { + currentVisibleIndex.value -= 1 + sectionItemListState.animateScrollToItem(currentVisibleIndex.value) + } else { + currentVisibleIndex.value = bannerImages.size - 1 + sectionItemListState.animateScrollToItem(currentVisibleIndex.value) + } + } + }, + contentAlignment = Alignment.Center, + ) { + Icon( + modifier = modifier.align(Alignment.Center), + imageVector = Icons.Filled.ArrowBackIosNew, + contentDescription = null, + tint = Color.White, + ) + } + + Box( + modifier = Modifier + .clip( + CircleShape + ) + .size(42.dp) + .background(colorResource(id = R.color.primary_button)), + contentAlignment = Alignment.Center + ) { + Icon( + modifier = modifier + .align(Alignment.Center) + .clickable { + coroutineScope.launch { + if (currentVisibleIndex.value == bannerImages.size - 1) { + currentVisibleIndex.value = 0 + sectionItemListState.animateScrollToItem(currentVisibleIndex.value) + } else { + currentVisibleIndex.value += 1 + sectionItemListState.animateScrollToItem(currentVisibleIndex.value) + } + } + }, + imageVector = Icons.Filled.ArrowForwardIos, + contentDescription = null, + tint = Color.White, + ) + } } } LazyRow( @@ -69,7 +154,7 @@ fun Carousel(modifier: Modifier = Modifier) { horizontalArrangement = Arrangement.Center, ) { itemsIndexed(bannerImages) { curentIndex, image -> - IndicatorDot() + IndicatorDot(isSelected = currentVisibleIndex.value == curentIndex) } } } @@ -88,12 +173,17 @@ fun CarouselPreview(modifier: Modifier = Modifier) { } @Composable -fun IndicatorDot(modifier: Modifier = Modifier) { +fun IndicatorDot( + modifier: Modifier = Modifier, + isSelected: Boolean = false +) { Box( modifier = modifier .padding(4.dp) .size(12.dp) .clip(CircleShape) - .background(colorResource(id = R.color.primary_button)) + .background( + colorResource(id = if (isSelected) R.color.primary_button else R.color.light_gray) + ) ) } \ No newline at end of file From 19efb1e461bbccfd766f1691ce888397693300b0 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 13 Aug 2023 22:01:15 +0700 Subject: [PATCH 13/65] Tweak ui --- .../shop/composables/ProductItem.kt | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt index 8dab699b..9013587e 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -14,6 +15,7 @@ import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Card import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add @@ -34,32 +36,51 @@ import com.hieuwu.groceriesstore.domain.models.ProductModel @Composable fun ProductItem( modifier: Modifier = Modifier, - product: ProductModel + product: ProductModel, + onAddToCartClick: (ProductModel) -> Unit ) { Card( modifier = modifier .width(180.dp) - .padding(12.dp) + .height(260.dp) + .padding(12.dp), + elevation = 2.dp ) { Column( modifier = modifier .fillMaxWidth() + .padding(12.dp), ) { GlideImage( contentScale = ContentScale.Crop, model = product.image, contentDescription = null, - modifier = modifier.fillMaxWidth().height(80.dp) + modifier = modifier + .fillMaxWidth() + .height(80.dp) ) - Text(modifier = modifier.fillMaxWidth(), text = product.name ?: "") - Text(modifier = modifier.fillMaxWidth(), text = product.description ?: "") + Text( + modifier = modifier.fillMaxWidth(), text = product.name ?: "", + maxLines = 1, + style = MaterialTheme.typography.subtitle1 + ) + Spacer(modifier = modifier.height(8.dp)) + Text( + modifier = modifier.fillMaxWidth(), + text = product.description ?: "", + maxLines = 3, + ) + Spacer(modifier = modifier.height(8.dp)) Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { - Text(text = "${product.price}$") + Text( + text = "${product.price}$", + style = MaterialTheme.typography.h6 + ) Button( - onClick = { /*TODO*/ }, + onClick = { onAddToCartClick(product) }, modifier = Modifier.size(48.dp), colors = ButtonDefaults.buttonColors( backgroundColor = colorResource(id = R.color.colorPrimary) @@ -89,6 +110,7 @@ private fun ProductItemPreview(modifier: Modifier = Modifier) { name = "Groeceries", price = 2.4, image = "", - ) + ), + onAddToCartClick = {} ) } \ No newline at end of file From 7ce6c52b00ddb71dbc0b40903a6f00f8970b5c57 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 13 Aug 2023 22:01:23 +0700 Subject: [PATCH 14/65] Handle on item click --- .../presentation/shop/composables/ProductCatalogue.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt index c3a46bf3..6a5b67bb 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt @@ -21,7 +21,8 @@ import com.hieuwu.groceriesstore.domain.models.ProductModel fun ProductCatalogue( modifier: Modifier, products: List, - title: String + title: String, + onAddToCartClick: (ProductModel) -> Unit ) { Column(modifier = modifier.fillMaxWidth()) { Row( @@ -43,7 +44,10 @@ fun ProductCatalogue( contentPadding = PaddingValues(8.dp) ) { items(products) { item -> - ProductItem(product = item) + ProductItem( + product = item, + onAddToCartClick = onAddToCartClick + ) } } } @@ -83,6 +87,7 @@ fun ProductCataloguePreview(modifier: Modifier = Modifier) { price = 2.6, image = "", ) - ) + ), + onAddToCartClick = {} ) } \ No newline at end of file From e333487bf6bb473f37e010b3bca2460c68820fd8 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 13 Aug 2023 22:01:29 +0700 Subject: [PATCH 15/65] Handle item click --- .../groceriesstore/presentation/shop/ShopScreen.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt index 320e3a28..84eb25bb 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -17,8 +17,15 @@ fun ShopScreen( viewModel: ShopViewModel = hiltViewModel() ) { val products = viewModel.productList.collectAsState() - Column(modifier = modifier.padding(20.dp).fillMaxSize()) { + Column( + modifier = modifier + .padding(20.dp) + .fillMaxSize() + ) { Carousel(modifier = modifier) - ProductCatalogue(modifier = modifier, products = products.value, title = "Best seller") + ProductCatalogue(modifier = modifier, + products = products.value, + title = "Best seller", + onAddToCartClick = { product -> viewModel.addToCart(product) }) } } \ No newline at end of file From f138868b8f052729e660710c5ae863434d7d369d Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 13 Aug 2023 23:04:03 +0700 Subject: [PATCH 16/65] Remove unused modifier --- .../com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt index 84eb25bb..ff5280a1 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -23,7 +23,7 @@ fun ShopScreen( .fillMaxSize() ) { Carousel(modifier = modifier) - ProductCatalogue(modifier = modifier, + ProductCatalogue( products = products.value, title = "Best seller", onAddToCartClick = { product -> viewModel.addToCart(product) }) From 98dba39e101ce4c16bbc6489cfabde3c36f1fb8d Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 13 Aug 2023 23:04:12 +0700 Subject: [PATCH 17/65] Tweak ui --- .../shop/composables/ProductCatalogue.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt index 6a5b67bb..c21465a2 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -19,7 +20,7 @@ import com.hieuwu.groceriesstore.domain.models.ProductModel @Composable fun ProductCatalogue( - modifier: Modifier, + modifier: Modifier = Modifier, products: List, title: String, onAddToCartClick: (ProductModel) -> Unit @@ -27,11 +28,14 @@ fun ProductCatalogue( Column(modifier = modifier.fillMaxWidth()) { Row( modifier = modifier - .padding(12.dp) + .padding(horizontal = 12.dp) .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { - Text(text = title) + Text( + text = title, + style = MaterialTheme.typography.subtitle1 + ) Text(text = "Show all", modifier = modifier.clickable { }) @@ -40,7 +44,7 @@ fun ProductCatalogue( LazyRow( modifier = modifier .fillMaxWidth() - .padding(vertical = 8.dp), + .padding(horizontal = 8.dp), contentPadding = PaddingValues(8.dp) ) { items(products) { item -> From 7757acc979cf008fb512e118460391755d33428e Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 13 Aug 2023 23:04:17 +0700 Subject: [PATCH 18/65] Tweak ui --- .../presentation/shop/composables/ProductItem.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt index 9013587e..9277e32f 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt @@ -42,8 +42,7 @@ fun ProductItem( Card( modifier = modifier .width(180.dp) - .height(260.dp) - .padding(12.dp), + .height(260.dp), elevation = 2.dp ) { Column( From d8bf95848fe4e0e2fab497f608600cb132bdfdea Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Mon, 14 Aug 2023 22:52:33 +0700 Subject: [PATCH 19/65] Tweak ui --- .../presentation/shop/ShopScreen.kt | 46 +++++++++++++++---- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt index ff5280a1..392e5118 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -1,13 +1,26 @@ package com.hieuwu.groceriesstore.presentation.shop +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +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.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import com.hieuwu.groceriesstore.R import com.hieuwu.groceriesstore.presentation.shop.composables.Carousel import com.hieuwu.groceriesstore.presentation.shop.composables.ProductCatalogue @@ -17,15 +30,28 @@ fun ShopScreen( viewModel: ShopViewModel = hiltViewModel() ) { val products = viewModel.productList.collectAsState() - Column( - modifier = modifier - .padding(20.dp) - .fillMaxSize() - ) { - Carousel(modifier = modifier) - ProductCatalogue( - products = products.value, - title = "Best seller", - onAddToCartClick = { product -> viewModel.addToCart(product) }) + Box(modifier = modifier.background(colorResource(id = R.color.colorPrimary))) { + Column( + modifier = modifier + .fillMaxSize() + .clip(RoundedCornerShape(topStartPercent = 8, topEndPercent = 8)) + .background(Color.White) + ) { + Image( + modifier = modifier + .align(Alignment.CenterHorizontally) + .size(64.dp), + painter = painterResource(id = R.drawable.colorful_carrot), + contentDescription = null, + ) + Text(text = stringResource(id = R.string.welcome_prompt)) + Carousel(modifier = modifier) + ProductCatalogue( + products = products.value, + title = "Best seller", + onAddToCartClick = { product -> viewModel.addToCart(product) }) + } + } + } \ No newline at end of file From 9e015f8f4247b5a18c3e1cd1cd0a4ce6c576d39e Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Tue, 15 Aug 2023 23:16:06 +0700 Subject: [PATCH 20/65] Update Ui spacing --- .../presentation/shop/ShopScreen.kt | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt index 392e5118..6a107c2e 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -4,10 +4,16 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -18,6 +24,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.hieuwu.groceriesstore.R @@ -31,27 +38,55 @@ fun ShopScreen( ) { val products = viewModel.productList.collectAsState() Box(modifier = modifier.background(colorResource(id = R.color.colorPrimary))) { + + Text( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + text = "Groceries Store", + color = Color.White, + style = MaterialTheme.typography.h5, + textAlign = TextAlign.Center + ) Column( modifier = modifier + .verticalScroll(rememberScrollState()) .fillMaxSize() + .padding(top = 64.dp) .clip(RoundedCornerShape(topStartPercent = 8, topEndPercent = 8)) .background(Color.White) ) { Image( modifier = modifier + .padding(top = 8.dp) .align(Alignment.CenterHorizontally) - .size(64.dp), + .size(48.dp), painter = painterResource(id = R.drawable.colorful_carrot), contentDescription = null, ) - Text(text = stringResource(id = R.string.welcome_prompt)) - Carousel(modifier = modifier) + Text( + modifier = modifier.align(Alignment.CenterHorizontally), + text = stringResource(id = R.string.welcome_prompt) + ) + Carousel(modifier = modifier.padding(4.dp)) ProductCatalogue( products = products.value, title = "Best seller", - onAddToCartClick = { product -> viewModel.addToCart(product) }) + onAddToCartClick = { product -> viewModel.addToCart(product) } + ) + Spacer(modifier = modifier.height(12.dp)) + ProductCatalogue( + products = products.value, + title = "Hot deal", + onAddToCartClick = { product -> viewModel.addToCart(product) } + ) + Spacer(modifier = modifier.height(12.dp)) + ProductCatalogue( + products = products.value, + title = "Exclusive offer", + onAddToCartClick = { product -> viewModel.addToCart(product) } + ) } - } } \ No newline at end of file From ce38deb33791d92fdc362fb1e5aea1c25c7abdb0 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Tue, 15 Aug 2023 23:16:22 +0700 Subject: [PATCH 21/65] Remove unused code --- .../presentation/shop/composables/Carousel.kt | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt index 0e16ee16..2d1d47a9 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -62,18 +63,6 @@ fun Carousel(modifier: Modifier = Modifier) { state = sectionItemListState, ) { items(bannerImages) { image -> -// val isLeftScrolling = sectionItemListState.isScrollingUp() -// LaunchedEffect(key1 = isLeftScrolling) { -// if (currentVisibleIndex.value != 0) { -// -// if (isLeftScrolling) { -// currentVisibleIndex.value += 1 -// } else { -// currentVisibleIndex.value -= 1 -// } -// sectionItemListState.animateScrollToItem(currentVisibleIndex.value) -// } -// } GlideImage( contentScale = ContentScale.Crop, model = image, From 3839b54ea32329ef0be64eb87aef595dd5ac108d Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Tue, 15 Aug 2023 23:19:54 +0700 Subject: [PATCH 22/65] Update spacing --- .../presentation/shop/composables/ProductCatalogue.kt | 1 + .../presentation/shop/composables/ProductItem.kt | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt index c21465a2..56cc122b 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt @@ -49,6 +49,7 @@ fun ProductCatalogue( ) { items(products) { item -> ProductItem( + modifier = modifier.padding(4.dp), product = item, onAddToCartClick = onAddToCartClick ) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt index 9277e32f..882779c1 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt @@ -48,7 +48,7 @@ fun ProductItem( Column( modifier = modifier .fillMaxWidth() - .padding(12.dp), + .padding(4.dp), ) { GlideImage( contentScale = ContentScale.Crop, @@ -63,13 +63,13 @@ fun ProductItem( maxLines = 1, style = MaterialTheme.typography.subtitle1 ) - Spacer(modifier = modifier.height(8.dp)) + Spacer(modifier = modifier.height(4.dp)) Text( modifier = modifier.fillMaxWidth(), text = product.description ?: "", - maxLines = 3, + maxLines = 2, ) - Spacer(modifier = modifier.height(8.dp)) + Spacer(modifier = modifier.height(4.dp)) Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween From ddc9d07500c963ddce36cc6ec826031aacab06f6 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Wed, 16 Aug 2023 23:15:29 +0700 Subject: [PATCH 23/65] Remove unused import --- .../groceriesstore/presentation/shop/composables/Carousel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt index 2d1d47a9..2e7c6e21 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/Carousel.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth From 22e46a94ac09b2645000f36b14a5bcd336295b79 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Wed, 16 Aug 2023 23:15:33 +0700 Subject: [PATCH 24/65] Remove unused import --- .../groceriesstore/presentation/shop/composables/ProductItem.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt index 882779c1..3e653ba3 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt @@ -1,7 +1,6 @@ package com.hieuwu.groceriesstore.presentation.shop.composables import android.content.res.Configuration -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -24,7 +23,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi From 01b5b6ece002abba0a8fa5bcd09e123c6b6dce96 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Fri, 18 Aug 2023 22:34:43 +0700 Subject: [PATCH 25/65] Navigate to details --- .../presentation/shop/composables/ProductCatalogue.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt index 56cc122b..a8277dd3 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductCatalogue.kt @@ -23,7 +23,8 @@ fun ProductCatalogue( modifier: Modifier = Modifier, products: List, title: String, - onAddToCartClick: (ProductModel) -> Unit + onAddToCartClick: (ProductModel) -> Unit, + onNavigateToProductDetails: (ProductModel) -> Unit ) { Column(modifier = modifier.fillMaxWidth()) { Row( @@ -51,7 +52,8 @@ fun ProductCatalogue( ProductItem( modifier = modifier.padding(4.dp), product = item, - onAddToCartClick = onAddToCartClick + onAddToCartClick = onAddToCartClick, + onNavigateToProductDetails = { onNavigateToProductDetails(item) } ) } } @@ -93,6 +95,7 @@ fun ProductCataloguePreview(modifier: Modifier = Modifier) { image = "", ) ), - onAddToCartClick = {} + onAddToCartClick = {}, + onNavigateToProductDetails = {} ) } \ No newline at end of file From e3d9010c0ae7682a991b8c0d65a7812245049de0 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Fri, 18 Aug 2023 22:34:46 +0700 Subject: [PATCH 26/65] Navigate to details --- .../presentation/shop/composables/ProductItem.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt index 3e653ba3..981baa77 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/composables/ProductItem.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -30,18 +31,20 @@ import com.bumptech.glide.integration.compose.GlideImage import com.hieuwu.groceriesstore.R import com.hieuwu.groceriesstore.domain.models.ProductModel -@OptIn(ExperimentalGlideComposeApi::class) +@OptIn(ExperimentalGlideComposeApi::class, ExperimentalMaterialApi::class) @Composable fun ProductItem( modifier: Modifier = Modifier, product: ProductModel, - onAddToCartClick: (ProductModel) -> Unit + onAddToCartClick: (ProductModel) -> Unit, + onNavigateToProductDetails: (String) -> Unit ) { Card( modifier = modifier .width(180.dp) .height(260.dp), - elevation = 2.dp + elevation = 2.dp, + onClick = { onNavigateToProductDetails(product.id) } ) { Column( modifier = modifier @@ -108,6 +111,7 @@ private fun ProductItemPreview(modifier: Modifier = Modifier) { price = 2.4, image = "", ), - onAddToCartClick = {} + onAddToCartClick = {}, + onNavigateToProductDetails = {} ) } \ No newline at end of file From de883d92cacc056df66dba0a43cb2930957dbdc3 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Fri, 18 Aug 2023 22:34:51 +0700 Subject: [PATCH 27/65] Navigate to details --- .../presentation/shop/ShopScreen.kt | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt index 6a107c2e..f4de646f 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -34,7 +35,8 @@ import com.hieuwu.groceriesstore.presentation.shop.composables.ProductCatalogue @Composable fun ShopScreen( modifier: Modifier = Modifier, - viewModel: ShopViewModel = hiltViewModel() + viewModel: ShopViewModel = hiltViewModel(), + navigateToProductDetails: (String) -> Unit ) { val products = viewModel.productList.collectAsState() Box(modifier = modifier.background(colorResource(id = R.color.colorPrimary))) { @@ -56,6 +58,14 @@ fun ShopScreen( .clip(RoundedCornerShape(topStartPercent = 8, topEndPercent = 8)) .background(Color.White) ) { + + val selectedProduct = viewModel.navigateToSelectedProperty.collectAsState().value + LaunchedEffect(key1 = selectedProduct) { + selectedProduct?.let { + navigateToProductDetails(selectedProduct.id) + viewModel.displayProductDetailsComplete() + } + } Image( modifier = modifier .padding(top = 8.dp) @@ -72,19 +82,22 @@ fun ShopScreen( ProductCatalogue( products = products.value, title = "Best seller", - onAddToCartClick = { product -> viewModel.addToCart(product) } + onAddToCartClick = { product -> viewModel.addToCart(product) }, + onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } ) Spacer(modifier = modifier.height(12.dp)) ProductCatalogue( products = products.value, title = "Hot deal", - onAddToCartClick = { product -> viewModel.addToCart(product) } + onAddToCartClick = { product -> viewModel.addToCart(product) }, + onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } ) Spacer(modifier = modifier.height(12.dp)) ProductCatalogue( products = products.value, title = "Exclusive offer", - onAddToCartClick = { product -> viewModel.addToCart(product) } + onAddToCartClick = { product -> viewModel.addToCart(product) }, + onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } ) } } From 17317284f9b50b9ae245a3b9bce93ef5a7974ac8 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Fri, 18 Aug 2023 22:35:00 +0700 Subject: [PATCH 28/65] Remove unused code --- .../presentation/shop/ShopFragment.kt | 134 +----------------- 1 file changed, 3 insertions(+), 131 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt index b9522111..05b68303 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopFragment.kt @@ -1,45 +1,16 @@ package com.hieuwu.groceriesstore.presentation.shop -import android.graphics.drawable.Drawable import android.os.Bundle -import android.transition.TransitionInflater import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageView -import android.widget.LinearLayout import androidx.compose.ui.platform.ComposeView -import androidx.core.content.ContextCompat -import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.viewpager.widget.ViewPager -import com.hieuwu.groceriesstore.R -import com.hieuwu.groceriesstore.databinding.FragmentShopBinding -import com.hieuwu.groceriesstore.domain.models.ProductModel -import com.hieuwu.groceriesstore.presentation.adapters.GridListItemAdapter -import com.hieuwu.groceriesstore.presentation.adapters.ViewPagerAdapter -import com.hieuwu.groceriesstore.presentation.orderhistory.OrderHistoryScreen -import com.hieuwu.groceriesstore.utilities.showMessageSnackBar import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch @AndroidEntryPoint class ShopFragment : Fragment() { - - private val viewModel: ShopViewModel by viewModels() - private lateinit var dots: Array - private lateinit var binding: FragmentShopBinding - private lateinit var gridListItemAdapter: GridListItemAdapter - private lateinit var nonActiveDot: Drawable - private lateinit var activeDot: Drawable - private var dotCount: Int = 0 - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -47,81 +18,10 @@ class ShopFragment : Fragment() { ): View? { return ComposeView(requireContext()).apply { setContent { - ShopScreen() - } - } -// binding = DataBindingUtil.inflate( -// inflater, R.layout.fragment_shop, container, false -// ) -// -// binding.viewModel = viewModel -// binding.lifecycleOwner = this -// nonActiveDot = -// ContextCompat.getDrawable(requireContext(), R.drawable.non_active_dot_shape)!! -// activeDot = ContextCompat.getDrawable(requireContext(), R.drawable.active_dot_shape)!! -// setObserver() -// setUpRecyclerView() -// drawSliderDotSymbols() -// setEventListener() -// -// val inflater = TransitionInflater.from(requireContext()) -// exitTransition = inflater.inflateTransition(R.transition.fade) -// -// -// return binding.root - } - - private fun drawSliderDotSymbols() { - val viewPagerAdapter = ViewPagerAdapter(requireContext()) - binding.viewPager.adapter = viewPagerAdapter - dotCount = viewPagerAdapter.count - val sliderDotspanel = binding.sliderDots - dots = Array(dotCount) { ImageView(requireContext()) } - val params = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - params.setMargins(10, 0, 10, 0) - repeat(dotCount) { - dots[it].setImageDrawable(nonActiveDot) - sliderDotspanel.addView(dots[it], params) - } - dots[0].setImageDrawable(activeDot) - } - - private fun setEventListener() { - binding.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { - override fun onPageScrolled( - position: Int, - positionOffset: Float, - positionOffsetPixels: Int - ) {} - - override fun onPageSelected(position: Int) { - repeat(dotCount) { - dots[it].setImageDrawable(nonActiveDot) - } - dots[position].setImageDrawable(activeDot) - } - - override fun onPageScrollStateChanged(state: Int) {} - }) - } - - private fun setObserver() { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - viewModel.navigateToSelectedProperty.collect { - it?.let { - navigateToProductDetail(it.id) - viewModel.displayProductDetailsComplete() - } + ShopScreen( + navigateToProductDetails = { id -> navigateToProductDetail(id) } - } - launch { - viewModel.currentCart.collect{} - } + ) } } } @@ -133,32 +33,4 @@ class ShopFragment : Fragment() { findNavController().navigate(direction) } - private fun addToCart(product: ProductModel) { - viewModel.addToCart(product) - showMessageSnackBar("Added ${product.name}") - } - - private fun setUpRecyclerView() { - gridListItemAdapter = GridListItemAdapter( - GridListItemAdapter.OnClickListener( - clickListener = { viewModel.displayProductDetails(it) }, - addToCartListener = { addToCart(it) } - ) - ) - binding.exclusiveOfferRecyclerview.adapter = - gridListItemAdapter - - binding.exclusiveOfferRecyclerview.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - - binding.bestSellingRecyclerview.adapter = - gridListItemAdapter - - binding.bestSellingRecyclerview.layoutManager = - LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - - binding.recommendedRecyclerview.adapter = - gridListItemAdapter - - binding.recommendedRecyclerview.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - } } From 35411ee3602f43890460c7e9452d59c1a5b8a726 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Fri, 18 Aug 2023 22:43:09 +0700 Subject: [PATCH 29/65] Add dependency --- app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle b/app/build.gradle index 1aca5070..1530dd51 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -114,6 +114,7 @@ dependencies { implementation "com.google.accompanist:accompanist-themeadapter-material:0.28.0" implementation("androidx.hilt:hilt-navigation-compose:1.0.0") implementation "com.github.bumptech.glide:compose:1.0.0-alpha.1" + implementation "androidx.compose.material3:material3:1.1.1" debugImplementation "androidx.compose.ui:ui-tooling:1.4.2" implementation "androidx.compose.ui:ui-tooling-preview:1.4.2" From 36d73026e52354b7c25af2039cd82eb0f14c6f81 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Fri, 18 Aug 2023 22:43:34 +0700 Subject: [PATCH 30/65] Show snack bar --- .../presentation/shop/ShopScreen.kt | 149 +++++++++++------- 1 file changed, 92 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt index f4de646f..9bf3dfaa 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/shop/ShopScreen.kt @@ -14,10 +14,15 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.MaterialTheme +import androidx.compose.material3.Scaffold import androidx.compose.material.Text +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +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 @@ -31,6 +36,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.hieuwu.groceriesstore.R import com.hieuwu.groceriesstore.presentation.shop.composables.Carousel import com.hieuwu.groceriesstore.presentation.shop.composables.ProductCatalogue +import kotlinx.coroutines.launch @Composable fun ShopScreen( @@ -38,67 +44,96 @@ fun ShopScreen( viewModel: ShopViewModel = hiltViewModel(), navigateToProductDetails: (String) -> Unit ) { - val products = viewModel.productList.collectAsState() - Box(modifier = modifier.background(colorResource(id = R.color.colorPrimary))) { - Text( - modifier = modifier - .fillMaxWidth() - .padding(vertical = 12.dp), - text = "Groceries Store", - color = Color.White, - style = MaterialTheme.typography.h5, - textAlign = TextAlign.Center - ) - Column( - modifier = modifier - .verticalScroll(rememberScrollState()) - .fillMaxSize() - .padding(top = 64.dp) - .clip(RoundedCornerShape(topStartPercent = 8, topEndPercent = 8)) - .background(Color.White) - ) { + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + Scaffold( + snackbarHost = { SnackbarHost(snackbarHostState) }, + + ) { paddingValues -> + Box(modifier = modifier.background(colorResource(id = R.color.colorPrimary))) { + val products = viewModel.productList.collectAsState() - val selectedProduct = viewModel.navigateToSelectedProperty.collectAsState().value - LaunchedEffect(key1 = selectedProduct) { - selectedProduct?.let { - navigateToProductDetails(selectedProduct.id) - viewModel.displayProductDetailsComplete() - } - } - Image( - modifier = modifier - .padding(top = 8.dp) - .align(Alignment.CenterHorizontally) - .size(48.dp), - painter = painterResource(id = R.drawable.colorful_carrot), - contentDescription = null, - ) Text( - modifier = modifier.align(Alignment.CenterHorizontally), - text = stringResource(id = R.string.welcome_prompt) - ) - Carousel(modifier = modifier.padding(4.dp)) - ProductCatalogue( - products = products.value, - title = "Best seller", - onAddToCartClick = { product -> viewModel.addToCart(product) }, - onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } - ) - Spacer(modifier = modifier.height(12.dp)) - ProductCatalogue( - products = products.value, - title = "Hot deal", - onAddToCartClick = { product -> viewModel.addToCart(product) }, - onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } - ) - Spacer(modifier = modifier.height(12.dp)) - ProductCatalogue( - products = products.value, - title = "Exclusive offer", - onAddToCartClick = { product -> viewModel.addToCart(product) }, - onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } + modifier = modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + text = "Groceries Store", + color = Color.White, + style = MaterialTheme.typography.h5, + textAlign = TextAlign.Center ) + Column( + modifier = modifier + .verticalScroll(rememberScrollState()) + .fillMaxSize() + .padding(top = 64.dp) + .clip(RoundedCornerShape(topStartPercent = 8, topEndPercent = 8)) + .background(Color.White) + ) { + + val selectedProduct = viewModel.navigateToSelectedProperty.collectAsState().value + LaunchedEffect(key1 = selectedProduct) { + selectedProduct?.let { + navigateToProductDetails(selectedProduct.id) + viewModel.displayProductDetailsComplete() + } + } + Image( + modifier = modifier + .padding(top = 8.dp) + .align(Alignment.CenterHorizontally) + .size(48.dp), + painter = painterResource(id = R.drawable.colorful_carrot), + contentDescription = null, + ) + Text( + modifier = modifier.align(Alignment.CenterHorizontally), + text = stringResource(id = R.string.welcome_prompt) + ) + Carousel(modifier = modifier.padding(4.dp)) + ProductCatalogue( + products = products.value, + title = "Best seller", + onAddToCartClick = { product -> + viewModel.addToCart(product) + scope.launch { + snackbarHostState.showSnackbar( + "Added ${product.name}" + ) + } + }, + onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } + ) + Spacer(modifier = modifier.height(12.dp)) + ProductCatalogue( + products = products.value, + title = "Hot deal", + onAddToCartClick = { product -> + viewModel.addToCart(product) + scope.launch { + snackbarHostState.showSnackbar( + "Added ${product.name}" + ) + } + }, + onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } + ) + Spacer(modifier = modifier.height(12.dp)) + ProductCatalogue( + products = products.value, + title = "Exclusive offer", + onAddToCartClick = { product -> + viewModel.addToCart(product) + scope.launch { + snackbarHostState.showSnackbar( + "Added ${product.name}" + ) + } + }, + onNavigateToProductDetails = { id -> viewModel.displayProductDetails(id) } + ) + } } } From 9b0845c7a494b16f3bdf9d94ce8562c08ee3b495 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Fri, 18 Aug 2023 23:01:08 +0700 Subject: [PATCH 31/65] Remove unused layout --- app/src/main/res/layout/fragment_shop.xml | 208 ---------------------- 1 file changed, 208 deletions(-) delete mode 100644 app/src/main/res/layout/fragment_shop.xml diff --git a/app/src/main/res/layout/fragment_shop.xml b/app/src/main/res/layout/fragment_shop.xml deleted file mode 100644 index e622b7de..00000000 --- a/app/src/main/res/layout/fragment_shop.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 9479343c94eb498c95e3f7eb7677d1953f2a7f90 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 19 Aug 2023 12:16:50 +0700 Subject: [PATCH 32/65] Create composable --- .../presentation/productdetail/ProductDetailScreen.kt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt new file mode 100644 index 00000000..430dace1 --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt @@ -0,0 +1,2 @@ +package com.hieuwu.groceriesstore.presentation.productdetail + From 6a37f0fcf88752ed388563da895cf189c52b4a86 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sat, 19 Aug 2023 12:17:00 +0700 Subject: [PATCH 33/65] Implement screen --- .../productdetail/ProductDetailScreen.kt | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt index 430dace1..05ad5a6e 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt @@ -1,2 +1,141 @@ package com.hieuwu.groceriesstore.presentation.productdetail +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.Button +import androidx.compose.material.ButtonColors +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Minimize +import androidx.compose.material.icons.filled.NavigateBefore +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.hieuwu.groceriesstore.R +import com.hieuwu.groceriesstore.domain.models.ProductModel + +@Composable +fun ProductDetailScreen( + modifier: Modifier = Modifier, + product: ProductModel +) { + + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + Scaffold( + snackbarHost = { SnackbarHost(snackbarHostState) }, + topBar = { + TopAppBar( + title = { + Text("Product details") + }, + backgroundColor = colorResource(id = R.color.colorPrimary), + contentColor = Color.White, + navigationIcon = { + IconButton(onClick = { + + }) { + Icon( + imageVector = Icons.Filled.NavigateBefore, + contentDescription = null + ) + } + } + ) + } + ) { paddingValues -> + Column(modifier = modifier + .padding(paddingValues) + .padding(horizontal = 20.dp)) { + Image( + modifier = modifier + .fillMaxWidth() + .height(320.dp), + painter = painterResource(id = R.drawable.banner), + contentDescription = null + ) + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(text = product.name ?: "") + Text(text = "$ ${product.price}") + } + Spacer(modifier = modifier.height(8.dp)) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + IconButton(onClick = { /*TODO*/ }) { + Icon(imageVector = Icons.Filled.Remove, contentDescription = null) + } + Spacer(modifier = modifier.width(8.dp)) + Text(text = "4") + Spacer(modifier = modifier.width(8.dp)) + IconButton(onClick = { }) { + Icon( + imageVector = Icons.Filled.Add, contentDescription = null, + tint = colorResource(id = R.color.primary_button) + ) + } + } + Spacer(modifier = modifier.height(8.dp)) + Text(text = "Description") + Text(text = product.description ?: "") + Spacer(modifier = modifier.height(12.dp)) + Text(text = "Nutrition") + Text(text = product.nutrition ?: "") + Spacer(modifier = modifier.height(12.dp)) + Button( + modifier = modifier + .fillMaxWidth(), + onClick = { /*TODO*/ }, + colors = ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.primary_button), + contentColor = Color.White + ) + ) { + Text(text = "Add to Basket") + } + } + } + +} + +@Preview +@Composable +fun ProductDetailScreenPreview() { + ProductDetailScreen( + product = ProductModel( + id = "", + name = "Wagu Beef", + price = 12.24, + description = "A classic vegetable for any meal", + nutrition = "Many Canadians are nutritionally deficient" + ) + ) +} \ No newline at end of file From 2b515f702022640baff57c2f683406c9f4ed8c42 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 20 Aug 2023 22:19:33 +0700 Subject: [PATCH 34/65] Handle event --- .../productdetail/ProductDetailScreen.kt | 73 ++++++++++++------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt index 05ad5a6e..1b8ee4aa 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt @@ -1,7 +1,6 @@ package com.hieuwu.groceriesstore.presentation.productdetail import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -11,7 +10,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.Button -import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonDefaults import androidx.compose.material.Icon import androidx.compose.material.IconButton @@ -19,13 +17,14 @@ import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Minimize import androidx.compose.material.icons.filled.NavigateBefore import androidx.compose.material.icons.filled.Remove +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment @@ -35,15 +34,16 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import com.hieuwu.groceriesstore.R -import com.hieuwu.groceriesstore.domain.models.ProductModel +import kotlinx.coroutines.launch @Composable fun ProductDetailScreen( modifier: Modifier = Modifier, - product: ProductModel + viewModel: ProductDetailViewModel = hiltViewModel() ) { - + val product = viewModel.product.collectAsState().value val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() Scaffold( @@ -68,13 +68,15 @@ fun ProductDetailScreen( ) } ) { paddingValues -> - Column(modifier = modifier - .padding(paddingValues) - .padding(horizontal = 20.dp)) { + Column( + modifier = modifier + .padding(paddingValues) + .padding(horizontal = 20.dp) + ) { Image( modifier = modifier .fillMaxWidth() - .height(320.dp), + .height(230.dp), painter = painterResource(id = R.drawable.banner), contentDescription = null ) @@ -82,21 +84,32 @@ fun ProductDetailScreen( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { - Text(text = product.name ?: "") - Text(text = "$ ${product.price}") + Text( + text = product?.name ?: "", + style = MaterialTheme.typography.titleMedium + ) + Text( + text = "$ ${product?.price}", + style = MaterialTheme.typography.titleMedium + ) } Spacer(modifier = modifier.height(8.dp)) Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { - IconButton(onClick = { /*TODO*/ }) { + val quantity = viewModel.quantity.collectAsState().value + IconButton(onClick = { + viewModel.decreaseQty() + }) { Icon(imageVector = Icons.Filled.Remove, contentDescription = null) } Spacer(modifier = modifier.width(8.dp)) - Text(text = "4") + Text(text = "$quantity") Spacer(modifier = modifier.width(8.dp)) - IconButton(onClick = { }) { + IconButton(onClick = { + viewModel.increaseQty() + }) { Icon( imageVector = Icons.Filled.Add, contentDescription = null, tint = colorResource(id = R.color.primary_button) @@ -104,16 +117,29 @@ fun ProductDetailScreen( } } Spacer(modifier = modifier.height(8.dp)) - Text(text = "Description") - Text(text = product.description ?: "") + Text( + text = "Description", + style = MaterialTheme.typography.titleMedium + ) + Text(text = product?.description ?: "") Spacer(modifier = modifier.height(12.dp)) - Text(text = "Nutrition") - Text(text = product.nutrition ?: "") + Text( + text = "Nutrition", + style = MaterialTheme.typography.titleMedium + ) + Text(text = product?.nutrition ?: "") Spacer(modifier = modifier.height(12.dp)) Button( modifier = modifier .fillMaxWidth(), - onClick = { /*TODO*/ }, + onClick = { + viewModel.addToCart() + scope.launch { + snackbarHostState.showSnackbar( + "Added ${product?.name}" + ) + } + }, colors = ButtonDefaults.buttonColors( backgroundColor = colorResource(id = R.color.primary_button), contentColor = Color.White @@ -130,12 +156,5 @@ fun ProductDetailScreen( @Composable fun ProductDetailScreenPreview() { ProductDetailScreen( - product = ProductModel( - id = "", - name = "Wagu Beef", - price = 12.24, - description = "A classic vegetable for any meal", - nutrition = "Many Canadians are nutritionally deficient" - ) ) } \ No newline at end of file From 4ab2fa2945725abaa680c2a1a850266d69735044 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 20 Aug 2023 22:19:47 +0700 Subject: [PATCH 35/65] Refactor code --- .../productdetail/ProductDetailViewModel.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt index 1ebb8bf2..d9e0dd33 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt @@ -49,20 +49,24 @@ class ProductDetailViewModel @Inject constructor( notifyPropertyChanged(BR.qty) } + private val _quantity = MutableStateFlow(1) + val quantity: StateFlow + get() = _quantity + private val _showSnackbarEvent = MutableStateFlow(false) val showSnackBarEvent: StateFlow get() = _showSnackbarEvent.asStateFlow() fun addToCart() { viewModelScope.launch { - val subtotal = product.value?.price?.times(qty) ?: 0.0 + val subtotal = product.value?.price?.times(_quantity.value) ?: 0.0 if (currentCart.value != null) { // Add to cart val cartId = currentCart.value!!.id val lineItem = LineItem( productId = product.value!!.id, orderId = cartId, - quantity = _qty, + quantity = _quantity.value, subtotal = subtotal ) orderRepository.addLineItem(lineItem) @@ -77,7 +81,7 @@ class ProductDetailViewModel @Inject constructor( val lineItem = LineItem( productId = product.value!!.id, orderId = id, - quantity = _qty, + quantity = _quantity.value, subtotal = subtotal ) orderRepository.addLineItem(lineItem) @@ -87,12 +91,12 @@ class ProductDetailViewModel @Inject constructor( } fun increaseQty() { - qty++ + _quantity.value++ } fun decreaseQty() { - if (qty <= 1) return - qty-- + if (_quantity.value <= 1) return + _quantity.value-- } fun doneShowingSnackbar() { From 9f6184519d4b28e61e583f302dce7a01729028ca Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 20 Aug 2023 22:24:03 +0700 Subject: [PATCH 36/65] Remove unused code --- .../productdetail/ProductDetailFragment.kt | 89 ++----------------- 1 file changed, 7 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailFragment.kt index 51bc35c0..955708e6 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailFragment.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailFragment.kt @@ -1,105 +1,30 @@ package com.hieuwu.groceriesstore.presentation.productdetail import android.os.Bundle -import android.transition.TransitionInflater import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.widget.NestedScrollView -import androidx.databinding.DataBindingUtil +import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController -import com.hieuwu.groceriesstore.R -import com.hieuwu.groceriesstore.databinding.FragmentProductDetailBinding -import com.hieuwu.groceriesstore.utilities.showMessageSnackBar import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import timber.log.Timber @AndroidEntryPoint class ProductDetailFragment : Fragment() { - private lateinit var binding: FragmentProductDetailBinding - private val viewModel: ProductDetailViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - - binding = DataBindingUtil.inflate( - inflater, R.layout.fragment_product_detail, container, false - ) - - binding.viewModel = viewModel - binding.lifecycleOwner = this - - setObserver() - - seEventListener() - - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.slide_right) - exitTransition = inflater.inflateTransition(R.transition.fade) - - return binding.root - } - - private fun setObserver() { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - viewModel.product.collect{} - } - - launch { - viewModel.currentCart.collect{} - } - - launch { - viewModel.showSnackBarEvent.collect { - if (it) { // Observed state is true. - showMessageSnackBar(viewModel.qty.toString() + " x " + - viewModel.product.value?.name + " is added") - // Reset state to make sure the snackbar is only shown once, even if the device - // has a configuration change. - viewModel.doneShowingSnackbar() - } + return ComposeView(requireContext()).apply { + setContent { + ProductDetailScreen( + onNavigateBack = { + findNavController().navigateUp() } - } + ) } } } - - private fun seEventListener() { - var isToolbarShown = false - binding.productDetailScrollview.setOnScrollChangeListener( - NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ -> - Timber.d("$scrollY") - - // User scrolled past image to height of toolbar and the title text is - // underneath the toolbar, so the toolbar should be shown. - val shouldShowToolbar = scrollY > binding.toolbar.height - - // The new state of the toolbar differs from the previous state; update - // appbar and toolbar attributes. - if (isToolbarShown != shouldShowToolbar) { - isToolbarShown = shouldShowToolbar - - // Use shadow animator to add elevation if toolbar is shown - binding.appbar.isActivated = shouldShowToolbar - - // Show the plant name if toolbar is shown - binding.toolbarLayout.isTitleEnabled = shouldShowToolbar - } - }) - - binding.toolbar.setNavigationOnClickListener { - findNavController().navigateUp() - } - } } From 11f010c7643d5d4a877e198efccec9b9bfbaf3ce Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 20 Aug 2023 22:24:08 +0700 Subject: [PATCH 37/65] Handle navigate back --- .../presentation/productdetail/ProductDetailScreen.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt index 1b8ee4aa..cc7c1703 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt @@ -41,7 +41,8 @@ import kotlinx.coroutines.launch @Composable fun ProductDetailScreen( modifier: Modifier = Modifier, - viewModel: ProductDetailViewModel = hiltViewModel() + viewModel: ProductDetailViewModel = hiltViewModel(), + onNavigateBack: () -> Unit ) { val product = viewModel.product.collectAsState().value val snackbarHostState = remember { SnackbarHostState() } @@ -56,9 +57,7 @@ fun ProductDetailScreen( backgroundColor = colorResource(id = R.color.colorPrimary), contentColor = Color.White, navigationIcon = { - IconButton(onClick = { - - }) { + IconButton(onClick = onNavigateBack) { Icon( imageVector = Icons.Filled.NavigateBefore, contentDescription = null @@ -156,5 +155,6 @@ fun ProductDetailScreen( @Composable fun ProductDetailScreenPreview() { ProductDetailScreen( + onNavigateBack = {} ) } \ No newline at end of file From bba4ca1620ba40ddab7cfc78cfe6f001c137fb29 Mon Sep 17 00:00:00 2001 From: Hieu Vu Date: Sun, 20 Aug 2023 22:25:52 +0700 Subject: [PATCH 38/65] Remove unused code --- .../productdetail/ProductDetailViewModel.kt | 26 +- .../res/layout/fragment_product_detail.xml | 228 ------------------ 2 files changed, 3 insertions(+), 251 deletions(-) delete mode 100644 app/src/main/res/layout/fragment_product_detail.xml diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt index d9e0dd33..ce0d6215 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailViewModel.kt @@ -1,25 +1,22 @@ package com.hieuwu.groceriesstore.presentation.productdetail -import androidx.databinding.Bindable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope -import com.hieuwu.groceriesstore.BR import com.hieuwu.groceriesstore.data.database.entities.LineItem import com.hieuwu.groceriesstore.data.database.entities.Order -import com.hieuwu.groceriesstore.domain.models.OrderModel import com.hieuwu.groceriesstore.data.repository.OrderRepository +import com.hieuwu.groceriesstore.domain.models.OrderModel import com.hieuwu.groceriesstore.domain.usecases.GetProductDetailUseCase import com.hieuwu.groceriesstore.presentation.utils.ObservableViewModel import com.hieuwu.groceriesstore.utilities.OrderStatus import dagger.hilt.android.lifecycle.HiltViewModel -import java.util.UUID +import java.util.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.stateIn -import javax.inject.Inject import kotlinx.coroutines.launch +import javax.inject.Inject @HiltViewModel class ProductDetailViewModel @Inject constructor( @@ -40,23 +37,10 @@ class ProductDetailViewModel @Inject constructor( orderRepository.getOneOrderByStatus(OrderStatus.IN_CART) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null) - private var _qty: Int = 1 - var qty: Int - @Bindable - get() = _qty - set(value) { - _qty = value - notifyPropertyChanged(BR.qty) - } - private val _quantity = MutableStateFlow(1) val quantity: StateFlow get() = _quantity - private val _showSnackbarEvent = MutableStateFlow(false) - val showSnackBarEvent: StateFlow - get() = _showSnackbarEvent.asStateFlow() - fun addToCart() { viewModelScope.launch { val subtotal = product.value?.price?.times(_quantity.value) ?: 0.0 @@ -86,7 +70,6 @@ class ProductDetailViewModel @Inject constructor( ) orderRepository.addLineItem(lineItem) } - _showSnackbarEvent.value = true } } @@ -99,7 +82,4 @@ class ProductDetailViewModel @Inject constructor( _quantity.value-- } - fun doneShowingSnackbar() { - _showSnackbarEvent.value = false - } } diff --git a/app/src/main/res/layout/fragment_product_detail.xml b/app/src/main/res/layout/fragment_product_detail.xml deleted file mode 100644 index 71233920..00000000 --- a/app/src/main/res/layout/fragment_product_detail.xml +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -