├── core
├── .gitignore
├── consumer-rules.pro
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── ic_logo.png
│ │ │ ├── product_img_example.png
│ │ │ ├── profile_avatar_placeholder_large.png
│ │ │ ├── searchview_background.xml
│ │ │ ├── bottom_sheet_dialog_background.xml
│ │ │ ├── ic_code.xml
│ │ │ ├── button_gradient.xml
│ │ │ ├── button_small_gradient_background.xml
│ │ │ ├── ic_close.xml
│ │ │ ├── ic_arrow_back.xml
│ │ │ ├── ic_card.xml
│ │ │ ├── ic_tune.xml
│ │ │ ├── ic_sign_out.xml
│ │ │ ├── ic_broom.xml
│ │ │ ├── ic_crescent.xml
│ │ │ ├── ic_cart.xml
│ │ │ ├── ic_bug.xml
│ │ │ └── button_gradient_inactive.xml
│ │ ├── font
│ │ │ ├── montserrat_bold_ttf.ttf
│ │ │ ├── montserrat_medium_ttf.ttf
│ │ │ ├── montserrat_regular_ttf.ttf
│ │ │ └── montserrat_semibold_ttf.ttf
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── colors.xml
│ │ └── layout
│ │ │ ├── list_item_recycler.xml
│ │ │ ├── searchview_custom.xml
│ │ │ └── layout_progress_bar.xml
│ │ ├── java
│ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── core
│ │ │ ├── util
│ │ │ ├── Constants.kt
│ │ │ ├── Log.kt
│ │ │ └── FlowUtils.kt
│ │ │ └── navigation
│ │ │ ├── destination
│ │ │ ├── CartDestination.kt
│ │ │ ├── HomeDestination.kt
│ │ │ ├── UserDestination.kt
│ │ │ ├── CatalogDestination.kt
│ │ │ ├── OrdersDestination.kt
│ │ │ ├── CheckoutDestination.kt
│ │ │ ├── SignUpDestination.kt
│ │ │ ├── ProductDetailsDestination.kt
│ │ │ ├── NavDestination.kt
│ │ │ └── SignInDestination.kt
│ │ │ ├── FragmentExt.kt
│ │ │ └── MarketplaceNavigator.kt
│ │ └── AndroidManifest.xml
└── proguard-rules.pro
├── data
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── data
│ │ │ ├── mapper
│ │ │ ├── DepartmentMapper.kt
│ │ │ ├── CartItemMapper.kt
│ │ │ ├── ProductPreviewMapper.kt
│ │ │ └── OrderMapper.kt
│ │ │ ├── model
│ │ │ ├── CartItemBean.kt
│ │ │ ├── ProductPreviewBean.kt
│ │ │ └── OrderBean.kt
│ │ │ ├── DummyProducts.kt
│ │ │ └── DepartmentRepositoryImpl.kt
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── narcissus
│ │ └── marketplace
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── domain
├── .gitignore
├── src
│ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── domain
│ │ │ ├── model
│ │ │ ├── Cart.kt
│ │ │ ├── orders
│ │ │ │ ├── OrderPaymentStatus.kt
│ │ │ │ ├── OrderStatus.kt
│ │ │ │ ├── Order.kt
│ │ │ │ ├── OrderItem.kt
│ │ │ │ └── OrderPaymentResult.kt
│ │ │ ├── SpecialOfferBanner.kt
│ │ │ ├── UserProfile.kt
│ │ │ ├── Department.kt
│ │ │ ├── Review.kt
│ │ │ ├── ProductOfTheDay.kt
│ │ │ ├── SimilarProduct.kt
│ │ │ ├── User.kt
│ │ │ ├── ProductPreview.kt
│ │ │ ├── CheckoutItem.kt
│ │ │ ├── CartItem.kt
│ │ │ └── ProductDetails.kt
│ │ │ ├── auth
│ │ │ ├── Patterns.kt
│ │ │ ├── SignOutResult.kt
│ │ │ ├── AuthState.kt
│ │ │ ├── SignInResult.kt
│ │ │ ├── SignUpResult.kt
│ │ │ └── PasswordRequirement.kt
│ │ │ ├── card
│ │ │ ├── CardPatterns.kt
│ │ │ └── CardValidationResult.kt
│ │ │ ├── repository
│ │ │ ├── DepartmentRepository.kt
│ │ │ ├── ProductsDetailsRepository.kt
│ │ │ ├── OrderRepository.kt
│ │ │ ├── CartRepository.kt
│ │ │ ├── UserRepository.kt
│ │ │ └── ProductsPreviewRepository.kt
│ │ │ ├── usecase
│ │ │ ├── GetCart.kt
│ │ │ ├── SignOut.kt
│ │ │ ├── GetOrderList.kt
│ │ │ ├── GetUserData.kt
│ │ │ ├── GetAuthStateFlow.kt
│ │ │ ├── GetCartCost.kt
│ │ │ ├── GetSelectedCartItems.kt
│ │ │ ├── GetDepartments.kt
│ │ │ ├── GetRecentlyVisitedProducts.kt
│ │ │ ├── RemoveSelectedCartItems.kt
│ │ │ ├── SignInWithGoogle.kt
│ │ │ ├── GetIsUserAuthenticated.kt
│ │ │ ├── GetAllProducts.kt
│ │ │ ├── SelectAllCartItems.kt
│ │ │ ├── GetRandomProducts.kt
│ │ │ ├── GetTopRatedProducts.kt
│ │ │ ├── GetTopSalesProducts.kt
│ │ │ ├── GetPeopleAreBuyingProducts.kt
│ │ │ ├── RemoveFromCart.kt
│ │ │ ├── GetSimilarProducts.kt
│ │ │ ├── GetProductsByDepartmentId.kt
│ │ │ ├── RestoreCartItems.kt
│ │ │ ├── SetCartItemAmount.kt
│ │ │ ├── GetTopRatedProductsByDepartment.kt
│ │ │ ├── GetTopSalesProductsByDepartment.kt
│ │ │ ├── SetCartItemSelected.kt
│ │ │ ├── GetCheckout.kt
│ │ │ ├── GetCartItemsAmount.kt
│ │ │ ├── SignInWithEmail.kt
│ │ │ ├── GetCartCostFlow.kt
│ │ │ ├── AddToCart.kt
│ │ │ ├── GetProductDetails.kt
│ │ │ ├── SignUpWithEmail.kt
│ │ │ ├── AddCard.kt
│ │ │ └── MakeAnOrder.kt
│ │ │ └── util
│ │ │ ├── ActionResult.kt
│ │ │ └── SearchParams.kt
│ └── test
│ │ └── java
│ │ └── com
│ │ └── narcissus
│ │ └── marketplace
│ │ ├── RemoveSelectedCartItems.kt
│ │ ├── RemoveFromCartTest.kt
│ │ ├── SelectAllCartItemsTest.kt
│ │ ├── GetCartTest.kt
│ │ ├── SetCartItemAmountTest.kt
│ │ ├── SetCartItemSelectedTest.kt
│ │ ├── GetRandomProductsTest.kt
│ │ ├── GetTopRatedProductsTest.kt
│ │ ├── GetTopSalesProductsTest.kt
│ │ ├── GetRecentlyVisitedProductsTest.kt
│ │ └── AddToCartTest.kt
└── build.gradle
├── apiclient
├── .gitignore
├── consumer-rules.pro
├── src
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── apiclient
│ │ │ ├── di
│ │ │ └── Qualifiers.kt
│ │ │ ├── Constants.kt
│ │ │ └── api
│ │ │ ├── service
│ │ │ └── OrderApiService.kt
│ │ │ ├── model
│ │ │ ├── DepartmentsResponse.kt
│ │ │ ├── OrderData.kt
│ │ │ ├── ProductPreviewsResponse.kt
│ │ │ └── SerializedNames.kt
│ │ │ └── interceptor
│ │ │ └── CacheInterceptor.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── firebase
├── .gitignore
├── consumer-rules.pro
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── narcissus
│ │ └── marketplace
│ │ └── data
│ │ └── firebase
│ │ ├── di
│ │ ├── Qualifiers.kt
│ │ └── FirebaseModule.kt
│ │ └── Constants.kt
├── proguard-rules.pro
└── build.gradle
├── persistence
├── .gitignore
├── consumer-rules.pro
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── narcissus
│ │ └── marketplace
│ │ └── data
│ │ └── persistence
│ │ ├── database
│ │ ├── AppDatabase.kt
│ │ └── ProductDao.kt
│ │ ├── model
│ │ └── ProductEntity.kt
│ │ └── di
│ │ └── PersistenceModule.kt
├── proguard-rules.pro
└── build.gradle
├── ui
├── cart
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── narcissus
│ │ │ │ └── marketplace
│ │ │ │ └── ui
│ │ │ │ └── cart
│ │ │ │ ├── di
│ │ │ │ └── CartQualifiers.kt
│ │ │ │ └── checkout
│ │ │ │ ├── OrderConstants.kt
│ │ │ │ └── CheckoutScreenState.kt
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ ├── rounded_shape.xml
│ │ │ ├── button_background_selector.xml
│ │ │ ├── background_button_disabled.xml
│ │ │ ├── custom_checkbox.xml
│ │ │ ├── ic_add.xml
│ │ │ ├── ic_subtract.xml
│ │ │ ├── ic_check_arrow.xml
│ │ │ ├── checkbox_unchecked.xml
│ │ │ ├── checkbox_checked.xml
│ │ │ ├── solid_stroke.xml
│ │ │ ├── ic_visa.xml
│ │ │ └── ic_mastercard.xml
│ │ │ ├── values
│ │ │ └── dimens.xml
│ │ │ └── navigation
│ │ │ └── nav_graph_cart.xml
│ └── proguard-rules.pro
├── catalog
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── narcissus
│ │ │ │ └── marketplace
│ │ │ │ └── ui
│ │ │ │ └── catalog
│ │ │ │ ├── di
│ │ │ │ └── CatalogModule.kt
│ │ │ │ └── CatalogViewModel.kt
│ │ │ └── res
│ │ │ ├── navigation
│ │ │ └── nav_graph_catalog.xml
│ │ │ └── layout
│ │ │ └── fragment_catalog.xml
│ └── proguard-rules.pro
├── home
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── integers.xml
│ │ │ │ └── strings.xml
│ │ │ ├── layout
│ │ │ │ ├── tab_view.xml
│ │ │ │ ├── fragment_home_screen_page.xml
│ │ │ │ ├── list_item_headline.xml
│ │ │ │ ├── list_item_banner.xml
│ │ │ │ └── list_item_featured_content.xml
│ │ │ ├── navigation
│ │ │ │ └── nav_graph_home.xml
│ │ │ └── animator
│ │ │ │ ├── decrease_tab.xml
│ │ │ │ └── increase_tab.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── ui
│ │ │ └── home
│ │ │ ├── recycler
│ │ │ ├── FeaturedTab.kt
│ │ │ └── ProductsOfTheDayAdapter.kt
│ │ │ ├── util
│ │ │ └── TextViewUtils.kt
│ │ │ └── di
│ │ │ └── HomeModule.kt
│ └── proguard-rules.pro
├── search
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── res
│ │ │ ├── navigation
│ │ │ │ └── nav_graph_search.xml
│ │ │ └── layout
│ │ │ │ └── fragment_search_history.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── ui
│ │ │ └── search
│ │ │ └── SearchFragment.kt
│ ├── proguard-rules.pro
│ └── build.gradle
├── sign_in
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── ic_google.png
│ │ │ │ └── button_google_background.xml
│ │ │ └── navigation
│ │ │ │ └── nav_graph_sign_in.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── narcissus
│ │ │ │ └── marketplace
│ │ │ │ └── ui
│ │ │ │ └── sign_in
│ │ │ │ ├── di
│ │ │ │ └── SignInQualifiers.kt
│ │ │ │ ├── sign_up
│ │ │ │ └── SignUpViewModel.kt
│ │ │ │ └── SignInViewModel.kt
│ │ │ └── AndroidManifest.xml
│ └── proguard-rules.pro
├── splash
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── res
│ │ │ └── navigation
│ │ │ │ └── nav_graph_splash.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── ui
│ │ │ └── splash
│ │ │ └── SplashViewModel.kt
│ └── proguard-rules.pro
├── user
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ └── navigation
│ │ │ │ └── nav_graph_user.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ └── ui
│ │ │ └── user
│ │ │ ├── orders
│ │ │ ├── OrdersSideEffect.kt
│ │ │ ├── OrdersState.kt
│ │ │ └── OrdersViewModel.kt
│ │ │ ├── UserState.kt
│ │ │ ├── theme
│ │ │ ├── Shape.kt
│ │ │ ├── Dimen.kt
│ │ │ ├── Font.kt
│ │ │ ├── Color.kt
│ │ │ └── Type.kt
│ │ │ ├── UserSideEffect.kt
│ │ │ └── di
│ │ │ └── UserModule.kt
│ └── proguard-rules.pro
└── product_details
│ ├── .gitignore
│ ├── consumer-rules.pro
│ ├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ └── strings.xml
│ │ ├── drawable
│ │ │ ├── reviews_recyclerview_divider.xml
│ │ │ ├── recycler_view_divider.xml
│ │ │ └── dotted_line.xml
│ │ ├── layout
│ │ │ ├── list_item_details_main_info.xml
│ │ │ ├── list_item_details_main_info_placeholder.xml
│ │ │ ├── list_item_details_product_placeholder.xml
│ │ │ ├── list_item_details_price.xml
│ │ │ ├── list_item_product_preview_loading.xml
│ │ │ ├── list_item_details_titile_basic.xml
│ │ │ ├── list_item_details_divider.xml
│ │ │ ├── list_item_details_title_button.xml
│ │ │ └── list_item_details_product_about_multiple_lines.xml
│ │ └── anim
│ │ │ ├── slide_in_left.xml
│ │ │ ├── slide_in_right.xml
│ │ │ ├── slide_out_left.xml
│ │ │ └── slide_out_right.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── narcissus
│ │ └── marketplace
│ │ └── ui
│ │ └── product_details
│ │ ├── model
│ │ ├── ToolbarData.kt
│ │ └── ParcelableReview.kt
│ │ ├── di
│ │ └── ProductDetailsModule.kt
│ │ ├── ProductsAdapter.kt
│ │ ├── utils
│ │ └── GetTextLinearGradient.kt
│ │ ├── main_info_recycler_view
│ │ └── ProductMainInfoAdapter.kt
│ │ └── reviews
│ │ └── DividerDecoration.kt
│ └── proguard-rules.pro
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── app
├── src
│ └── main
│ │ ├── ic_launcher-playstore.png
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── drawable
│ │ │ ├── profile_avatar_placeholder_large.png
│ │ │ ├── rectangle_back.xml
│ │ │ ├── ic_arrow_forward_ios_black_36dp.xml
│ │ │ ├── ic_arrow_back_black_36dp.xml
│ │ │ ├── ic_home.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_catalog.xml
│ │ │ ├── ic_user.xml
│ │ │ └── ic_cart.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── color
│ │ │ └── background_search_tint.xml
│ │ ├── menu
│ │ │ └── bottom_navigation.xml
│ │ ├── navigation
│ │ │ └── nav_graph.xml
│ │ ├── values-night
│ │ │ └── themes.xml
│ │ └── values
│ │ │ └── themes.xml
│ │ ├── java
│ │ └── com
│ │ │ └── narcissus
│ │ │ └── marketplace
│ │ │ ├── di
│ │ │ └── AppModule.kt
│ │ │ └── ui
│ │ │ └── MarketplaceApp.kt
│ │ └── AndroidManifest.xml
├── .gitignore
└── proguard-rules.pro
├── .gitignore
├── .cspell.json
├── settings.gradle
├── .mega-linter.yml
├── gradle.properties
└── .github
└── workflows
└── android_deploy_to_firebase.yml
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/data/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/apiclient/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/apiclient/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/firebase/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/firebase/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/persistence/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/cart/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/cart/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/catalog/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/home/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/home/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/search/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/search/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/sign_in/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/splash/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/splash/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/user/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/user/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/persistence/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/catalog/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/sign_in/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/product_details/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ui/product_details/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/core/src/main/res/drawable/ic_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/core/src/main/res/font/montserrat_bold_ttf.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/core/src/main/res/font/montserrat_bold_ttf.ttf
--------------------------------------------------------------------------------
/ui/sign_in/src/main/res/drawable/ic_google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/ui/sign_in/src/main/res/drawable/ic_google.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/core/src/main/res/drawable/product_img_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/core/src/main/res/drawable/product_img_example.png
--------------------------------------------------------------------------------
/core/src/main/res/font/montserrat_medium_ttf.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/core/src/main/res/font/montserrat_medium_ttf.ttf
--------------------------------------------------------------------------------
/core/src/main/res/font/montserrat_regular_ttf.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/core/src/main/res/font/montserrat_regular_ttf.ttf
--------------------------------------------------------------------------------
/core/src/main/res/font/montserrat_semibold_ttf.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/core/src/main/res/font/montserrat_semibold_ttf.ttf
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ui/user/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Source code
4 |
5 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 150
4 |
5 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/Cart.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class Cart(
4 | val data: List
5 | )
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/profile_avatar_placeholder_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/app/src/main/res/drawable/profile_avatar_placeholder_large.png
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 150dp
4 |
5 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/util/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.util
2 |
3 | object Constants {
4 | const val THEME_KEY = "THEME_KEY"
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/profile_avatar_placeholder_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beleavemebe/marketplace-app/HEAD/core/src/main/res/drawable/profile_avatar_placeholder_large.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 | google-services.json
12 | /report
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/auth/Patterns.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.auth
2 |
3 | object Patterns {
4 | const val EMAIL = "^[A-Za-z](.*)([@])(.+)(\\.)(.+)"
5 | }
6 |
--------------------------------------------------------------------------------
/ui/sign_in/src/main/java/com/narcissus/marketplace/ui/sign_in/di/SignInQualifiers.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.sign_in.di
2 |
3 | object SignInQualifiers {
4 | object DefaultWebClientId
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/di/Qualifiers.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient.di
2 |
3 | object Qualifiers {
4 | object OrderApiService
5 | object ContentApiService
6 | }
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/card/CardPatterns.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.card
2 |
3 | object CardPatterns {
4 | const val CARD_HOLDER_NAME = "^((?:[A-Za-z]+ ?){1,3})\$"
5 | }
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/orders/OrderPaymentStatus.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model.orders
2 |
3 | enum class OrderPaymentStatus {
4 | PAID,
5 | CANCELLED
6 | }
7 |
--------------------------------------------------------------------------------
/ui/cart/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/home/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/home/src/main/java/com/narcissus/marketplace/ui/home/recycler/FeaturedTab.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.home.recycler
2 |
3 | enum class FeaturedTab {
4 | TOP_RATED,
5 | TOP_SALES,
6 | EXPLORE
7 | }
8 |
--------------------------------------------------------------------------------
/ui/user/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/apiclient/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/firebase/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/catalog/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/search/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/sign_in/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/splash/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/orders/OrdersSideEffect.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.orders
2 |
3 | sealed class OrdersSideEffect {
4 | object NavigateUp : OrdersSideEffect()
5 | }
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/SpecialOfferBanner.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class SpecialOfferBanner(
4 | val imgUrl: String,
5 | val destinationLink: String,
6 | )
7 |
--------------------------------------------------------------------------------
/persistence/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/auth/SignOutResult.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.auth
2 |
3 | sealed class SignOutResult {
4 | object Error : SignOutResult()
5 | object Success : SignOutResult()
6 | }
7 |
--------------------------------------------------------------------------------
/firebase/src/main/java/com/narcissus/marketplace/data/firebase/di/Qualifiers.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.firebase.di
2 |
3 | object Qualifiers {
4 | object CartReference
5 | object OrdersReference
6 | object UserUid
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/util/Log.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.util
2 |
3 | import android.util.Log
4 |
5 | @Suppress("unused")
6 | inline fun Any.log(msg: () -> Any?) = Log.d("marketplace-debug", msg().toString())
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/orders/OrderStatus.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model.orders
2 |
3 | enum class OrderStatus {
4 | ACCEPTED,
5 | COMPLETED,
6 | INDELEVERING,
7 | CANCELED,
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/CartDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | object CartDestination : NavDestination {
4 | override val url = "marketplace-app://cart"
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/HomeDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | object HomeDestination : NavDestination {
4 | override val url = "marketplace-app://home"
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/UserDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | object UserDestination : NavDestination {
4 | override val url = "marketplace-app://user"
5 | }
6 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/java/com/narcissus/marketplace/ui/product_details/model/ToolbarData.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.product_details.model
2 |
3 | data class ToolbarData(
4 | val productIcon: String,
5 | val productName: String,
6 | )
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/UserProfile.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class UserProfile(
4 | val id: String,
5 | val name: String?,
6 | val email: String,
7 | val iconUrl: String?
8 | )
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/CatalogDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | object CatalogDestination : NavDestination {
4 | override val url = "marketplace-app://catalog"
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/OrdersDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | object OrdersDestination : NavDestination {
4 | override val url = "marketplace-app://user/orders"
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/CheckoutDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | object CheckoutDestination : NavDestination {
4 | override val url = "marketplace-app://cart/checkout"
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/SignUpDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | object SignUpDestination : NavDestination {
4 | override val url: String = "marketplace-app://sign-up"
5 | }
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/Department.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class Department(
4 | val departmentId: String,
5 | val name: String,
6 | val productsAmount: Int,
7 | val imageUrl: String,
8 | )
9 |
--------------------------------------------------------------------------------
/ui/cart/src/main/java/com/narcissus/marketplace/ui/cart/di/CartQualifiers.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.cart.di
2 |
3 | object CartQualifiers {
4 | object PaymentInProgressNotification
5 | object PaymentOneTimeRequest
6 | object PaymentWorkerInputData
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/searchview_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Feb 09 17:38:01 MSK 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/core/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 | 12dp
5 | 16dp
6 | 32dp
7 |
8 |
--------------------------------------------------------------------------------
/ui/home/src/main/java/com/narcissus/marketplace/ui/home/util/TextViewUtils.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.home.util
2 |
3 | import android.graphics.Paint
4 | import android.widget.TextView
5 |
6 | fun TextView.crossOut() {
7 | paintFlags = paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
8 | }
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/repository/DepartmentRepository.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.repository
2 |
3 | import com.narcissus.marketplace.domain.model.Department
4 |
5 | interface DepartmentRepository {
6 | suspend fun getDepartments(): List
7 | }
8 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/rounded_shape.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/ProductDetailsDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | class ProductDetailsDestination(productId: String) : NavDestination {
4 | override val url = "marketplace-app://product/$productId"
5 | }
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/Review.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class Review(
4 | val reviewId: String,
5 | val author: String,
6 | val details: String,
7 | val rating: Int,
8 | val reviewAuthorIcon: String,
9 | )
10 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/drawable/reviews_recyclerview_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/orders/OrdersState.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.orders
2 |
3 | import com.narcissus.marketplace.domain.model.orders.Order
4 |
5 | data class OrdersState(
6 | val isLoading: Boolean,
7 | val orders: List = emptyList()
8 | )
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetCart.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 |
5 | class GetCart(private val cartRepository: CartRepository) {
6 | operator fun invoke() = cartRepository.getCart()
7 | }
8 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/button_background_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.cspell.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignorePaths": [
3 | "**/node_modules/**",
4 | "**/vscode-extension/**",
5 | "**/.git/**",
6 | ".vscode",
7 | "megalinter",
8 | "package-lock.json",
9 | "report"
10 | ],
11 | "language": "en",
12 | "noConfigSearch": true,
13 | "words": [],
14 | "version": "0.2"
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/NavDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | import android.net.Uri
4 |
5 | sealed interface NavDestination {
6 | val url: String
7 | }
8 |
9 | val NavDestination.uri: Uri
10 | get() = Uri.parse(url)
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/SignOut.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.UserRepository
4 |
5 | class SignOut(private val userRepository: UserRepository) {
6 | suspend operator fun invoke() = userRepository.signOut()
7 | }
8 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/background_button_disabled.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/color/background_search_tint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/destination/SignInDestination.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation.destination
2 |
3 | class SignInDestination(hasNavigatedFromUserScreen: Boolean) : NavDestination {
4 | override val url: String = "marketplace-app://sign-in/$hasNavigatedFromUserScreen"
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/bottom_sheet_dialog_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/ProductOfTheDay.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class ProductOfTheDay(
4 | val id: String,
5 | val imageUrl: String,
6 | val name: String,
7 | val oldPrice: Int,
8 | val newPrice: Int,
9 | val percentOff: Int,
10 | )
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetOrderList.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.OrderRepository
4 |
5 | class GetOrderList(private val orderRepository: OrderRepository) {
6 | operator fun invoke() = orderRepository.getOrders()
7 | }
8 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/custom_checkbox.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/repository/ProductsDetailsRepository.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.repository
2 |
3 | import com.narcissus.marketplace.domain.model.ProductDetails
4 |
5 | interface ProductsDetailsRepository {
6 | suspend fun getProductDetailsById(productId: String): ProductDetails
7 | }
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetUserData.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.UserRepository
4 |
5 | class GetUserData(private val userRepository: UserRepository) {
6 | suspend operator fun invoke() = userRepository.getUserData()
7 | }
8 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/drawable/recycler_view_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/UserState.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user
2 |
3 | import com.narcissus.marketplace.domain.model.UserProfile
4 |
5 | data class UserState(
6 | val isLoading: Boolean,
7 | val isUserAuthenticated: Boolean? = null,
8 | val user: UserProfile? = null,
9 | )
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetAuthStateFlow.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.UserRepository
4 |
5 | class GetAuthStateFlow(private val userRepository: UserRepository) {
6 | operator fun invoke() = userRepository.authStateFlow
7 | }
8 |
--------------------------------------------------------------------------------
/firebase/src/main/java/com/narcissus/marketplace/data/firebase/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.firebase
2 |
3 | internal object Constants {
4 | const val DATABASE_URL = "https://epam-marketplace-app-default-rtdb.europe-west1.firebasedatabase.app/"
5 | const val CHILD_CART = "cart"
6 | const val CHILD_ORDERS = "orders"
7 | }
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetCartCost.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 |
5 | class GetCartCost(private val cartRepository: CartRepository) {
6 | suspend operator fun invoke() =
7 | cartRepository.getCartCost()
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rectangle_back.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/orders/Order.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model.orders
2 |
3 | import java.util.Date
4 |
5 | data class Order(
6 | val id: String,
7 | val number: Int,
8 | val date: Date,
9 | val status: OrderStatus,
10 | val summaryPrice: Int,
11 | val items: List,
12 | )
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetSelectedCartItems.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 |
5 | class GetSelectedCartItems(private val cartRepository: CartRepository) {
6 | suspend operator fun invoke() = cartRepository.getSelectedCartItems()
7 | }
8 |
--------------------------------------------------------------------------------
/ui/sign_in/src/main/res/drawable/button_google_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/SimilarProduct.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class SimilarProduct(
4 | val id: String,
5 | val icon: String,
6 | val price: Int,
7 | val name: String,
8 | val category: String,
9 | val type: String,
10 | val stock: Int,
11 | val rating: Int,
12 | )
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetDepartments.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.DepartmentRepository
4 |
5 | class GetDepartments(private val departmentsRepository: DepartmentRepository) {
6 | suspend operator fun invoke() = departmentsRepository.getDepartments()
7 | }
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetRecentlyVisitedProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.UserRepository
4 |
5 | class GetRecentlyVisitedProducts(private val userRepository: UserRepository) {
6 | operator fun invoke() = userRepository.recentlyVisitedProducts
7 | }
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/RemoveSelectedCartItems.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 |
5 | class RemoveSelectedCartItems(private val cartRepository: CartRepository) {
6 | suspend operator fun invoke() = cartRepository.deleteSelectedItems()
7 | }
8 |
--------------------------------------------------------------------------------
/ui/catalog/src/main/java/com/narcissus/marketplace/ui/catalog/di/CatalogModule.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.catalog.di
2 |
3 | import com.narcissus.marketplace.ui.catalog.CatalogViewModel
4 | import org.koin.androidx.viewmodel.dsl.viewModel
5 | import org.koin.dsl.module
6 |
7 | val catalogModule = module {
8 | viewModel { CatalogViewModel(get()) }
9 | }
10 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/drawable/dotted_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 |
6 | val Shapes = Shapes(
7 | small = RoundedCornerShape(HalfPadding / 2),
8 | medium = RoundedCornerShape(HalfPadding),
9 | )
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/auth/AuthState.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.auth
2 |
3 | import com.narcissus.marketplace.domain.model.UserProfile
4 |
5 | sealed class AuthState {
6 | object Unknown : AuthState()
7 | object NotAuthenticated : AuthState()
8 | data class Authenticated(val user: UserProfile?) : AuthState()
9 | }
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/SignInWithGoogle.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.UserRepository
4 |
5 | class SignInWithGoogle(private val userRepository: UserRepository) {
6 | suspend operator fun invoke(idToken: String) = userRepository.signInWithGoogle(idToken)
7 | }
8 |
--------------------------------------------------------------------------------
/ui/home/src/main/java/com/narcissus/marketplace/ui/home/di/HomeModule.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.home.di
2 |
3 | import com.narcissus.marketplace.ui.home.HomeViewModel
4 | import org.koin.androidx.viewmodel.dsl.viewModel
5 | import org.koin.dsl.module
6 |
7 | val homeModule = module {
8 | viewModel { HomeViewModel(get(), get(), get(), get(), get()) }
9 | }
10 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/layout/tab_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetIsUserAuthenticated.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.UserRepository
4 |
5 | class GetIsUserAuthenticated(private val userRepository: UserRepository) {
6 | suspend operator fun invoke() =
7 | userRepository.isUserAuthenticated()
8 | }
9 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/theme/Dimen.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.theme
2 |
3 | import androidx.compose.ui.unit.dp
4 |
5 | val IconSize = 18.dp
6 |
7 | val SmallPadding = 4.dp
8 | val HalfPadding = 8.dp
9 | val IntermediatePadding = 12.dp
10 | val DefaultPadding = 16.dp
11 |
12 | val HeaderHeight = 56.dp
13 | val ItemHeight = 40.dp
14 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient
2 |
3 | object Constants {
4 | internal const val BASE_URL = "https://api-narcissus-marketplace.herokuapp.com/"
5 | internal const val CACHE_DIR = "http-cache"
6 | internal const val CACHE_SIZE = 10L * 1024L * 1024L
7 | internal const val CACHE_MAX_AGE = 3
8 | }
9 |
--------------------------------------------------------------------------------
/domain/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | id 'org.jetbrains.kotlin.jvm'
4 | }
5 |
6 | java {
7 | sourceCompatibility = JavaVersion.VERSION_1_7
8 | targetCompatibility = JavaVersion.VERSION_1_7
9 | }
10 | dependencies {
11 | implementation(Coroutines.coroutinesCore)
12 | testImplementation(Junit.junit)
13 | testImplementation(Mockk.mockk)
14 | }
15 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetAllProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetAllProducts(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke() = productsPreviewRepository.getProducts()
7 | }
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/SelectAllCartItems.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 |
5 | class SelectAllCartItems(private val cartRepository: CartRepository) {
6 | suspend operator fun invoke(isSelected: Boolean) = cartRepository.selectAllCartItems(isSelected)
7 | }
8 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_main_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/mapper/DepartmentMapper.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.mapper
2 |
3 | import com.narcissus.marketplace.apiclient.api.model.DepartmentResponseData
4 | import com.narcissus.marketplace.domain.model.Department
5 |
6 | fun DepartmentResponseData.toDepartment() =
7 | Department(
8 | id, name, numberOfProducts, imageUrl,
9 | )
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetRandomProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetRandomProducts(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke() = productsPreviewRepository.getProductsRandom()
7 | }
8 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/ic_subtract.xml:
--------------------------------------------------------------------------------
1 |
4 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_forward_ios_black_36dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetTopRatedProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetTopRatedProducts(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke() = productsPreviewRepository.getProductsTopRated()
7 | }
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetTopSalesProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetTopSalesProducts(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke() = productsPreviewRepository.getProductsTopSales()
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_back_black_36dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/model/CartItemBean.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.model
2 |
3 | data class CartItemBean(
4 | var productId: String? = null,
5 | var productImage: String? = null,
6 | var productPrice: Int = 0,
7 | var productName: String? = null,
8 | var amount: Int = 0,
9 | var isSelected: Boolean = false,
10 | var stock: Int? = null
11 | )
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/User.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | import com.narcissus.marketplace.domain.model.orders.Order
4 |
5 | data class User(
6 | val id: String,
7 | val email: String,
8 | val firstName: String,
9 | val lastName: String,
10 | val cart: Cart,
11 | val orders: List,
12 | val cartNumber: Long,
13 | )
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetPeopleAreBuyingProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetPeopleAreBuyingProducts(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke() = productsPreviewRepository.getProductsPeopleAreBuying()
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/RemoveFromCart.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 |
6 | class RemoveFromCart(private val cartRepository: CartRepository) {
7 | suspend operator fun invoke(cartItem: CartItem) = cartRepository.removeFromCart(cartItem)
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_code.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/UserSideEffect.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user
2 |
3 | sealed class UserSideEffect {
4 | data class Toast(val text: String) : UserSideEffect()
5 | data class SwitchTheme(val checked: Boolean) : UserSideEffect()
6 | object NavigateToSignIn : UserSideEffect()
7 | object NavigateToOrders : UserSideEffect()
8 | object ViewSourceCode : UserSideEffect()
9 | }
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetSimilarProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetSimilarProducts(
6 | private val productsPreviewRepository: ProductsPreviewRepository,
7 | ) {
8 | suspend operator fun invoke(id: String) =
9 | productsPreviewRepository.getSimilarProducts(id)
10 | }
11 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/ic_check_arrow.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/ProductPreview.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class ProductPreview(
4 | val id: String,
5 | val icon: String,
6 | val price: Int,
7 | val name: String,
8 | val department: String,
9 | val type: String,
10 | val stock: Int,
11 | val color: String,
12 | val material: String,
13 | val rating: Int,
14 | val sales: Int
15 | )
16 |
--------------------------------------------------------------------------------
/ui/cart/src/main/java/com/narcissus/marketplace/ui/cart/checkout/OrderConstants.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.cart.checkout
2 |
3 | object OrderConstants {
4 | const val PAY_INTENT_FILTER = "com.narcissus.marketplace.ui.cart.checkout.IntentFilter.PAY_INTENT_FILTER"
5 | const val KEY_ON_COMPLETE_NOTIFICATION_ID = "NotificationId"
6 | const val KEY_ORDER_UUID = "Order UUID"
7 | const val CHECKOUT_CHANNEL_ID = "MarketplaceChannelId"
8 | }
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/card/CardValidationResult.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.card
2 |
3 | sealed class CardValidationResult {
4 | object InvalidCardNumber : CardValidationResult()
5 | object InvalidExpirationDate : CardValidationResult()
6 | object InvalidCardHolderName : CardValidationResult()
7 | object InvalidCvv : CardValidationResult()
8 | object Success : CardValidationResult()
9 | }
10 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/anim/slide_in_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetProductsByDepartmentId.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetProductsByDepartmentId(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke(departmentId: String) =
7 | productsPreviewRepository.getProductsByDepartment(departmentId)
8 | }
9 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/anim/slide_in_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/anim/slide_out_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/anim/slide_out_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/button_gradient.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/RestoreCartItems.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 |
6 | class RestoreCartItems(private val cartRepository: CartRepository) {
7 | suspend operator fun invoke(orderList: List) =
8 | cartRepository.addAllSelectedToCart(orderList)
9 | }
10 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/di/UserModule.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.di
2 |
3 | import com.narcissus.marketplace.ui.user.UserViewModel
4 | import com.narcissus.marketplace.ui.user.orders.OrdersViewModel
5 | import org.koin.androidx.viewmodel.dsl.viewModel
6 | import org.koin.dsl.module
7 |
8 | val userModule = module {
9 | viewModel { UserViewModel(get(), get()) }
10 | viewModel { OrdersViewModel(get()) }
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/checkbox_unchecked.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/FragmentExt.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | val Fragment.navigator: MarketplaceNavigator
6 | get() {
7 | return (requireActivity() as? MarketplaceNavigator)
8 | ?: throw IllegalStateException(
9 | "Host activity does not implement `MarketplaceCrossModuleNavigator` interface",
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/SetCartItemAmount.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 |
6 | class SetCartItemAmount(private val cartRepository: CartRepository) {
7 | suspend operator fun invoke(cartItem: CartItem, amount: Int) =
8 | cartRepository.setCartItemAmount(cartItem, amount)
9 | }
10 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/java/com/narcissus/marketplace/ui/product_details/di/ProductDetailsModule.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.product_details.di
2 |
3 | import com.narcissus.marketplace.ui.product_details.ProductDetailsViewModel
4 | import org.koin.androidx.viewmodel.dsl.viewModel
5 | import org.koin.dsl.module
6 |
7 | val productDetailsModule = module {
8 | viewModel { (productId: String) -> ProductDetailsViewModel(productId, get(), get(), get()) }
9 | }
10 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/button_small_gradient_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetTopRatedProductsByDepartment.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetTopRatedProductsByDepartment(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke(departmentId: String) =
7 | productsPreviewRepository.getProductsByDepartmentIdTopRated(departmentId)
8 | }
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetTopSalesProductsByDepartment.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
4 |
5 | class GetTopSalesProductsByDepartment(private val productsPreviewRepository: ProductsPreviewRepository) {
6 | suspend operator fun invoke(departmentId: String) =
7 | productsPreviewRepository.getProductsByDepartmentIdTopSales(departmentId)
8 | }
9 |
--------------------------------------------------------------------------------
/data/src/test/java/com/narcissus/marketplace/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_arrow_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/SetCartItemSelected.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 |
6 | class SetCartItemSelected(private val cartRepository: CartRepository) {
7 | suspend operator fun invoke(cartItem: CartItem, selected: Boolean) =
8 | cartRepository.setCartItemSelected(cartItem, selected)
9 | }
10 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/model/ProductPreviewBean.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.model
2 |
3 | data class ProductPreviewBean(
4 | var id: String = "",
5 | var icon: String = "",
6 | var price: Int = 0,
7 | var name: String = "",
8 | var department: String = "",
9 | var type: String = "",
10 | var stock: Int = 0,
11 | var color: String = "",
12 | var material: String = "",
13 | var rating: Int = 0,
14 | var sales: Int = 0
15 | )
16 |
--------------------------------------------------------------------------------
/ui/home/src/main/java/com/narcissus/marketplace/ui/home/recycler/ProductsOfTheDayAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.home.recycler
2 |
3 | import com.google.android.material.card.MaterialCardView
4 | import com.hannesdorfmann.adapterdelegates4.ListDelegationAdapter
5 |
6 | class ProductsOfTheDayAdapter(
7 | onProductClicked: (id: String, cardView: MaterialCardView) -> Unit
8 | ) : ListDelegationAdapter>(
9 | ProductOfTheDayItem.delegate(onProductClicked)
10 | )
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/narcissus/marketplace/di/AppModule.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.di
2 |
3 | import com.narcissus.marketplace.R
4 | import com.narcissus.marketplace.ui.sign_in.di.SignInQualifiers
5 | import org.koin.android.ext.koin.androidContext
6 | import org.koin.core.qualifier.qualifier
7 | import org.koin.dsl.module
8 |
9 | val appModule = module {
10 | factory(qualifier()) {
11 | androidContext().getString(R.string.default_web_client_id)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/auth/SignInResult.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.auth
2 |
3 | import com.narcissus.marketplace.domain.model.UserProfile
4 |
5 | sealed class SignInResult {
6 | object Error : SignInResult()
7 | object InvalidEmail : SignInResult()
8 | object BlankPassword : SignInResult()
9 | object WrongPassword : SignInResult()
10 | object UserNotFound : SignInResult()
11 | data class Success(val userProfile: UserProfile) : SignInResult()
12 | }
13 |
--------------------------------------------------------------------------------
/ui/search/src/main/res/navigation/nav_graph_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/auth/SignUpResult.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.auth
2 |
3 | import com.narcissus.marketplace.domain.model.UserProfile
4 |
5 | sealed class SignUpResult {
6 | object Error : SignUpResult()
7 | object BlankFullName : SignUpResult()
8 | object InvalidEmail : SignUpResult()
9 | data class InvalidPassword(val failedRequirements: List) : SignUpResult()
10 | data class Success(val userProfile: UserProfile) : SignUpResult()
11 | }
12 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/CheckoutItem.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class CheckoutItem(
4 | val detailId: String,
5 | val detailName: String,
6 | val detailAmount: Int,
7 | val detailPrice: Int,
8 | )
9 |
10 | fun CartItem.toCheckoutItem(): CheckoutItem =
11 | CheckoutItem(
12 | detailId = productId,
13 | detailName = productName,
14 | detailPrice = amount * productPrice,
15 | detailAmount = amount,
16 | )
17 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_card.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_tune.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetCheckout.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.CheckoutItem
4 | import com.narcissus.marketplace.domain.model.toCheckoutItem
5 | import com.narcissus.marketplace.domain.repository.CartRepository
6 |
7 | class GetCheckout(private val cartRepository: CartRepository) {
8 | suspend operator fun invoke(): List =
9 | cartRepository.getSelectedCartItems().map { it.toCheckoutItem() }
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_sign_out.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/cart/src/main/java/com/narcissus/marketplace/ui/cart/checkout/CheckoutScreenState.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.cart.checkout
2 |
3 | import com.narcissus.marketplace.domain.model.CheckoutItem
4 |
5 | sealed class CheckoutScreenState {
6 | object Loading : CheckoutScreenState()
7 | data class Idle(val items: List, val totalCost: Int) : CheckoutScreenState()
8 | data class PaymentFailed(val message: String) : CheckoutScreenState()
9 | data class PaymentSuccessful(val message: String) : CheckoutScreenState()
10 | }
11 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/api/service/OrderApiService.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient.api.service
2 |
3 | import com.narcissus.marketplace.apiclient.api.model.OrderPaymentQueryBody
4 | import com.narcissus.marketplace.apiclient.api.model.OrderPaymentResponse
5 | import retrofit2.http.Body
6 | import retrofit2.http.POST
7 |
8 | interface OrderApiService {
9 | @POST("actions/checkout")
10 | suspend fun payForTheOrder(
11 | @Body body: OrderPaymentQueryBody
12 | ): OrderPaymentResponse
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/orders/OrderItem.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model.orders
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 |
5 | data class OrderItem(
6 | val productId: String,
7 | val productImage: String,
8 | val productPrice: Int,
9 | val productName: String,
10 | val amount: Int,
11 | val amountPrice: Int,
12 | )
13 | fun CartItem.toOrderItem(): OrderItem =
14 | OrderItem(productId, productImage, productPrice, productName, amount, productPrice * amount)
15 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetCartItemsAmount.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.mapLatest
6 |
7 | class GetCartItemsAmount(private val cartRepository: CartRepository) {
8 | operator fun invoke(): Flow =
9 | cartRepository.getCart()
10 | .mapLatest { items ->
11 | items.count { it.isSelected }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/theme/Font.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.theme
2 |
3 | import androidx.compose.ui.text.font.Font
4 | import androidx.compose.ui.text.font.toFontFamily
5 | import com.narcissus.marketplace.core.R as CORE
6 |
7 | val Montserrat = Font(CORE.font.montserrat_regular_ttf).toFontFamily()
8 | val MontserratMedium = Font(CORE.font.montserrat_medium_ttf).toFontFamily()
9 | val MontserratSemiBold = Font(CORE.font.montserrat_semibold_ttf).toFontFamily()
10 | val MontserratBold = Font(CORE.font.montserrat_bold_ttf).toFontFamily()
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_catalog.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/persistence/src/main/java/com/narcissus/marketplace/data/persistence/database/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.persistence.database
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import com.narcissus.marketplace.data.persistence.model.ProductEntity
6 |
7 | @Database(
8 | entities = [ProductEntity::class],
9 | version = 1,
10 | )
11 | abstract class AppDatabase : RoomDatabase() {
12 | abstract fun productDao(): ProductDao
13 |
14 | companion object {
15 | const val DATABASE_NAME = "marketplace-app.db"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/repository/OrderRepository.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.repository
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.model.orders.Order
5 | import com.narcissus.marketplace.domain.model.orders.OrderPaymentResult
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | interface OrderRepository {
9 | fun getOrders(): Flow>
10 | suspend fun payForTheOrder(orderList: List, orderUUID: String): OrderPaymentResult
11 | suspend fun saveOrder(order: Order)
12 | }
13 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_main_info_placeholder.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/navigation/nav_graph_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/java/com/narcissus/marketplace/ui/product_details/ProductsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.product_details
2 |
3 | import com.google.android.material.card.MaterialCardView
4 | import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
5 |
6 | class ProductsAdapter(
7 | onProductClicked: (id: String, cardView: MaterialCardView) -> Unit
8 | ) : AsyncListDifferDelegationAdapter(
9 | ProductListItem.DIFF_CALLBACK,
10 | ProductListItem.LoadingProduct.delegate(),
11 | ProductListItem.Product.delegate(onProductClicked),
12 | )
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/util/FlowUtils.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.util
2 |
3 | import androidx.lifecycle.LifecycleCoroutineScope
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.collect
6 |
7 | fun Flow.launchWhenStarted(lifecycleScope: LifecycleCoroutineScope) {
8 | lifecycleScope.launchWhenStarted {
9 | this@launchWhenStarted.collect()
10 | }
11 | }
12 |
13 | fun Flow.launchWhenResumed(lifecycleScope: LifecycleCoroutineScope) {
14 | lifecycleScope.launchWhenResumed {
15 | this@launchWhenResumed.collect()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ui/catalog/src/main/res/navigation/nav_graph_catalog.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ui/splash/src/main/res/navigation/nav_graph_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_product_placeholder.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/CartItem.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class CartItem(
4 | val productId: String,
5 | val productImage: String,
6 | val productPrice: Int,
7 | val productName: String,
8 | val amount: Int,
9 | val isSelected: Boolean,
10 | val stock: Int
11 | )
12 |
13 | fun ProductPreview.toCartItem() =
14 | CartItem(
15 | productId = id,
16 | productImage = icon,
17 | productPrice = price,
18 | productName = name,
19 | amount = 1,
20 | isSelected = false,
21 | stock = stock
22 | )
23 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_broom.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_crescent.xml:
--------------------------------------------------------------------------------
1 |
6 |
13 |
14 |
--------------------------------------------------------------------------------
/core/src/main/res/layout/list_item_recycler.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/narcissus/marketplace/core/navigation/MarketplaceNavigator.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.core.navigation
2 |
3 | import androidx.navigation.NavOptions
4 | import androidx.navigation.Navigator
5 | import com.narcissus.marketplace.core.navigation.destination.NavDestination
6 |
7 | interface MarketplaceNavigator {
8 | fun navigate(destination: NavDestination)
9 | fun navigate(destination: NavDestination, options: NavOptions)
10 | fun navigate(destination: NavDestination, extras: Navigator.Extras)
11 | fun navigate(destination: NavDestination, options: NavOptions, extras: Navigator.Extras)
12 | fun navigateUp()
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/res/layout/searchview_custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/DummyProducts.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data
2 |
3 | import com.narcissus.marketplace.domain.model.ProductPreview
4 |
5 | internal object DummyProducts {
6 | val previews = listOf(
7 | ProductPreview("1", "https://c.tenor.com/UjdeUF--bBkAAAAS/sussy.gif", 1449, "Apple MacBook Pro 13", "", "", 752, "", "", 3, 152),
8 | ProductPreview("2", "https://c.tenor.com/UjdeUF--bBkAAAAS/sussy.gif", 1299, "Apple MacBook Air 13", "", "", 1021, "", "", 5, 196),
9 | ProductPreview("3", "https://c.tenor.com/UjdeUF--bBkAAAAS/sussy.gif", 2199, "Apple MacBook Pro 16", "", "", 128, "", "", 4, 65),
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple200 = Color(0xFFBB86FC)
6 | val Purple500 = Color(0xFF6200EE)
7 | val Purple700 = Color(0xFF3700B3)
8 | val Teal200 = Color(0xFF03DAC5)
9 |
10 | val GradientBackgroundStart = Color(0xFF68B4FA)
11 | val GradientBackgroundEnd = Color(0xFF8E44EB)
12 |
13 | val Primary = Color(0xFF8E44EB)
14 | val Black = Color(0xFF000000)
15 | val White = Color(0xFFFFFFFF)
16 | val DarkPrimary = Color(0xFF292331)
17 | val GreyLight = Color(0xFFF6F6F6)
18 | val Grey = Color(0xFF707070)
19 | val Red = Color(0xFFF30808)
20 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/checkbox_checked.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/java/com/narcissus/marketplace/ui/product_details/utils/GetTextLinearGradient.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.product_details.utils
2 |
3 | import android.content.Context
4 | import android.graphics.LinearGradient
5 | import android.graphics.Shader
6 | import com.narcissus.marketplace.core.R as CORE
7 |
8 | fun getTextLinearGradient(context: Context) = LinearGradient(
9 | 0f,
10 | 0f,
11 | 100f,
12 | 100f,
13 | arrayOf(
14 | context.getColor(CORE.color.gradient_background_start),
15 | context.getColor(CORE.color.gradient_background_end),
16 | ).toIntArray(),
17 | null,
18 | Shader.TileMode.CLAMP,
19 | )
20 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_price.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_product_preview_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/persistence/src/main/java/com/narcissus/marketplace/data/persistence/model/ProductEntity.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.persistence.model
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "product")
7 | data class ProductEntity(
8 | @PrimaryKey
9 | val id: String = "",
10 | val icon: String = "",
11 | val price: Int = 0,
12 | val name: String = "",
13 | val department: String = "",
14 | val type: String = "",
15 | val stock: Int = 0,
16 | val color: String = "",
17 | val material: String = "",
18 | val rating: Int = 0,
19 | val sales: Int = 0,
20 | val created: Long = System.currentTimeMillis(),
21 | )
22 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/java/com/narcissus/marketplace/ui/product_details/model/ParcelableReview.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.product_details.model
2 |
3 | import android.os.Parcelable
4 | import com.narcissus.marketplace.domain.model.Review
5 | import kotlinx.parcelize.Parcelize
6 |
7 | @Parcelize
8 | data class ParcelableReview(
9 | val reviewId: String,
10 | val author: String,
11 | val details: String,
12 | val rating: Int,
13 | val reviewAuthorIcon: String,
14 | ) : Parcelable
15 |
16 | fun Review.toParcelableReview() =
17 | ParcelableReview(
18 | reviewId,
19 | author,
20 | details,
21 | rating,
22 | reviewAuthorIcon,
23 | )
24 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/util/ActionResult.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.util
2 |
3 | typealias Mapper = (Input) -> Output
4 |
5 | sealed class ActionResult {
6 | class SuccessResult(val data: T) : ActionResult()
7 | class ErrorResult(val message: String) : ActionResult()
8 |
9 | fun mapResult(mapper: Mapper) = when (this) {
10 | is SuccessResult -> SuccessResult(mapper(this.data))
11 | is ErrorResult -> ErrorResult(this.message)
12 | }
13 |
14 | fun getOrThrow(): T = when (this) {
15 | is ErrorResult -> error("Result is error")
16 | is SuccessResult -> this.data
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/persistence/src/main/java/com/narcissus/marketplace/data/persistence/database/ProductDao.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.persistence.database
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.narcissus.marketplace.data.persistence.model.ProductEntity
8 | import kotlinx.coroutines.flow.Flow
9 |
10 | @Dao
11 | interface ProductDao {
12 | // TODO: add limit of ~15
13 | @Query("SELECT * FROM product ORDER BY created DESC")
14 | fun getProducts(): Flow>
15 |
16 | @Insert(onConflict = OnConflictStrategy.REPLACE)
17 | suspend fun insertProduct(productEntity: ProductEntity)
18 | }
19 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/java/com/narcissus/marketplace/ui/product_details/main_info_recycler_view/ProductMainInfoAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.product_details.main_info_recycler_view
2 |
3 | import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
4 |
5 | class ProductMainInfoAdapter(
6 | onPurchaseClicked: () -> Unit,
7 | onGoToCartClicked: () -> Unit,
8 | ) : AsyncListDifferDelegationAdapter(
9 | ProductMainInfoItem.DIFF_CALLBACK,
10 | ProductMainInfoItem.RatingSection.delegate(),
11 | ProductMainInfoItem.ActivePurchaseButton.delegate(onPurchaseClicked),
12 | ProductMainInfoItem.InactivePurchaseButton.delegate(onGoToCartClicked),
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/bottom_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/mapper/CartItemMapper.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.mapper
2 |
3 | import com.narcissus.marketplace.data.model.CartItemBean
4 | import com.narcissus.marketplace.domain.model.CartItem
5 |
6 | fun CartItemBean.toCartItem(): CartItem? =
7 | runCatching {
8 | CartItem(
9 | productId!!,
10 | productImage!!,
11 | productPrice,
12 | productName!!,
13 | amount,
14 | isSelected,
15 | stock!!,
16 | )
17 | }.getOrDefault(null)
18 |
19 | fun CartItem.toBean(): CartItemBean =
20 | CartItemBean(productId, productImage, productPrice, productName, amount, isSelected, stock)
21 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/layout/fragment_home_screen_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/orders/OrderPaymentResult.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model.orders
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import java.util.Date
5 |
6 | class OrderPaymentResult(
7 | val id: String?,
8 | val number: Int?,
9 | val status: OrderPaymentStatus,
10 | val message: String,
11 | )
12 |
13 | fun List.toOrder(orderUUID: String, orderNumber: Int, orderDate: Date, orderStatus: OrderStatus): Order =
14 | Order(
15 | orderUUID,
16 | orderNumber,
17 | orderDate,
18 | orderStatus,
19 | this.sumOf { it.amount * it.productPrice },
20 | this.map { it.toOrderItem() },
21 | )
22 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/solid_stroke.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @dimen/margin_small
4 | 14sp
5 | 16sp
6 | 1dp
7 | 18dp
8 | 100dp
9 | 20dp
10 | 70dp
11 | 25dp
12 | 100dp
13 | 40dp
14 |
15 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/layout/list_item_headline.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_titile_basic.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_cart.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_cart.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/model/OrderBean.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.model
2 |
3 | import com.narcissus.marketplace.domain.model.orders.OrderStatus
4 | import java.util.Date
5 |
6 | data class OrderBean(
7 | val number: Int? = null,
8 | val id: String? = null,
9 | val date: Date? = null,
10 | val status: OrderStatus? = null,
11 | val summaryPrice: Int? = null,
12 | val items: List? = null,
13 | )
14 |
15 | data class OrderItemBean(
16 | val productId: String? = null,
17 | val productImage: String? = null,
18 | val productPrice: Int? = null,
19 | val productName: String? = null,
20 | val amount: Int? = null,
21 | val amountPrice: Int? = null
22 | )
23 |
--------------------------------------------------------------------------------
/persistence/src/main/java/com/narcissus/marketplace/data/persistence/di/PersistenceModule.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.persistence.di
2 |
3 | import androidx.room.Room
4 | import com.narcissus.marketplace.data.persistence.database.AppDatabase
5 | import com.narcissus.marketplace.data.persistence.database.AppDatabase.Companion.DATABASE_NAME
6 | import org.koin.android.ext.koin.androidContext
7 | import org.koin.dsl.module
8 |
9 | val persistenceModule = module {
10 | single {
11 | Room.databaseBuilder(
12 | androidContext(),
13 | AppDatabase::class.java,
14 | DATABASE_NAME
15 | ).build()
16 | }
17 |
18 | single {
19 | get().productDao()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_bug.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/api/model/DepartmentsResponse.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient.api.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class DepartmentsResponse(
6 | @SerializedName(SerializedNames.data)
7 | val data: List
8 | )
9 |
10 | data class DepartmentResponseData(
11 | @SerializedName(SerializedNames.departmentId)
12 | val id: String,
13 |
14 | @SerializedName(SerializedNames.departmentName)
15 | val name: String,
16 |
17 | @SerializedName(SerializedNames.departmentNumProducts)
18 | val numberOfProducts: Int,
19 |
20 | @SerializedName(SerializedNames.departmentImageUrl)
21 | val imageUrl: String,
22 | )
23 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "Marketplace App"
16 | include ':app'
17 | include ':core'
18 | include ':domain'
19 | include ':data'
20 | include ':ui:user'
21 | include ':apiclient'
22 | include ':persistence'
23 | include ':ui:search'
24 | include ':firebase'
25 | include ':ui:splash'
26 | include ':ui:cart'
27 | include ':ui:catalog'
28 | include ':ui:home'
29 | include ':ui:sign_in'
30 | include ':ui:product_details'
31 |
--------------------------------------------------------------------------------
/.mega-linter.yml:
--------------------------------------------------------------------------------
1 | # Configuration file for MegaLinter
2 | # See all available variables at https://megalinter.github.io/configuration/ and in linters documentation
3 |
4 | APPLY_FIXES: all # all, none, or list of linter keys
5 | # ENABLE: # If you use ENABLE variable, all other languages/formats/tooling-formats will be disabled by default
6 | ENABLE_LINTERS: # If you use ENABLE_LINTERS variable, all other linters will be disabled by default
7 | - KOTLIN_KTLINT
8 | DISABLE:
9 | - COPYPASTE # Comment to enable checks of excessive copy-pastes
10 | # - SPELL # Uncomment to disable checks of spelling mistakes
11 | SHOW_ELAPSED_TIME: false
12 | FILEIO_REPORTER: true
13 | # DISABLE_ERRORS: true # Uncomment if you want MegaLinter to detect errors but not block CI to pass
14 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/DepartmentRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data
2 |
3 | import com.narcissus.marketplace.apiclient.api.model.DepartmentResponseData
4 | import com.narcissus.marketplace.apiclient.api.service.ApiService
5 | import com.narcissus.marketplace.data.mapper.toDepartment
6 | import com.narcissus.marketplace.domain.model.Department
7 | import com.narcissus.marketplace.domain.repository.DepartmentRepository
8 |
9 | internal class DepartmentRepositoryImpl(
10 | private val apiService: ApiService,
11 | ) : DepartmentRepository {
12 | override suspend fun getDepartments(): List {
13 | return apiService.getDepartments().data
14 | .map(DepartmentResponseData::toDepartment)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ui/splash/src/main/java/com/narcissus/marketplace/ui/splash/SplashViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.splash
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.MutableStateFlow
7 | import kotlinx.coroutines.flow.StateFlow
8 | import kotlinx.coroutines.flow.asStateFlow
9 | import kotlinx.coroutines.launch
10 |
11 | class SplashViewModel : ViewModel() {
12 | private val _isLaunchedFlow = MutableStateFlow(false)
13 | val isLaunchedFlow: StateFlow = _isLaunchedFlow.asStateFlow()
14 |
15 | fun launch() {
16 | viewModelScope.launch {
17 | delay(1000L)
18 | _isLaunchedFlow.value = true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | NCS Shop
4 | Home
5 | Catalog
6 | Cart
7 | User
8 | NCS SHOP
9 | Order
10 | NCS Shop Logo
11 | $%1$d
12 | %1$dx
13 | #%1$s
14 | Please, enter your full name
15 | Add to cart
16 |
17 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/SignInWithEmail.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.auth.Patterns
4 | import com.narcissus.marketplace.domain.auth.SignInResult
5 | import com.narcissus.marketplace.domain.repository.UserRepository
6 |
7 | class SignInWithEmail(private val userRepository: UserRepository) {
8 | suspend operator fun invoke(email: String, pass: String): SignInResult {
9 | if (!Patterns.EMAIL.toRegex().matches(email)) {
10 | return SignInResult.InvalidEmail
11 | }
12 |
13 | if (pass.isBlank()) {
14 | return SignInResult.BlankPassword
15 | }
16 |
17 | return userRepository.signInWithEmail(email, pass)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Top sales
4 | Top rated
5 | You viewed
6 | Explore
7 | Special offer
8 | Products of the day
9 | -%1$d%%
10 | Featured
11 | That\'s it! 🙃\nVisit our catalog\nfor more cool offers
12 | Special offer banner
13 | People are buying
14 |
15 |
--------------------------------------------------------------------------------
/ui/search/src/main/res/layout/fragment_search_history.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/api/interceptor/CacheInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient.api.interceptor
2 |
3 | import com.narcissus.marketplace.apiclient.Constants
4 | import okhttp3.CacheControl
5 | import okhttp3.Interceptor
6 | import okhttp3.Response
7 | import java.util.concurrent.TimeUnit
8 |
9 | class CacheInterceptor : Interceptor {
10 | override fun intercept(chain: Interceptor.Chain): Response {
11 | val response = chain.proceed(chain.request())
12 |
13 | val cacheControl = CacheControl.Builder()
14 | .maxAge(Constants.CACHE_MAX_AGE, TimeUnit.MINUTES)
15 | .build()
16 |
17 | return response.newBuilder()
18 | .header("Cache-Control", cacheControl.toString())
19 | .build()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ui/search/src/main/java/com/narcissus/marketplace/ui/search/SearchFragment.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.search
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.fragment.app.Fragment
6 | import com.narcissus.marketplace.ui.search.databinding.FragmentSearchHistoryBinding
7 |
8 | class SearchFragment : Fragment(R.layout.fragment_search_history) {
9 | private var _binding: FragmentSearchHistoryBinding? = null
10 | private val binding get() = _binding!!
11 |
12 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
13 | super.onViewCreated(view, savedInstanceState)
14 | _binding = FragmentSearchHistoryBinding.bind(view)
15 | }
16 |
17 | override fun onDestroyView() {
18 | super.onDestroyView()
19 | _binding = null
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/repository/CartRepository.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.repository
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface CartRepository {
7 | fun getCart(): Flow>
8 | suspend fun addToCart(cartItem: CartItem)
9 | suspend fun removeFromCart(cartItem: CartItem)
10 | suspend fun setCartItemSelected(cartItem: CartItem, selected: Boolean)
11 | suspend fun setCartItemAmount(cartItem: CartItem, amount: Int)
12 | suspend fun selectAllCartItems(isSelected: Boolean)
13 | suspend fun deleteSelectedItems()
14 | suspend fun addAllSelectedToCart(cartItems: List)
15 | suspend fun getSelectedCartItems(): List
16 | suspend fun getCartCost(): Int
17 | }
18 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetCartCostFlow.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.mapLatest
6 |
7 | class GetCartCostFlow(private val cartRepository: CartRepository) {
8 | operator fun invoke(): Flow =
9 | cartRepository.getCart()
10 | .mapLatest { items ->
11 | if (items.isEmpty()) {
12 | 0
13 | } else {
14 | items.asSequence()
15 | .filter { it.isSelected }
16 | .map { it.productPrice * it.amount }
17 | .reduceOrNull(Int::plus) ?: 0
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/data/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/AddToCart.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.ProductDetails
4 | import com.narcissus.marketplace.domain.model.ProductPreview
5 | import com.narcissus.marketplace.domain.model.toCartItem
6 | import com.narcissus.marketplace.domain.model.toProductPreview
7 | import com.narcissus.marketplace.domain.repository.CartRepository
8 |
9 | class AddToCart(private val cartRepository: CartRepository) {
10 | suspend operator fun invoke(productPreview: ProductPreview) {
11 | val cartItem = productPreview.toCartItem()
12 | cartRepository.addToCart(cartItem)
13 | }
14 |
15 | suspend operator fun invoke(productDetails: ProductDetails) {
16 | invoke(productDetails.toProductPreview())
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/firebase/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/cart/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/home/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/ui/user/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/apiclient/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/data/src/androidTest/java/com/narcissus/marketplace/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.narcissus.marketplace.test", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/model/ProductDetails.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.model
2 |
3 | data class ProductDetails(
4 | val id: String,
5 | val icon: String,
6 | val price: Int,
7 | val name: String,
8 | val department: String,
9 | val type: String,
10 | val stock: Int,
11 | val color: String,
12 | val material: String,
13 | val description: String,
14 | val rating: Int,
15 | val sales: Int,
16 | val reviews: List,
17 | )
18 |
19 | fun ProductDetails.toProductPreview(): ProductPreview {
20 | return ProductPreview(
21 | id,
22 | icon,
23 | price,
24 | name,
25 | department,
26 | type,
27 | stock,
28 | color,
29 | material,
30 | rating,
31 | sales
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/persistence/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/catalog/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/search/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/sign_in/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/splash/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ui/product_details/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/util/SearchParams.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.util
2 |
3 | data class SearchParams(
4 | val query: String,
5 | val filters: Set = setOf(FilterType.None),
6 | val sortType: SortType = SortType.NONE,
7 | val sortDirection: SortDirection = SortDirection.ASC
8 | ) {
9 |
10 | sealed class FilterType {
11 | class Rating(val minValue: Int, maxValue: Int) : FilterType()
12 | class Sales(val minValue: Int, maxValue: Int) : FilterType()
13 | class Price(val minValue: Int, maxValue: Int) : FilterType()
14 | object None : FilterType()
15 | }
16 |
17 | enum class SortType {
18 | RATING,
19 | SALES,
20 | PRICE,
21 | NONE
22 | }
23 |
24 | enum class SortDirection {
25 | DESC,
26 | ASC
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/RemoveSelectedCartItems.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 | import com.narcissus.marketplace.domain.usecase.RemoveSelectedCartItems
5 | import io.mockk.coEvery
6 | import io.mockk.coVerify
7 | import io.mockk.mockk
8 | import kotlinx.coroutines.runBlocking
9 | import org.junit.Test
10 |
11 | class RemoveSelectedCartItems {
12 | private val cartRepository = mockk {
13 | coEvery { deleteSelectedItems() } returns Unit
14 | }
15 | val removeSelectedFromCart = RemoveSelectedCartItems(cartRepository)
16 |
17 | @Test
18 | fun `should call removal of selected items`() {
19 | runBlocking {
20 | removeSelectedFromCart()
21 | }
22 | coVerify(exactly = 1) { removeSelectedFromCart() }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ui/user/src/main/res/navigation/nav_graph_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/animator/decrease_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
14 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/animator/increase_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
14 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/GetProductDetails.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.ProductDetails
4 | import com.narcissus.marketplace.domain.model.toProductPreview
5 | import com.narcissus.marketplace.domain.repository.ProductsDetailsRepository
6 | import com.narcissus.marketplace.domain.repository.UserRepository
7 |
8 | class GetProductDetails(
9 | private val productsDetailsRepository: ProductsDetailsRepository,
10 | private val userRepository: UserRepository,
11 | ) {
12 | suspend operator fun invoke(productId: String): ProductDetails {
13 | val productDetails = productsDetailsRepository
14 | .getProductDetailsById(productId)
15 |
16 | userRepository.writeToVisitedProducts(productDetails.toProductPreview())
17 |
18 | return productDetails
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/api/model/OrderData.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient.api.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class OrderPaymentResponse(
6 | @SerializedName("order_id")
7 | val orderId: String,
8 | @SerializedName("order_number")
9 | val orderNumber: Int?,
10 | @SerializedName("order_payment_status")
11 | val orderPaymentStatus: String,
12 | @SerializedName("message")
13 | val message: String,
14 | )
15 |
16 | data class OrderPaymentQueryBody(
17 | @SerializedName("id")
18 | val id: String,
19 | @SerializedName("order_items")
20 | val orderedItemsList: List,
21 | )
22 |
23 | data class OrderPaymentRequestBodyItem(
24 | @SerializedName("product_id")
25 | val productId: String,
26 | @SerializedName("product_amount")
27 | val productAmount: Int,
28 | )
29 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Product image
4 | %1$d sales
5 | In stock: %1$d
6 | Purchase
7 | Type
8 | Color
9 | Material
10 | Description
11 | About
12 | Reviews
13 | Similar Products
14 | Show all reviews
15 | Hide all reviews
16 | All reviews
17 | Go to Cart
18 |
19 |
--------------------------------------------------------------------------------
/core/src/main/res/drawable/button_gradient_inactive.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
12 |
13 |
14 |
15 | -
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/navigation/nav_graph_cart.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/core/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 | #707070
11 | #6B6B6B
12 | #EAE4E4
13 | #9432C2
14 | #EBEFFF
15 | #68B4FA
16 | #8E44EB
17 | #FF0000
18 | #121212
19 | #3E5ABE
20 |
21 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/RemoveFromCartTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 | import com.narcissus.marketplace.domain.usecase.RemoveFromCart
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.runBlocking
10 | import org.junit.Test
11 |
12 | class RemoveFromCartTest {
13 | private val cartItem = mockk()
14 | private val cartRepository = mockk {
15 | coEvery { removeFromCart(cartItem) } returns Unit
16 | }
17 | val removeFromCart = RemoveFromCart(cartRepository)
18 |
19 | @Test
20 | fun `should remove item from cart`() {
21 | runBlocking {
22 | removeFromCart(cartItem)
23 | }
24 | coVerify(exactly = 1) { cartRepository.removeFromCart(cartItem) }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/layout/list_item_banner.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/SelectAllCartItemsTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.repository.CartRepository
4 | import com.narcissus.marketplace.domain.usecase.SelectAllCartItems
5 | import io.mockk.coEvery
6 | import io.mockk.coVerify
7 | import io.mockk.mockk
8 | import kotlinx.coroutines.runBlocking
9 | import org.junit.Test
10 |
11 | class SelectAllCartItemsTest {
12 | private val isSelected = mockk(relaxed = true)
13 | private val cartRepository = mockk {
14 | coEvery { selectAllCartItems(any()) } returns Unit
15 | }
16 | private val selectAllCartItems = SelectAllCartItems(cartRepository)
17 |
18 | @Test
19 | fun `should call all cart item selection with correct attribute`() {
20 | runBlocking {
21 | selectAllCartItems(isSelected)
22 | }
23 | coVerify(exactly = 1) { cartRepository.selectAllCartItems(isSelected) }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/res/layout/layout_progress_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
21 |
22 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/GetCartTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 | import com.narcissus.marketplace.domain.usecase.GetCart
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.runBlocking
11 | import org.junit.Assert
12 | import org.junit.Test
13 |
14 | class GetCartTest {
15 | private val exceptedResult = mockk>>()
16 | private val cartRepository = mockk {
17 | coEvery { getCart() } returns exceptedResult
18 | }
19 | private val getCart = GetCart(cartRepository)
20 | private val result = getCart()
21 |
22 | @Test
23 | fun `should return actual cart`() {
24 | runBlocking {
25 | Assert.assertEquals(exceptedResult, result)
26 | }
27 | coVerify(exactly = 1) { cartRepository.getCart() }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ui/home/src/main/res/layout/list_item_featured_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/SetCartItemAmountTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 | import com.narcissus.marketplace.domain.usecase.SetCartItemAmount
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.runBlocking
10 | import org.junit.Test
11 |
12 | class SetCartItemAmountTest {
13 | private val expectedCartItem = mockk()
14 | private val amount = mockk(relaxed = true)
15 | private val cartRepository = mockk {
16 | coEvery { setCartItemAmount(expectedCartItem, amount) } returns Unit
17 | }
18 | private val setCartItemAmount = SetCartItemAmount(cartRepository)
19 |
20 | @Test
21 | fun `should set cart item amount`() {
22 | runBlocking {
23 | setCartItemAmount(expectedCartItem, amount)
24 | }
25 | coVerify(exactly = 1) { cartRepository.setCartItemAmount(expectedCartItem, amount) }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ui/sign_in/src/main/res/navigation/nav_graph_sign_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/SignUpWithEmail.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.auth.PasswordRequirement
4 | import com.narcissus.marketplace.domain.auth.Patterns.EMAIL
5 | import com.narcissus.marketplace.domain.auth.SignUpResult
6 | import com.narcissus.marketplace.domain.repository.UserRepository
7 |
8 | class SignUpWithEmail(private val userRepository: UserRepository) {
9 | suspend operator fun invoke(fullName: String, email: String, password: String): SignUpResult {
10 | if (fullName.isBlank()) {
11 | return SignUpResult.BlankFullName
12 | }
13 |
14 | if (email.isBlank() || !EMAIL.toRegex().matches(email)) {
15 | return SignUpResult.InvalidEmail
16 | }
17 |
18 | val failedRequirements = PasswordRequirement.findFailedRequirements(password)
19 | if (failedRequirements.isNotEmpty()) {
20 | return SignUpResult.InvalidPassword(failedRequirements)
21 | }
22 |
23 | return userRepository.signUpWithEmail(fullName, email, password)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/SetCartItemSelectedTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.repository.CartRepository
5 | import com.narcissus.marketplace.domain.usecase.SetCartItemSelected
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.runBlocking
10 | import org.junit.Test
11 |
12 | class SetCartItemSelectedTest {
13 | private val expectedCartItem = mockk()
14 | private val selection = mockk(relaxed = true)
15 | private val cartRepository = mockk {
16 | coEvery { setCartItemSelected(expectedCartItem, selection) } returns Unit
17 | }
18 | private val setCartItemAmount = SetCartItemSelected(cartRepository)
19 |
20 | @Test
21 | fun `should set cart item selection`() {
22 | runBlocking {
23 | setCartItemAmount(expectedCartItem, selection)
24 | }
25 | coVerify(exactly = 1) { cartRepository.setCartItemSelected(expectedCartItem, selection) }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/GetRandomProductsTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.ProductPreview
4 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
5 | import com.narcissus.marketplace.domain.usecase.GetRandomProducts
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.runBlocking
10 | import org.junit.Assert
11 | import org.junit.Test
12 |
13 | class GetRandomProductsTest {
14 | private val expectedProductPreviews = mockk>()
15 | private val productsPreviewRepository = mockk {
16 | coEvery { getProductsRandom() } returns expectedProductPreviews
17 | }
18 | private val getRandomProducts = GetRandomProducts(productsPreviewRepository)
19 |
20 | @Test
21 | fun `should return actual random products`() {
22 | runBlocking {
23 | Assert.assertEquals(expectedProductPreviews, getRandomProducts())
24 | }
25 | coVerify(exactly = 1) { productsPreviewRepository.getProductsRandom() }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/mapper/ProductPreviewMapper.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.mapper
2 |
3 | import com.narcissus.marketplace.data.model.ProductPreviewBean
4 | import com.narcissus.marketplace.data.persistence.model.ProductEntity
5 | import com.narcissus.marketplace.domain.model.ProductPreview
6 |
7 | fun ProductEntity.toProductPreview(): ProductPreview {
8 | return ProductPreview(
9 | id = id,
10 | icon = icon,
11 | price = price,
12 | name = name,
13 | department = department,
14 | type = type,
15 | stock = stock,
16 | color = color,
17 | material = material,
18 | rating = rating,
19 | sales = sales
20 | )
21 | }
22 |
23 | fun ProductPreviewBean.toProductPreview(): ProductPreview {
24 | return ProductPreview(
25 | id = id,
26 | icon = icon,
27 | price = price,
28 | name = name,
29 | department = department,
30 | type = type,
31 | stock = stock,
32 | color = color,
33 | material = material,
34 | rating = rating,
35 | sales = sales,
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/GetTopRatedProductsTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.ProductPreview
4 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
5 | import com.narcissus.marketplace.domain.usecase.GetTopRatedProducts
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.runBlocking
10 | import org.junit.Assert
11 | import org.junit.Test
12 |
13 | class GetTopRatedProductsTest {
14 | private val expectedProductPreviews = mockk>()
15 | private val productsPreviewRepository = mockk {
16 | coEvery { getProductsTopRated() } returns expectedProductPreviews
17 | }
18 | private val getTopRatedProducts = GetTopRatedProducts(productsPreviewRepository)
19 |
20 | @Test
21 | fun `should return actual top rated products`() {
22 | runBlocking {
23 | Assert.assertEquals(expectedProductPreviews, getTopRatedProducts())
24 | }
25 | coVerify(exactly = 1) { productsPreviewRepository.getProductsTopRated() }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/GetTopSalesProductsTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.ProductPreview
4 | import com.narcissus.marketplace.domain.repository.ProductsPreviewRepository
5 | import com.narcissus.marketplace.domain.usecase.GetTopSalesProducts
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.runBlocking
10 | import org.junit.Assert
11 | import org.junit.Test
12 |
13 | class GetTopSalesProductsTest {
14 | private val expectedProductPreviews = mockk>()
15 | private val productsPreviewRepository = mockk {
16 | coEvery { getProductsTopSales() } returns expectedProductPreviews
17 | }
18 | private val getTopSalesProducts = GetTopSalesProducts(productsPreviewRepository)
19 |
20 | @Test
21 | fun `should return actual top rated products`() {
22 | runBlocking {
23 | Assert.assertEquals(expectedProductPreviews, getTopSalesProducts())
24 | }
25 | coVerify(exactly = 1) { productsPreviewRepository.getProductsTopSales() }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/ic_visa.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/java/com/narcissus/marketplace/ui/product_details/reviews/DividerDecoration.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.product_details.reviews
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.drawable.Drawable
5 | import androidx.recyclerview.widget.RecyclerView
6 | import androidx.recyclerview.widget.RecyclerView.ItemDecoration
7 |
8 | class DividerDecoration(
9 | private val divider: Drawable,
10 | ) : ItemDecoration() {
11 | override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
12 | val dividerLeft = parent.paddingLeft
13 | val dividerRight = parent.width - parent.paddingRight
14 | val childCount = parent.childCount
15 | for (i in 0..childCount - 2) {
16 | val child = parent.getChildAt(i)
17 | val params = child.layoutParams as RecyclerView.LayoutParams
18 | val dividerTop = child.bottom + params.bottomMargin
19 | val dividerBottom = dividerTop + divider.intrinsicHeight
20 | divider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom)
21 | divider.draw(canvas)
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/apiclient/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
5 | }
6 |
7 | android {
8 | compileSdk 32
9 |
10 | defaultConfig {
11 | minSdk 24
12 | targetSdk 32
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | consumerProguardFiles "consumer-rules.pro"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | kotlinOptions {
29 | jvmTarget = '1.8'
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation(Retrofit.retrofit)
35 | implementation(Retrofit.gsonConverters)
36 |
37 | implementation(Coroutines.coroutinesCore)
38 |
39 | implementation(Koin.koinCore)
40 | implementation(Koin.koinAndroid)
41 | testImplementation(Koin.koinTest)
42 | }
43 |
--------------------------------------------------------------------------------
/ui/catalog/src/main/java/com/narcissus/marketplace/ui/catalog/CatalogViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.catalog
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.narcissus.marketplace.domain.usecase.GetDepartments
6 | import com.narcissus.marketplace.ui.catalog.DepartmentListItem.DepartmentItem
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.SharingStarted
9 | import kotlinx.coroutines.flow.flow
10 | import kotlinx.coroutines.flow.shareIn
11 |
12 | class CatalogViewModel(
13 | private val getDepartments: GetDepartments,
14 | ) : ViewModel() {
15 | companion object {
16 | private const val DEPARTMENTS_AMOUNT = 14
17 | }
18 |
19 | val departments: Flow> = flow {
20 | val dummyDepartments = Array(DEPARTMENTS_AMOUNT) {
21 | DepartmentListItem.LoadingDepartmentItem()
22 | }
23 | emit(dummyDepartments.toList())
24 |
25 | val departments = getDepartments().map(::DepartmentItem)
26 | emit(departments)
27 | }.shareIn(
28 | viewModelScope,
29 | SharingStarted.Lazily,
30 | 1
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/ui/sign_in/src/main/java/com/narcissus/marketplace/ui/sign_in/sign_up/SignUpViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.sign_in.sign_up
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.narcissus.marketplace.domain.auth.SignUpResult
6 | import com.narcissus.marketplace.domain.usecase.SignUpWithEmail
7 | import kotlinx.coroutines.channels.BufferOverflow
8 | import kotlinx.coroutines.flow.MutableSharedFlow
9 | import kotlinx.coroutines.flow.asSharedFlow
10 | import kotlinx.coroutines.launch
11 |
12 | class SignUpViewModel(
13 | val signUpWithEmail: SignUpWithEmail,
14 | ) : ViewModel() {
15 | private val _signUpResultFlow = MutableSharedFlow(
16 | replay = 1,
17 | extraBufferCapacity = 1,
18 | onBufferOverflow = BufferOverflow.DROP_OLDEST
19 | )
20 |
21 | val signUpResultFlow = _signUpResultFlow.asSharedFlow()
22 |
23 | fun signUpWithEmailPassword(fullName: String, email: String, password: String) {
24 | viewModelScope.launch {
25 | val authResult = signUpWithEmail(fullName, email, password)
26 | _signUpResultFlow.emit(authResult)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/api/model/ProductPreviewsResponse.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient.api.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class ProductPreviewsResponse(
6 | @SerializedName(SerializedNames.data)
7 | val data: List
8 | )
9 |
10 | data class ProductPreviewResponseData(
11 | @SerializedName(SerializedNames.id)
12 | val id: String,
13 | @SerializedName(SerializedNames.icon)
14 | val icon: String,
15 | @SerializedName(SerializedNames.productName)
16 | val name: String,
17 | @SerializedName(SerializedNames.price)
18 | val price: Int,
19 | @SerializedName(SerializedNames.type)
20 | val type: String,
21 | @SerializedName(SerializedNames.productDepartment)
22 | val departmentName: String,
23 | @SerializedName(SerializedNames.stock)
24 | val stock: Int,
25 | @SerializedName(SerializedNames.color)
26 | val color: String,
27 | @SerializedName(SerializedNames.material)
28 | val material: String,
29 | @SerializedName(SerializedNames.productRating)
30 | val rating: Int,
31 | @SerializedName(SerializedNames.sales)
32 | val sales: Int
33 | )
34 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/repository/UserRepository.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.repository
2 |
3 | import com.narcissus.marketplace.domain.auth.AuthState
4 | import com.narcissus.marketplace.domain.auth.SignInResult
5 | import com.narcissus.marketplace.domain.auth.SignOutResult
6 | import com.narcissus.marketplace.domain.auth.SignUpResult
7 | import com.narcissus.marketplace.domain.model.ProductPreview
8 | import com.narcissus.marketplace.domain.model.User
9 | import kotlinx.coroutines.flow.Flow
10 |
11 | interface UserRepository {
12 | suspend fun addCard(cardNumber: Long, svv: Int, expirationDate: String)
13 |
14 | suspend fun getUserData(): User
15 | suspend fun isUserAuthenticated(): Boolean
16 | suspend fun signInWithEmail(email: String, password: String): SignInResult
17 | suspend fun signUpWithEmail(fullName: String, email: String, password: String): SignUpResult
18 | suspend fun signOut(): SignOutResult
19 | suspend fun signInWithGoogle(idToken: String): SignInResult
20 |
21 | val authStateFlow: Flow
22 |
23 | val recentlyVisitedProducts: Flow>
24 | suspend fun writeToVisitedProducts(productPreview: ProductPreview)
25 | }
26 |
--------------------------------------------------------------------------------
/ui/cart/src/main/res/drawable/ic_mastercard.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
7 |
9 |
11 |
13 |
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/repository/ProductsPreviewRepository.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.repository
2 |
3 | import com.narcissus.marketplace.domain.model.ProductPreview
4 | import com.narcissus.marketplace.domain.util.ActionResult
5 | import com.narcissus.marketplace.domain.util.SearchParams
6 |
7 | interface ProductsPreviewRepository {
8 | suspend fun searchProducts(
9 | query: String,
10 | filters: Set
11 | ): ActionResult>
12 |
13 | suspend fun getProducts(): List
14 | suspend fun getProductsRandom(): List
15 | suspend fun getProductsTopRated(): List
16 | suspend fun getProductsTopSales(): List
17 | suspend fun getProductsPeopleAreBuying(): List
18 | suspend fun getProductsByDepartment(departmentId: String): ActionResult>
19 | suspend fun getProductsByDepartmentIdTopRated(departmentId: String): ActionResult>
20 | suspend fun getProductsByDepartmentIdTopSales(departmentId: String): ActionResult>
21 | suspend fun getSimilarProducts(productId: String): List
22 | }
23 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/AddCard.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.repository.UserRepository
4 |
5 | class AddCard(
6 | private val userRepository: UserRepository,
7 | ) {
8 | suspend operator fun invoke(
9 | cardNumber: Long,
10 | svv: Int,
11 | expirationDate: String
12 | ): Boolean {
13 | return if (checkIfCardNumberIsValid(cardNumber)) {
14 | userRepository.addCard(cardNumber, svv, expirationDate)
15 | true
16 | } else false
17 | }
18 |
19 | private fun checkIfCardNumberIsValid(cardNumber: Long): Boolean {
20 | val cardNumberStr = cardNumber.toString()
21 | var sum: Int = Character.getNumericValue(cardNumberStr[cardNumberStr.length - 1])
22 | val parity: Int = cardNumberStr.length % 2
23 | for (i in cardNumberStr.length - 2 downTo 0) {
24 | var summand: Int = Character.getNumericValue(cardNumberStr[i])
25 | if (i % 2 == parity) {
26 | val product = summand * 2
27 | summand = if (product > 9) product - 9 else product
28 | }
29 | sum += summand
30 | }
31 | return (sum % 10 == 0)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/GetRecentlyVisitedProductsTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.ProductPreview
4 | import com.narcissus.marketplace.domain.repository.UserRepository
5 | import com.narcissus.marketplace.domain.usecase.GetRecentlyVisitedProducts
6 | import io.mockk.coEvery
7 | import io.mockk.coVerify
8 | import io.mockk.mockk
9 | import kotlinx.coroutines.flow.flowOf
10 | import kotlinx.coroutines.runBlocking
11 | import org.junit.Assert
12 | import org.junit.Test
13 |
14 | class GetRecentlyVisitedProductsTest {
15 | private val expectedRecentlyVisitedProductsPreviews = flowOf(mockk>())
16 | private val userRepository = mockk {
17 | coEvery { recentlyVisitedProducts } returns expectedRecentlyVisitedProductsPreviews
18 | }
19 | private val getRecentlyVisitedProducts = GetRecentlyVisitedProducts(userRepository)
20 |
21 | @Test
22 | fun `should return actual recently visited products`() {
23 | runBlocking {
24 | Assert.assertEquals(
25 | expectedRecentlyVisitedProductsPreviews,
26 | getRecentlyVisitedProducts(),
27 | )
28 | }
29 | coVerify(exactly = 1) { userRepository.recentlyVisitedProducts }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/apiclient/src/main/java/com/narcissus/marketplace/apiclient/api/model/SerializedNames.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.apiclient.api.model
2 |
3 | internal object SerializedNames {
4 | const val icon = "product_image_lg"
5 | const val id = "_id"
6 | const val productName = "product_name"
7 | const val price = "product_price"
8 | const val type = "product_type"
9 | const val productDepartment = "product_department"
10 | const val stock = "product_stock"
11 | const val color = "product_color"
12 | const val material = "product_material"
13 | const val productRating = "product_ratings"
14 | const val sales = "product_sales"
15 | const val data = "data"
16 | const val productDescription = "product_description"
17 | const val productReviews = "product_reviews"
18 | const val reviewProductId = "review_productid"
19 | const val reviewAuthor = "review_name"
20 | const val reviewDetails = "review_details"
21 | const val reviewRating = "review_rating"
22 | const val reviewAvatar = "review_avatar"
23 | const val similarProducts = "product_similar"
24 | const val departmentId = "department_id"
25 | const val departmentName = "department_name"
26 | const val departmentNumProducts = "department_numProducts"
27 | const val departmentImageUrl = "department_imageUrl"
28 | }
29 |
--------------------------------------------------------------------------------
/firebase/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 32
8 |
9 | defaultConfig {
10 | minSdk 24
11 | targetSdk 32
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | kotlinOptions {
28 | jvmTarget = '1.8'
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation(Koin.koinCore)
34 | implementation(Koin.koinAndroid)
35 |
36 | implementation(Firebase.database)
37 | implementation(Firebase.auth)
38 | implementation platform(Firebase.firebaseBom)
39 |
40 |
41 | implementation(Androidx.core)
42 | implementation(Androidx.appCompat)
43 | implementation(Google.material)
44 | testImplementation(Junit.junit)
45 | androidTestImplementation(Androidx.junit)
46 | androidTestImplementation(Androidx.espressoCore)
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
19 |
20 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/data/src/main/java/com/narcissus/marketplace/data/mapper/OrderMapper.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.mapper
2 |
3 | import com.narcissus.marketplace.data.OrderRepositoryImpl
4 | import com.narcissus.marketplace.data.model.OrderBean
5 | import com.narcissus.marketplace.data.model.OrderItemBean
6 | import com.narcissus.marketplace.domain.model.orders.Order
7 | import com.narcissus.marketplace.domain.model.orders.OrderItem
8 | import com.narcissus.marketplace.domain.model.orders.OrderPaymentStatus
9 |
10 | fun OrderBean.toOrder() = runCatching {
11 | Order(
12 | id!!,
13 | number!!,
14 | date!!,
15 | status!!,
16 | summaryPrice!!,
17 | items!!.map { it.toOrderItem().getOrThrow() },
18 | )
19 | }.getOrNull()
20 |
21 | fun OrderItemBean.toOrderItem() =
22 | runCatching {
23 | OrderItem(
24 | productId!!,
25 | productImage!!,
26 | productPrice!!,
27 | productName!!,
28 | amount!!,
29 | amountPrice!!,
30 | )
31 | }
32 |
33 | fun orderPaymentResponseStatusToOrderPaymentStatus(paymentStatusResponse: String): OrderPaymentStatus {
34 | return when (paymentStatusResponse) {
35 | OrderRepositoryImpl.PAYMENT_STATUS_PAID -> OrderPaymentStatus.PAID
36 | else -> OrderPaymentStatus.CANCELLED
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/usecase/MakeAnOrder.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.usecase
2 |
3 | import com.narcissus.marketplace.domain.model.CartItem
4 | import com.narcissus.marketplace.domain.model.orders.OrderPaymentResult
5 | import com.narcissus.marketplace.domain.model.orders.OrderPaymentStatus
6 | import com.narcissus.marketplace.domain.model.orders.OrderStatus
7 | import com.narcissus.marketplace.domain.model.orders.toOrder
8 | import com.narcissus.marketplace.domain.repository.CartRepository
9 | import com.narcissus.marketplace.domain.repository.OrderRepository
10 | import java.util.Calendar
11 |
12 | class MakeAnOrder(
13 | private val orderRepository: OrderRepository,
14 | private val cartRepository: CartRepository,
15 | ) {
16 | suspend operator fun invoke(orderList: List, orderUUID: String): OrderPaymentResult {
17 | cartRepository.deleteSelectedItems()
18 | val paymentResult = orderRepository.payForTheOrder(orderList, orderUUID)
19 | if (paymentResult.status == OrderPaymentStatus.PAID) {
20 | orderRepository.saveOrder(
21 | orderList.toOrder(
22 | orderUUID,
23 | paymentResult.number!!,
24 | Calendar.getInstance().time, OrderStatus.ACCEPTED,
25 | ),
26 | )
27 | }
28 | return paymentResult
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
24 |
25 |
--------------------------------------------------------------------------------
/persistence/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | id 'kotlin-kapt'
5 | }
6 |
7 | android {
8 | compileSdk 32
9 |
10 | defaultConfig {
11 | minSdk 24
12 | targetSdk 32
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | consumerProguardFiles "consumer-rules.pro"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | kotlinOptions {
29 | jvmTarget = '1.8'
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation project(Modules.domain)
35 |
36 | implementation(Room.room)
37 | implementation(Room.roomRuntime)
38 | kapt(Room.roomCompiler)
39 |
40 | implementation(Koin.koinCore)
41 | implementation(Koin.koinAndroid)
42 | testImplementation(Koin.koinTest)
43 |
44 | implementation(Androidx.core)
45 | implementation(Androidx.appCompat)
46 | implementation(Google.material)
47 | testImplementation(Junit.junit)
48 | androidTestImplementation(Androidx.junit)
49 | androidTestImplementation(Androidx.espressoCore)
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/narcissus/marketplace/ui/MarketplaceApp.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui
2 |
3 | import android.app.Application
4 | import com.narcissus.marketplace.data.di.dataModule
5 | import com.narcissus.marketplace.di.appModule
6 | import com.narcissus.marketplace.di.domainModule
7 | import com.narcissus.marketplace.ui.cart.di.cartModule
8 | import com.narcissus.marketplace.ui.catalog.di.catalogModule
9 | import com.narcissus.marketplace.ui.home.di.homeModule
10 | import com.narcissus.marketplace.ui.product_details.di.productDetailsModule
11 | import com.narcissus.marketplace.ui.sign_in.di.signInModule
12 | import com.narcissus.marketplace.ui.user.di.userModule
13 | import org.koin.android.ext.koin.androidContext
14 | import org.koin.androidx.workmanager.koin.workManagerFactory
15 | import org.koin.core.context.startKoin
16 |
17 | class MarketplaceApp : Application() {
18 | override fun onCreate() {
19 | super.onCreate()
20 | startKoin {
21 | androidContext(this@MarketplaceApp)
22 | workManagerFactory()
23 | modules(
24 | appModule,
25 | dataModule,
26 | domainModule,
27 | homeModule,
28 | catalogModule,
29 | cartModule,
30 | userModule,
31 | signInModule,
32 | productDetailsModule,
33 | )
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/narcissus/marketplace/AddToCartTest.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace
2 |
3 | import com.narcissus.marketplace.domain.model.ProductDetails
4 | import com.narcissus.marketplace.domain.model.toCartItem
5 | import com.narcissus.marketplace.domain.model.toProductPreview
6 | import com.narcissus.marketplace.domain.repository.CartRepository
7 | import com.narcissus.marketplace.domain.usecase.AddToCart
8 | import io.mockk.coEvery
9 | import io.mockk.coVerifySequence
10 | import io.mockk.mockk
11 | import kotlinx.coroutines.runBlocking
12 | import org.junit.Test
13 |
14 | class AddToCartTest {
15 |
16 | private val cartRepository = mockk {
17 | coEvery { addToCart(any()) } returns Unit
18 | }
19 | val addToCart = AddToCart(cartRepository)
20 | private val productDetails = mockk(relaxed = true)
21 | private val cartItemExpected = productDetails.toProductPreview().toCartItem()
22 | @Test
23 | fun `should add product preview to cart`() {
24 | runBlocking { addToCart(productDetails.toProductPreview()) }
25 | coVerifySequence {
26 | cartRepository.addToCart(cartItemExpected)
27 | }
28 | }
29 |
30 | @Test
31 | fun `should add product details to cart`() {
32 |
33 | runBlocking { addToCart(productDetails) }
34 | coVerifySequence {
35 | cartRepository.addToCart(cartItemExpected)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_title_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
30 |
31 |
--------------------------------------------------------------------------------
/data/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 32
8 |
9 | defaultConfig {
10 | minSdk 24
11 | targetSdk 32
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | kotlinOptions {
28 | jvmTarget = '1.8'
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation(Koin.koinCore)
34 | testImplementation(Koin.koinTest)
35 |
36 | implementation platform(Firebase.firebaseBom)
37 | implementation(Firebase.auth)
38 | implementation(Firebase.database)
39 | implementation(Google.playServicesAuth)
40 |
41 | implementation project(Modules.domain)
42 | implementation project(Modules.apiClient)
43 | implementation project(Modules.persistence)
44 | implementation project(Modules.firebase)
45 |
46 | implementation(Coroutines.coroutinesCore)
47 | implementation(Coroutines.coroutinesPlayServices)
48 |
49 | testImplementation(Junit.junit)
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 |
25 |
26 |
--------------------------------------------------------------------------------
/firebase/src/main/java/com/narcissus/marketplace/data/firebase/di/FirebaseModule.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.data.firebase.di
2 |
3 | import android.util.Log
4 | import com.google.firebase.auth.FirebaseAuth
5 | import com.google.firebase.auth.ktx.auth
6 | import com.google.firebase.database.FirebaseDatabase
7 | import com.google.firebase.database.ktx.database
8 | import com.google.firebase.ktx.Firebase
9 | import com.narcissus.marketplace.data.firebase.Constants
10 | import org.koin.core.qualifier.qualifier
11 | import org.koin.dsl.module
12 |
13 | val firebaseModule = module {
14 | single {
15 | Firebase.database(Constants.DATABASE_URL)
16 | }
17 |
18 | single {
19 | Firebase.auth
20 | }
21 |
22 | factory(
23 | qualifier()
24 | ) {
25 | get().currentUser?.uid ?: "anonymous"
26 | }
27 |
28 | single(
29 | qualifier()
30 | ) {
31 | val userId = get(qualifier())
32 | Log.d("marketplace-debug", "user id: $userId")
33 | get().getReference("${Constants.CHILD_CART}/$userId")
34 | }
35 | single(
36 | qualifier()
37 | ) {
38 | val userId = get(qualifier())
39 | Log.d("marketplace-debug", "user id: $userId")
40 | get().getReference("${Constants.CHILD_ORDERS}/$userId")
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/orders/OrdersViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.orders
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.narcissus.marketplace.domain.model.orders.Order
6 | import com.narcissus.marketplace.domain.usecase.GetOrderList
7 | import kotlinx.coroutines.flow.launchIn
8 | import kotlinx.coroutines.flow.onEach
9 | import org.orbitmvi.orbit.Container
10 | import org.orbitmvi.orbit.ContainerHost
11 | import org.orbitmvi.orbit.syntax.simple.intent
12 | import org.orbitmvi.orbit.syntax.simple.postSideEffect
13 | import org.orbitmvi.orbit.syntax.simple.reduce
14 | import org.orbitmvi.orbit.viewmodel.container
15 |
16 | class OrdersViewModel(
17 | getOrderList: GetOrderList,
18 | ) : ViewModel(), ContainerHost {
19 |
20 | override val container: Container =
21 | container(OrdersState(isLoading = true))
22 |
23 | init {
24 | getOrderList()
25 | .onEach(::updateScreenState)
26 | .launchIn(viewModelScope)
27 | }
28 |
29 | private fun updateScreenState(orders: List) = intent {
30 | reduce {
31 | OrdersState(
32 | isLoading = false,
33 | orders = orders,
34 | )
35 | }
36 | }
37 |
38 | fun navigateUp() = intent {
39 | postSideEffect(OrdersSideEffect.NavigateUp)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ui/product_details/src/main/res/layout/list_item_details_product_about_multiple_lines.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
20 |
21 |
31 |
32 |
--------------------------------------------------------------------------------
/ui/sign_in/src/main/java/com/narcissus/marketplace/ui/sign_in/SignInViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.sign_in
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.narcissus.marketplace.domain.auth.SignInResult
6 | import com.narcissus.marketplace.domain.usecase.SignInWithEmail
7 | import com.narcissus.marketplace.domain.usecase.SignInWithGoogle
8 | import kotlinx.coroutines.channels.BufferOverflow
9 | import kotlinx.coroutines.flow.MutableSharedFlow
10 | import kotlinx.coroutines.flow.asSharedFlow
11 | import kotlinx.coroutines.launch
12 |
13 | class SignInViewModel(
14 | val signInWithEmail: SignInWithEmail,
15 | val signInWithGoogle: SignInWithGoogle,
16 | ) : ViewModel() {
17 |
18 | private val _signInResultFlow =
19 | MutableSharedFlow(
20 | replay = 1,
21 | extraBufferCapacity = 1,
22 | onBufferOverflow = BufferOverflow.DROP_OLDEST,
23 | )
24 |
25 | val signInResultFlow = _signInResultFlow.asSharedFlow()
26 |
27 | fun signInWithEmailPassword(email: String, password: String) {
28 | viewModelScope.launch {
29 | val signInResult = signInWithEmail(email, password)
30 | _signInResultFlow.emit(signInResult)
31 | }
32 | }
33 |
34 | fun signInWithGoogleAccount(idToken: String) {
35 | viewModelScope.launch {
36 | val signInResult = signInWithGoogle(idToken)
37 | _signInResultFlow.emit(signInResult)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/narcissus/marketplace/domain/auth/PasswordRequirement.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.domain.auth
2 |
3 | sealed interface PasswordRequirement {
4 | fun validate(password: String): Boolean
5 |
6 | object NotBlank : PasswordRequirement {
7 | override fun validate(password: String): Boolean =
8 | password.isNotBlank()
9 | }
10 |
11 | object NotTooShort : PasswordRequirement {
12 | private const val MIN_LENGTH = 8
13 |
14 | override fun validate(password: String): Boolean =
15 | password.length > MIN_LENGTH
16 | }
17 |
18 | object HasNumber : PasswordRequirement {
19 | override fun validate(password: String): Boolean =
20 | password.any(Char::isDigit)
21 | }
22 |
23 | object HasUppercaseLetter : PasswordRequirement {
24 | override fun validate(password: String): Boolean =
25 | password.any(Char::isUpperCase)
26 | }
27 |
28 | object HasLowercaseLetter : PasswordRequirement {
29 | override fun validate(password: String): Boolean =
30 | password.any(Char::isLowerCase)
31 | }
32 |
33 | companion object {
34 | fun findFailedRequirements(password: String): List {
35 | val requirements = listOf(NotBlank, NotTooShort, HasNumber, HasUppercaseLetter, HasLowercaseLetter)
36 | return requirements
37 | .filterNot { req ->
38 | req.validate(password)
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ui/catalog/src/main/res/layout/fragment_catalog.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/ui/search/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 32
8 |
9 | defaultConfig {
10 | minSdk 24
11 | targetSdk 32
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildFeatures {
18 | viewBinding true
19 | }
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = '1.8'
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation project(Modules.data)
37 | implementation project(Modules.domain)
38 | implementation project(Modules.core)
39 |
40 | implementation(Koin.koinCore)
41 | implementation(Koin.koinAndroid)
42 | testImplementation(Koin.koinTest)
43 |
44 | implementation(Navigation.navigationFragment)
45 | implementation(Navigation.navigationUi)
46 |
47 | implementation(Androidx.core)
48 | implementation(Androidx.appCompat)
49 | implementation(Google.material)
50 | testImplementation(Junit.junit)
51 | androidTestImplementation(Androidx.junit)
52 | androidTestImplementation(Androidx.espressoCore)
53 | }
54 |
--------------------------------------------------------------------------------
/ui/user/src/main/java/com/narcissus/marketplace/ui/user/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.narcissus.marketplace.ui.user.theme
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.text.TextStyle
6 | import androidx.compose.ui.unit.sp
7 |
8 | val Typography = Typography(
9 | h4 = TextStyle(
10 | fontFamily = MontserratSemiBold,
11 | fontSize = 34.sp,
12 | ),
13 | h5 = TextStyle(
14 | fontFamily = MontserratMedium,
15 | fontSize = 24.sp,
16 | ),
17 | h6 = TextStyle(
18 | fontFamily = MontserratSemiBold,
19 | fontSize = 21.sp,
20 | ),
21 | subtitle1 = TextStyle(
22 | fontFamily = MontserratMedium,
23 | fontSize = 17.sp,
24 | ),
25 | subtitle2 = TextStyle(
26 | fontFamily = MontserratMedium,
27 | fontSize = 15.sp,
28 | ),
29 | body1 = TextStyle(
30 | fontFamily = MontserratSemiBold,
31 | fontSize = 16.sp,
32 | ),
33 | body2 = TextStyle(
34 | fontFamily = Montserrat,
35 | fontSize = 14.sp,
36 | ),
37 | button = TextStyle(
38 | fontFamily = MontserratBold,
39 | fontSize = 14.sp,
40 | ),
41 | caption = TextStyle(
42 | fontFamily = Montserrat,
43 | fontSize = 12.sp,
44 | ),
45 | overline = TextStyle(
46 | fontFamily = Montserrat,
47 | fontSize = 10.sp,
48 | ),
49 | )
50 |
51 | val TextStyle.regular: TextStyle
52 | @Composable get() = copy(fontFamily = Montserrat)
53 |
--------------------------------------------------------------------------------
/.github/workflows/android_deploy_to_firebase.yml:
--------------------------------------------------------------------------------
1 | name: Build And Deploy App To Firebase Distribution
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: set up JDK 11
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 11
16 |
17 | - name: Make gradlew executable
18 | run: chmod +x ./gradlew
19 |
20 | - name: Create `google-services.json` file
21 | run: cat /home/runner/work/marketplace-app/marketplace-app/app/google-services.json | base64
22 |
23 | - name: Fill `google-services.json` with content
24 | env:
25 | DATA: ${{ secrets.GOOGLE_SERVICES_JSON }}
26 | run: echo $DATA > /home/runner/work/marketplace-app/marketplace-app/app/google-services.json
27 |
28 | - name: Add api key to local.properties
29 | env:
30 | PROPS_FILE: /home/runner/work/marketplace-app/marketplace-app/local.properties
31 | API_KEY: ${{ secrets.API_KEY }}
32 | run: echo DUMMYPRODUCTSAPIKEY=$API_KEY > $PROPS_FILE
33 |
34 | - name: build debug
35 | run: ./gradlew assembleDebug
36 |
37 | - name: Upload artifact to Firebase App Distribution
38 | uses: wzieba/Firebase-Distribution-Github-Action@v1
39 | with:
40 | appId: ${{ secrets.FIREBASE_APP_ID }}
41 | token: ${{ secrets.FIREBASE_TOKEN }}
42 | groups: test-team
43 | file: app/build/intermediates/apk/debug/app-debug.apk
44 |
--------------------------------------------------------------------------------