Skip to content

Store #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: dev
Choose a base branch
from
Draft

Store #71

wants to merge 7 commits into from

Conversation

almuheetushihab
Copy link
Collaborator

No description provided.

@@ -0,0 +1,3 @@
package com.example.store.models.product

class ProductsResponse : ArrayList<Product>()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:final-newline reported by reviewdog 🐶
File must end with a newline (\n)

fontWeight = FontWeight.Medium
)
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:final-newline reported by reviewdog 🐶
File must end with a newline (\n)

}
},
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.outline,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:trailing-comma-on-call-site reported by reviewdog 🐶
Unnecessary trailing comma before ")"

text = "$${"%.2f".format(product.price * product.quantity)}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.error,
fontWeight = FontWeight.Bold,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:trailing-comma-on-call-site reported by reviewdog 🐶
Unnecessary trailing comma before ")"

import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:no-unused-imports reported by reviewdog 🐶
Unused import

@@ -0,0 +1,2 @@
package com.example.store.ui.screen.categories

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:no-empty-file reported by reviewdog 🐶
File 'CategoriesViewModel.kt' should not be empty

import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.store.theme.StoreAppTheme
import com.example.store.ui.compositions.ProductItem

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:no-unused-imports reported by reviewdog 🐶
Unused import


@Suppress("ktlint:compose:modifier-missing-check")
@Composable
fun CheckOutScreenSkeleton(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] compose:param-order-check reported by reviewdog 🐶
Parameters in a composable function should be ordered following this pattern: params without defaults, modifiers, params with defaults and optionally, a trailing function that might not have a default param.
Current params are: [goBack: () -> Unit = {}, products: List, toggleUIMode: () -> Unit, goToTab: () -> Unit = {}] but could be [products: List, toggleUIMode: () -> Unit, goBack: () -> Unit = {}, goToTab: () -> Unit = {}].
See https://mrmans0n.github.io/compose-rules/rules/#ordering-composable-parameters-properly for more information.

import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] standard:no-unused-imports reported by reviewdog 🐶
Unused import

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @almuheetushihab, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

Summary of Changes

As gemini-code-assist, I've reviewed this pull request titled "Store". The primary goal of this PR is to introduce a new "Store" example feature into the application. This feature demonstrates a basic e-commerce flow, showcasing the integration of various modern Android development technologies including Jetpack Compose for the UI, Hilt for dependency injection, Retrofit and Moshi for networking, Paging 3 for list loading, and Compose Navigation for screen transitions. The new feature is added as a tutorial screen accessible from the main app's tutorial list.

Highlights

  • New Store Module: A new Android library module :store has been created to encapsulate the entire store feature.
  • Jetpack Compose UI: The user interface for the store feature, including screens for Home, Product Details, Cart, Categories, Orders, Profile, and Checkout, is implemented entirely using Jetpack Compose.
  • Compose Navigation: Navigation within the store feature is handled using Jetpack Compose Navigation, utilizing Serializable routes for passing arguments like product IDs or category names.
  • Networking and Data: Retrofit and Moshi are used to interact with a fake store API (fakestoreapi.com) to fetch product and category data. Repositories are implemented to abstract data sources.
  • Paging 3 Integration: The Paging 3 library is used in the Home screen to efficiently load and display a list of products.
  • Dependency Injection: Hilt is configured and used within the :store module for managing dependencies, such as API interfaces and repositories.
  • Main App Integration: The new Store feature is integrated into the main application's navigation graph and added as an entry in the tutorial list.

Changelog

Click here to see the changelog
  • .run/spotlessApply.run.xml
    • Added a new Gradle run configuration for spotlessApply.
  • app/build.gradle.kts
    • Added dependency on the new :store module.
  • app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/NavGraphMain.kt
    • Imported StoreMainScreen.
    • Added TutorialStore data object to TutorialsScreen sealed class.
    • Added a composable route for TutorialsScreen.TutorialStore to navigate to StoreMainScreen.
  • app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/tutorial/index/TutorialList.kt
    • Added a new 'Store' entry to the list of tutorials.
  • base/src/main/kotlin/org/imaginativeworld/whynotcompose/base/utils/Constants.kt
    • Added STORE_SERVER_ENDPOINT constant for the fake store API URL.
  • gradle/libs.versions.toml
    • Added version reference for material.
    • Added library definitions for androidx-material3 and material.
  • settings.gradle.kts
    • Included the new :store module in the project settings.
  • store/.gitignore
    • Added /build to the gitignore for the store module.
  • store/build.gradle.kts
    • Initial setup for the Android library module.
    • Configured plugins (android library, kotlin, serialization, compose, ksp).
    • Defined Android configuration (namespace, SDK versions, compile options, kotlin options, build features).
    • Added dependencies for base module, common-ui-compose, core-ktx, appcompat, swiperefreshlayout, Compose libraries (UI, tooling, animation, foundation, material3, icons, runtime, navigation, constraintlayout, activity, viewmodel, paging), Timber, Hilt, Retrofit, OkHttp, Gson, Moshi, Room, Coil, Kotlinx Serialization, and Haze.
  • store/src/main/AndroidManifest.xml
    • Added a basic AndroidManifest file.
  • store/src/main/kotlin/com/example/store/datasource/ProductPagingSource.kt
    • Implemented ProductPagingSource using Paging 3 to load products from the repository.
  • store/src/main/kotlin/com/example/store/di/StoreAppModule.kt
    • Created a Hilt module (StoreAppModule) to provide Retrofit instance and API interfaces (ProductsApiInterface, CategoriesApiInterface) for the store feature.
  • store/src/main/kotlin/com/example/store/models/categorie/Categories.kt
    • Defined Categories data class extending ArrayList<String>.
  • store/src/main/kotlin/com/example/store/models/categorie/Category.kt
    • Defined Category data class.
    • Added a dummy categories list for preview/placeholder.
  • store/src/main/kotlin/com/example/store/models/product/Product.kt
    • Defined Product data class with Moshi annotations.
  • store/src/main/kotlin/com/example/store/models/product/ProductsResponse.kt
    • Defined ProductsResponse data class extending ArrayList<Product>.
  • store/src/main/kotlin/com/example/store/models/product/Rating.kt
    • Defined Rating data class with Moshi annotations.
  • store/src/main/kotlin/com/example/store/network/api/CategoriesApiInterface.kt
    • Defined Retrofit interface for fetching categories.
  • store/src/main/kotlin/com/example/store/network/api/ProductsApiInterface.kt
    • Defined Retrofit interface for fetching products with pagination.
  • store/src/main/kotlin/com/example/store/repositories/CategoriesRepository.kt
    • Implemented CategoriesRepository using SafeApiRequest to fetch categories.
  • store/src/main/kotlin/com/example/store/repositories/ProductRepository.kt
    • Implemented ProductRepository using SafeApiRequest to fetch products with pagination.
  • store/src/main/kotlin/com/example/store/theme/Color.kt
    • Defined basic Material 3 colors.
  • store/src/main/kotlin/com/example/store/theme/Theme.kt
    • Defined StoreAppTheme Composable using Material 3 color schemes and typography.
  • store/src/main/kotlin/com/example/store/theme/Type.kt
    • Defined basic Material 3 typography.
  • store/src/main/kotlin/com/example/store/ui/compositions/CartCard.kt
    • Created CartItemCard Composable for displaying cart items with quantity controls.
  • store/src/main/kotlin/com/example/store/ui/compositions/CategoryCard.kt
    • Created CategoryCard Composable for displaying category items with Haze effect.
  • store/src/main/kotlin/com/example/store/ui/compositions/KeyValue.kt
    • Created KeyValue Composable for displaying key-value pairs.
  • store/src/main/kotlin/com/example/store/ui/compositions/OrderCard.kt
    • Created OrderCard Composable for displaying order summaries with expandable product lists.
  • store/src/main/kotlin/com/example/store/ui/compositions/OrderProductItem.kt
    • Created OrderProductItem Composable for displaying individual products within an order.
  • store/src/main/kotlin/com/example/store/ui/compositions/ProductItem.kt
    • Created ProductItem Composable for displaying product items in a grid with quantity controls and rating.
  • store/src/main/kotlin/com/example/store/ui/compositions/StoreAppBar.kt
    • Created StoreAppBar Composable for the top app bar with navigation and theme toggle.
  • store/src/main/kotlin/com/example/store/ui/compositions/TabScreen.kt
    • Created TabScreen Composable to define the main tab navigation structure (Home, Categories, Cart).
  • store/src/main/kotlin/com/example/store/ui/screen/StoreMainScreen.kt
    • Created StoreMainScreen as the entry point for the store feature, applying the theme and hosting the navigation graph.
  • store/src/main/kotlin/com/example/store/ui/screen/StoreNavGraph.kt
    • Defined the StoreNavHost Composable and navigation graph for the store feature, including routes for various screens.
  • store/src/main/kotlin/com/example/store/ui/screen/cart/CartScreen.kt
    • Implemented the Cart screen Composable, displaying cart items and total price (using dummy data).
  • store/src/main/kotlin/com/example/store/ui/screen/categories/CategoriesScreen.kt
    • Implemented the Categories screen Composable, displaying a grid of categories (using dummy data).
  • store/src/main/kotlin/com/example/store/ui/screen/categories/CategoriesViewModel.kt
    • Added an empty ViewModel file.
  • store/src/main/kotlin/com/example/store/ui/screen/categorieswiseproduct/CategoriesWiseProductScreen.kt
    • Implemented the Category Wise Product screen Composable (using dummy data).
  • store/src/main/kotlin/com/example/store/ui/screen/checkout/CheckOutScreen.kt
    • Implemented the Checkout screen Composable with address fields and order summary (using dummy data).
  • store/src/main/kotlin/com/example/store/ui/screen/home/StoreHomeScreen.kt
    • Implemented the Home screen Composable, displaying welcome message, profile icon, image carousel, category list, and product grid using Paging 3.
  • store/src/main/kotlin/com/example/store/ui/screen/home/StoreHomeScreenViewModel.kt
    • Implemented StoreHomeScreenViewModel using Hilt, fetching products via PagingSource and categories via repository, managing UI state.
  • store/src/main/kotlin/com/example/store/ui/screen/login/LogInScreen.kt
    • Implemented a simple Login screen Composable with basic validation.
  • store/src/main/kotlin/com/example/store/ui/screen/order/OrderScreen.kt
    • Implemented the Orders screen Composable, displaying a list of orders (using dummy data).
  • store/src/main/kotlin/com/example/store/ui/screen/productdetails/ProductDetailsScreen.kt
    • Implemented the Product Details screen Composable, displaying product information and quantity controls.
    • Added dummy product data (dummyProducts).
  • store/src/main/kotlin/com/example/store/ui/screen/profile/ProfileScreen.kt
    • Implemented the Profile screen Composable as a modal bottom sheet, displaying user details and navigation options (using dummy data).
  • store/src/main/kotlin/com/example/store/ui/screen/splash/SplashScreen.kt
    • Implemented a simple Splash screen Composable with a delay.
  • store/src/main/kotlin/com/example/store/utlis/Constants.kt
    • Added a constants file with a BASE_URL (appears redundant given the base module's Constants).
  • store/src/main/res/drawable/baseline_visibility_24.xml
    • Added an XML drawable for a visibility icon.
  • store/src/main/res/values/colors.xml
    • Defined basic colors for the module.
  • store/src/main/res/values/strings.xml
    • Defined the app name string resource.
  • store/src/main/res/values/themes.xml
    • Defined a basic theme style.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.


Compose screens take flight,
With data flowing just right.
A store built with code,
A digital abode,
Shining bright in the dark or light.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new 'Store' module to the project, demonstrating various UI components, navigation patterns, network calls (using Retrofit and Moshi), and data handling (using Paging 3). The structure of the new module is well-organized, separating concerns into UI, network, models, repositories, and DI. The use of Jetpack Compose for the UI is consistent with the project's theme. However, there are several areas for improvement, particularly regarding data handling, API usage, and some Compose best practices, which are detailed in the review comments below. I've reviewed the code based on general Android Kotlin Style Guide principles.

Summary of Findings

  • Incorrect API Pagination Parameter: The ProductPagingSource and ProductRepository use a page query parameter for fetching products, which is likely not supported by the fakestoreapi.com API. The API documentation suggests using limit and sort instead. This will lead to incorrect or no pagination.
  • Incorrect Total Price Calculation: The totalPrice in CartScreen and CheckOutScreen is calculated only once on initial composition and does not update when quantities change. In CheckOutScreen, the displayed total is also hardcoded.
  • Hardcoded Data in UI Screens: Several screens (TabScreen, StoreNavGraph, CategoriesWiseProductScreen, CheckOutScreen) use hardcoded dummy data (dummyProducts, dummyOrders, categories) instead of fetching dynamic data from the ViewModel/Repository. This prevents the UI from reflecting actual data changes.
  • Mixing API Model and UI State: The Product data class includes a quantity field, which seems to be UI state (for the cart) mixed with API response fields. Separating these concerns into different data classes (e.g., Product and CartItem) would improve maintainability.
  • Non-Idiomatic Kotlin (Extending ArrayList): Extending ArrayList directly in Categories.kt and ProductsResponse.kt is not the standard Kotlin approach. Using type aliases or data classes wrapping List is preferred.
  • Duplicate Constants: The BASE_URL constant is duplicated in store/src/main/kotlin/com/example/store/utlis/Constants.kt and base/src/main/kotlin/org/imaginativeworld/whynotcompose/base/utils/Constants.kt. A single source of truth should be used.
  • Commented-Out Code: There is commented-out code in CartCard.kt, ProductItem.kt, CategoriesWiseProductScreen.kt, and StoreHomeScreen.kt. This should be either uncommented or removed.
  • Hardcoded Local Images: Local drawable resources (R.drawable.store) are used for images in CategoryCard, OrderProductItem, and StoreHomeScreen (profile image) instead of the image URLs provided in the data models. This prevents the display of actual images.
  • Modifier Suppression Annotations: Multiple composables have @Suppress("ktlint:compose:modifier-missing-check") annotations. The underlying lint issue should be addressed or the suppression justified.
  • Unconventional Navigation Back Stack Handling: The navigation logic in StoreNavGraph.kt uses popUpTo(MainScreen.TabScreen) { inclusive = true } for navigating back to the tab screen from Order and Profile, and navController.navigate from Order/Checkout back to the tab screen. This deviates from standard popBackStack() behavior and might lead to unexpected back stack management.
  • Hardcoded Strings: The product count text in OrderCard is hardcoded and not localized.

Merge Readiness

This pull request introduces a significant new feature (the Store module). While the overall structure is good, there are several high-severity issues related to data correctness (pagination, total price calculation) and the use of hardcoded data instead of dynamic data fetching. These issues must be addressed before merging to ensure the module functions correctly with real data. Additionally, there are several medium-severity issues related to maintainability and navigation behavior that should also be addressed.

}
composable(Screens.Cart.route) {
CartScreen(
products = dummyProducts,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The Cart screen is using dummyProducts directly. This means the cart state is not persistent and doesn't reflect the quantities selected on the product list or details screens. The cart state should be managed, perhaps in a ViewModel or shared state, and passed to the CartScreen.

fontWeight = FontWeight.Bold
)
Text(
text = "$4948.00",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The displayed total price is hardcoded to "$4948.00". This should display the dynamically calculated totalPrice.

Suggested change
text = "$4948.00",
text = "$" + "%.2f".format(totalPrice),

var name by remember { mutableStateOf(TextFieldValue("")) }
var phone by remember { mutableStateOf(TextFieldValue("")) }
var address by remember { mutableStateOf(TextFieldValue("")) }
val totalPrice = products.sumOf { it.price }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The totalPrice is calculated once when the composable is first composed. It will not update automatically if the products list changes or if quantities were adjustable on a previous screen. This should reflect the actual total of the items being checked out.

) { paddingValues ->
val context = LocalContext.current
val cartItems = remember { mutableStateListOf(*products.toTypedArray()) }
val totalPrice by remember { mutableDoubleStateOf(cartItems.sumOf { it.price * it.quantity }) }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The totalPrice is calculated once when the composable is first composed. It will not update automatically when the quantities of items in cartItems change via onQuantityChange. You should use derivedStateOf or recalculate the total price whenever the cartItems list or any item's quantity changes.

Suggested change
val totalPrice by remember { mutableDoubleStateOf(cartItems.sumOf { it.price * it.quantity }) }
val totalPrice by remember(cartItems.sumOf { it.price * it.quantity }) { derivedStateOf { cartItems.sumOf { it.price * it.quantity } } }

interface ProductsApiInterface {

@GET("products")
suspend fun getProducts(@Query("page") page: Long): Response<List<Product>>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

As noted in ProductPagingSource.kt, the fakestoreapi.com /products endpoint does not appear to support a page query parameter. It supports limit and sort. You will likely need to change this API definition to match the actual API capabilities.

Suggested change
suspend fun getProducts(@Query("page") page: Long): Response<List<Product>>
suspend fun getProducts(@Query("limit") limit: Int, @Query("sort") sort: String? = null): Response<List<Product>>

Comment on lines +43 to +44
@Suppress("ktlint:compose:modifier-missing-check")
@Composable

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The @Suppress("ktlint:compose:modifier-missing-check") annotation suggests a potential lint issue related to passing modifiers. It's generally better to address the underlying issue or provide a clear justification for the suppression.

Comment on lines +57 to +58
@Suppress("ktlint:compose:modifier-missing-check")
@Composable

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The @Suppress("ktlint:compose:modifier-missing-check") annotation suggests a potential lint issue related to passing modifiers. It's generally better to address the underlying issue or provide a clear justification for the suppression.

Comment on lines +116 to +123
Image(
painter = painterResource(org.imaginativeworld.whynotcompose.common.compose.R.drawable.store),
contentDescription = "Profile Image",
modifier = Modifier
.size(100.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This Image composable uses a hardcoded local drawable (R.drawable.store) for the profile image. While this might be acceptable for a placeholder, using a dynamic image (e.g., from a user profile) would be more realistic for a profile section.

Comment on lines +38 to +39
@Suppress("ktlint:compose:modifier-missing-check")
@Composable

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The @Suppress("ktlint:compose:modifier-missing-check") annotation suggests a potential lint issue related to passing modifiers. It's generally better to address the underlying issue or provide a clear justification for the suppression.

Comment on lines +1 to +6
package com.example.store.utlis
class Constants {
companion object {
const val BASE_URL = "https://fakestoreapi.com/"
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The BASE_URL constant is also defined in base/src/main/kotlin/org/imaginativeworld/whynotcompose/base/utils/Constants.kt. To avoid duplication and maintain a single source of truth, remove this file and use the constant from the base module.

Copy link

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

// description = "Easy upgrade for faster boot up, shutdown, application load and response.",
// quantity = 6
// )
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [ktlint] reported by reviewdog 🐶
Not a valid Kotlin file (319:1 expecting a top level declaration)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant