From c782f76dba3d8fb56ee64ffb4f7f4cb394556b6b Mon Sep 17 00:00:00 2001 From: Wahid <1213089@dtu.edu.eg> Date: Wed, 1 Jul 2026 16:17:36 +0300 Subject: [PATCH] Revert "Feature/add search screen" --- .../local/preferenceses/Datastore.android.kt | 1 - .../AuthenticationRepositoryImpl.android.kt | 2 +- .../kotlin/com/troves/data/di/DataModule.kt | 8 +- .../AppPreferencesDataSourceImpl.kt | 5 +- .../preferenceses/AppPreferencesDatasource.kt | 2 +- .../local/preferenceses/AppPreferencesKeys.kt | 2 +- .../local/preferenceses/Datastore.kt | 2 +- .../AuthenticationRepositoryImpl.kt | 6 +- .../data/repository/TrovesRepositoryImpl.kt | 35 +-- .../data/repository/WishlistRepositoryImpl.kt | 2 +- .../data/source/remote/RemoteDatasource.kt | 8 +- .../source/remote/RemoteDatasourceImpl.kt | 14 +- .../source/remote/service/HttpClientExt.kt | 2 +- .../source/remote/service/TrovesApiService.kt | 19 +- .../remote/service/TrovesApiServiceImpl.kt | 33 +-- .../data/local/preferenceses/Datastore.ios.kt | 1 - .../AuthenticationRepositoryImpl.ios.kt | 2 +- .../com/troves/domain/{utils => }/Result.kt | 25 +-- .../com/troves/domain/di/DomainModule.kt | 5 - .../domain/entity/ProductSearchParams.kt | 34 --- .../repository/AuthenticationRepository.kt | 4 +- .../domain/repository/TrovesRepository.kt | 5 +- .../domain/usecase/auth/IsLoggedInUseCase.kt | 2 +- .../domain/usecase/auth/LoginUseCase.kt | 4 +- .../domain/usecase/auth/LogoutUseCase.kt | 2 +- .../domain/usecase/auth/RegisterUseCase.kt | 4 +- .../usecase/auth/SignInWithGoogleUseCase.kt | 4 +- .../domain/usecase/home/GetAdsUseCase.kt | 2 +- .../domain/usecase/home/GetBrandsUseCase.kt | 2 +- .../usecase/home/GetCategoriesUseCase.kt | 2 +- .../home/GetJustForYouProductsUseCase.kt | 4 +- .../home/GetTrendingProductsUseCase.kt | 4 +- .../onboarding/CompleteOnboardingUseCase.kt | 2 +- .../onboarding/IsOnboardingDoneUseCase.kt | 2 +- .../products/GetProductsByBrandUseCase.kt | 2 +- .../products/GetProductsByCategoryUseCase.kt | 2 +- .../search/FilterProductsByQueryUseCase.kt | 30 --- .../usecase/search/SearchProductsUseCase.kt | 34 --- .../usecase/shared/GetProductsUseCase.kt | 2 +- .../usecase/wishlist/SyncWishlistUseCase.kt | 2 +- .../usecase/wishlist/ToggleFavoriteUseCase.kt | 2 +- gradle/libs.versions.toml | 1 - presintation/build.gradle.kts | 2 +- .../presintation/di/PresentationModule.kt | 2 - .../troves/presintation/navigation/AppNav.kt | 22 -- .../presintation/navigation/AppRoute.kt | 2 - .../ui/allbrands/AllBrandsViewModel.kt | 2 +- .../presintation/ui/auth/AuthViewModel.kt | 2 +- .../ui/components/FilterBottomSheet.kt | 22 +- .../presintation/ui/home/HomeContract.kt | 1 - .../troves/presintation/ui/home/HomeScreen.kt | 2 - .../presintation/ui/home/HomeViewModel.kt | 9 +- .../productDetails/ProductDetailsViewModel.kt | 2 +- .../ui/products/ProductsScreen.kt | 2 - .../ui/products/ProductsViewModel.kt | 4 +- .../presintation/ui/search/SearchScreen.kt | 196 ----------------- .../ui/search/SearchScreenContract.kt | 80 ------- .../ui/search/SearchScreenViewModel.kt | 204 ----------------- .../ui/search/component/BrandRow.kt | 205 ------------------ .../ui/search/component/BrandRowSkeleton.kt | 109 ---------- .../ui/search/component/EmptySearchResult.kt | 82 ------- .../ui/search/component/ErrorView.kt | 101 --------- .../ui/search/component/ProductCard.kt | 136 ------------ .../search/component/ProductCardSkeleton.kt | 79 ------- .../ui/search/component/ProductGrid.kt | 66 ------ .../ui/search/component/ProductLoading.kt | 39 ---- .../search/component/RecentSearchSection.kt | 151 ------------- .../search/component/RecentSearchSkeleton.kt | 79 ------- .../ui/search/component/SearchBarSection.kt | 160 -------------- .../ui/search/component/SearchBarSkeleton.kt | 49 ----- .../ui/search/component/SearchContent.kt | 145 ------------- .../search/component/SearchLoadingContent.kt | 47 ---- 72 files changed, 78 insertions(+), 2250 deletions(-) rename data/src/commonMain/kotlin/com/troves/data/{source => }/local/preferenceses/AppPreferencesDataSourceImpl.kt (94%) rename data/src/commonMain/kotlin/com/troves/data/{source => }/local/preferenceses/AppPreferencesDatasource.kt (92%) rename data/src/commonMain/kotlin/com/troves/data/{source => }/local/preferenceses/AppPreferencesKeys.kt (93%) rename data/src/commonMain/kotlin/com/troves/data/{source => }/local/preferenceses/Datastore.kt (59%) rename domain/src/commonMain/kotlin/com/troves/domain/{utils => }/Result.kt (61%) delete mode 100644 domain/src/commonMain/kotlin/com/troves/domain/entity/ProductSearchParams.kt delete mode 100644 domain/src/commonMain/kotlin/com/troves/domain/usecase/search/FilterProductsByQueryUseCase.kt delete mode 100644 domain/src/commonMain/kotlin/com/troves/domain/usecase/search/SearchProductsUseCase.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreen.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenContract.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenViewModel.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRow.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRowSkeleton.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/EmptySearchResult.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ErrorView.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCard.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCardSkeleton.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductGrid.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductLoading.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSection.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSkeleton.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSection.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSkeleton.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchContent.kt delete mode 100644 presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchLoadingContent.kt diff --git a/data/src/androidMain/kotlin/com/troves/data/local/preferenceses/Datastore.android.kt b/data/src/androidMain/kotlin/com/troves/data/local/preferenceses/Datastore.android.kt index 1c574ac0..1f0f2283 100644 --- a/data/src/androidMain/kotlin/com/troves/data/local/preferenceses/Datastore.android.kt +++ b/data/src/androidMain/kotlin/com/troves/data/local/preferenceses/Datastore.android.kt @@ -6,7 +6,6 @@ import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.emptyPreferences -import com.troves.data.source.local.preferenceses.DATA_STORE_FILE_NAME import okio.Path.Companion.toPath // Android needs a Context to locate filesDir, so the platform Koin module diff --git a/data/src/androidMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.android.kt b/data/src/androidMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.android.kt index 7b327fd3..c7e7c463 100644 --- a/data/src/androidMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.android.kt +++ b/data/src/androidMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.android.kt @@ -1,6 +1,6 @@ package com.troves.data.repository -import com.troves.data.source.local.preferenceses.AppPreferencesDataSource +import com.troves.data.local.preferenceses.AppPreferencesDataSource actual fun createAuthenticationRepository( preferences: AppPreferencesDataSource diff --git a/data/src/commonMain/kotlin/com/troves/data/di/DataModule.kt b/data/src/commonMain/kotlin/com/troves/data/di/DataModule.kt index 86caafa6..e1c3ece0 100644 --- a/data/src/commonMain/kotlin/com/troves/data/di/DataModule.kt +++ b/data/src/commonMain/kotlin/com/troves/data/di/DataModule.kt @@ -2,22 +2,22 @@ package com.troves.data.di import com.troves.data.local.database.DatabaseFactory import com.troves.data.local.database.TrovesDatabase +import com.troves.data.local.preferenceses.AppPreferencesDataSource +import com.troves.data.local.preferenceses.AppPreferencesDataSourceImpl import com.troves.data.network.provideHttpClient import com.troves.data.repository.CartRepositoryImpl import com.troves.data.repository.PaymentRepositoryImpl import com.troves.data.repository.TrovesRepositoryImpl -import com.troves.data.repository.WishlistRepositoryImpl import com.troves.data.repository.createAuthenticationRepository -import com.troves.data.source.local.preferenceses.AppPreferencesDataSource -import com.troves.data.source.local.preferenceses.AppPreferencesDataSourceImpl import com.troves.data.source.remote.RemoteDatasource import com.troves.data.source.remote.RemoteDatasourceImpl import com.troves.data.source.remote.service.TrovesApiService import com.troves.data.source.remote.service.TrovesApiServiceImpl -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository import com.troves.domain.repository.CartRepository import com.troves.domain.repository.PaymentRepository import com.troves.domain.repository.TrovesRepository +import com.troves.data.repository.WishlistRepositoryImpl import com.troves.domain.repository.WishlistRepository import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.firestore.FirebaseFirestore diff --git a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesDataSourceImpl.kt b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesDataSourceImpl.kt similarity index 94% rename from data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesDataSourceImpl.kt rename to data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesDataSourceImpl.kt index 225a9f7e..4915555c 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesDataSourceImpl.kt +++ b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesDataSourceImpl.kt @@ -1,11 +1,10 @@ -package com.troves.data.source.local.preferenceses +package com.troves.data.local.preferenceses import kotlinx.io.IOException import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.emptyPreferences import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map @@ -81,7 +80,7 @@ class AppPreferencesDataSourceImpl( } private fun Flow.catchIOException() = catch { e -> - if (e is IOException) emit(emptyPreferences()) + if (e is IOException) emit(androidx.datastore.preferences.core.emptyPreferences()) else throw e } } \ No newline at end of file diff --git a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesDatasource.kt b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesDatasource.kt similarity index 92% rename from data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesDatasource.kt rename to data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesDatasource.kt index 30800270..8a60acf3 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesDatasource.kt +++ b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesDatasource.kt @@ -1,4 +1,4 @@ -package com.troves.data.source.local.preferenceses +package com.troves.data.local.preferenceses import kotlinx.coroutines.flow.Flow diff --git a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesKeys.kt b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesKeys.kt similarity index 93% rename from data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesKeys.kt rename to data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesKeys.kt index 6c56c9ba..1e0e003d 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/AppPreferencesKeys.kt +++ b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/AppPreferencesKeys.kt @@ -1,4 +1,4 @@ -package com.troves.data.source.local.preferenceses +package com.troves.data.local.preferenceses import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey diff --git a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/Datastore.kt b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/Datastore.kt similarity index 59% rename from data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/Datastore.kt rename to data/src/commonMain/kotlin/com/troves/data/local/preferenceses/Datastore.kt index fcee2e4a..69e84f9f 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/local/preferenceses/Datastore.kt +++ b/data/src/commonMain/kotlin/com/troves/data/local/preferenceses/Datastore.kt @@ -1,3 +1,3 @@ -package com.troves.data.source.local.preferenceses +package com.troves.data.local.preferenceses internal const val DATA_STORE_FILE_NAME = "app_preferences.preferences_pb" diff --git a/data/src/commonMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.kt b/data/src/commonMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.kt index 65c60123..bb433d59 100644 --- a/data/src/commonMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.kt @@ -1,8 +1,8 @@ package com.troves.data.repository -import com.troves.data.source.local.preferenceses.AppPreferencesDataSource -import com.troves.domain.utils.Result -import com.troves.domain.repository.AuthenticationRepository +import com.troves.data.local.preferenceses.AppPreferencesDataSource +import com.troves.domain.AuthenticationRepository +import com.troves.domain.Result import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.auth.GoogleAuthProvider import dev.gitlive.firebase.auth.auth diff --git a/data/src/commonMain/kotlin/com/troves/data/repository/TrovesRepositoryImpl.kt b/data/src/commonMain/kotlin/com/troves/data/repository/TrovesRepositoryImpl.kt index 4f46dd9c..f2b1474f 100644 --- a/data/src/commonMain/kotlin/com/troves/data/repository/TrovesRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/com/troves/data/repository/TrovesRepositoryImpl.kt @@ -4,21 +4,18 @@ import com.troves.data.mapper.toBrand import com.troves.data.mapper.toCategory import com.troves.data.mapper.toDomain import com.troves.data.source.remote.RemoteDatasource +import com.troves.domain.Result import com.troves.domain.entity.Ad import com.troves.domain.entity.Brand import com.troves.domain.entity.Category import com.troves.domain.entity.Product -import com.troves.domain.entity.ProductSearchParams +import com.troves.domain.fold +import com.troves.domain.map import com.troves.domain.repository.TrovesRepository -import com.troves.domain.utils.Result -import com.troves.domain.utils.fold -import com.troves.domain.utils.getOrElse -import com.troves.domain.utils.map import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.withContext -import kotlinx.io.IOException class TrovesRepositoryImpl( private val remoteDataSource: RemoteDatasource, @@ -29,37 +26,13 @@ class TrovesRepositoryImpl( return withContext(coroutineDispatcher) { remoteDataSource.getAllProducts().map { response -> response.products + ?.filterNotNull() ?.map { it.toDomain() } .orEmpty() } } } - override suspend fun getProductsByQuery(queryMap: Map): Result> { - return withContext(coroutineDispatcher) { - remoteDataSource.getProductsByQuery(queryMap = queryMap).map { response -> - response.products?.map { it.toDomain() }.orEmpty() - } - } - } - - override suspend fun searchProducts(params: ProductSearchParams): Result> { - return try { - withContext(coroutineDispatcher) { - var products = - remoteDataSource.searchProducts(params = params).getOrElse { emptyList() } - if (!params.query.isNullOrBlank()) { - products = products.filter { - it.title.contains(params.query ?: "", ignoreCase = true) - } - } - Result.Success(products) - } - } catch (e: IOException) { - Result.Error(e) - } - } - override suspend fun getProductById(productId: String): Result { return withContext(coroutineDispatcher) { remoteDataSource.getProductById(productId = productId).fold( diff --git a/data/src/commonMain/kotlin/com/troves/data/repository/WishlistRepositoryImpl.kt b/data/src/commonMain/kotlin/com/troves/data/repository/WishlistRepositoryImpl.kt index cedaba31..d14b9bc4 100644 --- a/data/src/commonMain/kotlin/com/troves/data/repository/WishlistRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/com/troves/data/repository/WishlistRepositoryImpl.kt @@ -4,7 +4,7 @@ import com.troves.data.local.database.WishlistDao import com.troves.data.local.database.WishlistEntity import com.troves.data.source.remote.RemoteDatasource import com.troves.data.source.remote.dto.WishlistDto -import com.troves.domain.utils.Result +import com.troves.domain.Result import com.troves.domain.entity.Product import com.troves.domain.repository.WishlistRepository import kotlinx.coroutines.flow.Flow diff --git a/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasource.kt b/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasource.kt index f7e0113d..971502fd 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasource.kt +++ b/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasource.kt @@ -7,19 +7,13 @@ import com.troves.data.source.remote.dto.MarketingEventsResponse import com.troves.data.source.remote.dto.ProductResponse import com.troves.data.source.remote.dto.ProductDto import com.troves.data.source.remote.dto.SingleProductResponse -import com.troves.domain.entity.Product -import com.troves.domain.entity.ProductSearchParams -import com.troves.domain.utils.Result import com.troves.data.source.remote.dto.WishlistDto +import com.troves.domain.Result interface RemoteDatasource { //region product suspend fun createProduct(productDto: ProductDto): Result suspend fun getAllProducts(): Result - suspend fun getProductsByQuery(queryMap: Map): Result - suspend fun searchProducts(params: ProductSearchParams): Result> - - suspend fun getProductImages(productId: String):Result> suspend fun getProductById(productId: String): Result suspend fun updateProduct(productId: String) diff --git a/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasourceImpl.kt b/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasourceImpl.kt index f6d34dbb..8edfc397 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasourceImpl.kt +++ b/data/src/commonMain/kotlin/com/troves/data/source/remote/RemoteDatasourceImpl.kt @@ -4,14 +4,12 @@ import com.troves.data.source.remote.dto.Collection import com.troves.data.source.remote.dto.CollectionImage import com.troves.data.source.remote.dto.CustomCollectionResponse import com.troves.data.source.remote.dto.MarketingEventsResponse -import com.troves.data.source.remote.dto.ProductDto import com.troves.data.source.remote.dto.ProductResponse +import com.troves.data.source.remote.dto.ProductDto import com.troves.data.source.remote.dto.SingleProductResponse import com.troves.data.source.remote.dto.WishlistDto import com.troves.data.source.remote.service.TrovesApiService -import com.troves.domain.entity.Product -import com.troves.domain.entity.ProductSearchParams -import com.troves.domain.utils.Result +import com.troves.domain.Result import dev.gitlive.firebase.firestore.FirebaseFirestore class RemoteDatasourceImpl( @@ -25,14 +23,6 @@ class RemoteDatasourceImpl( return trovesApiService.getAllProducts() } - override suspend fun getProductsByQuery(queryMap: Map): Result { - return trovesApiService.getProductsByQuery(queryMap = queryMap) - } - - override suspend fun searchProducts(params: ProductSearchParams): Result> { - return trovesApiService.searchProducts(params = params) - } - override suspend fun getProductImages(productId: String): Result> { return trovesApiService.getProductImages(productId = productId) } diff --git a/data/src/commonMain/kotlin/com/troves/data/source/remote/service/HttpClientExt.kt b/data/src/commonMain/kotlin/com/troves/data/source/remote/service/HttpClientExt.kt index cdaba967..c43254b9 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/remote/service/HttpClientExt.kt +++ b/data/src/commonMain/kotlin/com/troves/data/source/remote/service/HttpClientExt.kt @@ -6,7 +6,7 @@ import io.ktor.client.request.HttpRequestBuilder import io.ktor.client.request.request import io.ktor.client.statement.bodyAsText import io.ktor.http.HttpStatusCode -import com.troves.domain.utils.Result +import com.troves.domain.Result suspend inline fun HttpClient.getResults( block: HttpRequestBuilder.() -> Unit diff --git a/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiService.kt b/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiService.kt index cfe217f9..e88ef166 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiService.kt +++ b/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiService.kt @@ -1,5 +1,4 @@ package com.troves.data.source.remote.service - import com.troves.data.source.remote.dto.Collection import com.troves.data.source.remote.dto.CollectionImage import com.troves.data.source.remote.dto.CustomCollectionResponse @@ -7,19 +6,13 @@ import com.troves.data.source.remote.dto.MarketingEventsResponse import com.troves.data.source.remote.dto.ProductDto import com.troves.data.source.remote.dto.ProductResponse import com.troves.data.source.remote.dto.SingleProductResponse -import com.troves.domain.entity.Product -import com.troves.domain.entity.ProductSearchParams -import com.troves.domain.utils.Result interface TrovesApiService { //region products - suspend fun createProduct(productDto: ProductDto): Result - suspend fun getAllProducts(): Result - suspend fun getProductsByQuery(queryMap: Map): Result - suspend fun searchProducts(params: ProductSearchParams): Result> - - suspend fun getProductImages(productId: String): Result> - suspend fun getProductById(productId: String): Result + suspend fun createProduct(productDto: ProductDto): com.troves.domain.Result + suspend fun getAllProducts(): com.troves.domain.Result + suspend fun getProductImages(productId: String): com.troves.domain.Result> + suspend fun getProductById(productId: String): com.troves.domain.Result suspend fun updateProduct(productId: String) suspend fun deleteProduct(productDto: ProductDto) // endregion @@ -27,8 +20,8 @@ interface TrovesApiService { //region brands - suspend fun getAllBrands(): Result - suspend fun getCategory(): Result + suspend fun getAllBrands(): com.troves.domain.Result + suspend fun getCategory(): com.troves.domain.Result //endregion diff --git a/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiServiceImpl.kt b/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiServiceImpl.kt index 6a778d70..6f606a33 100644 --- a/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiServiceImpl.kt +++ b/data/src/commonMain/kotlin/com/troves/data/source/remote/service/TrovesApiServiceImpl.kt @@ -1,5 +1,6 @@ package com.troves.data.source.remote.service +import com.troves.data.source.remote.RemoteDatasource import com.troves.data.source.remote.dto.Collection import com.troves.data.source.remote.dto.CollectionImage import com.troves.data.source.remote.dto.CustomCollectionResponse @@ -7,14 +8,10 @@ import com.troves.data.source.remote.dto.MarketingEventsResponse import com.troves.data.source.remote.dto.ProductDto import com.troves.data.source.remote.dto.ProductResponse import com.troves.data.source.remote.dto.SingleProductResponse -import com.troves.domain.entity.Product -import com.troves.domain.entity.ProductSearchParams +import com.troves.domain.Result import io.ktor.client.HttpClient import io.ktor.http.HttpMethod -import io.ktor.http.parameters import io.ktor.http.path -import com.troves.domain.utils.Result -import io.ktor.client.request.parameter class TrovesApiServiceImpl( private val ktorClient: HttpClient @@ -29,32 +26,10 @@ class TrovesApiServiceImpl( url { path("products.json") } } - override suspend fun getProductsByQuery(queryMap: Map): Result = - ktorClient.getResults { - method = HttpMethod.Get - url { - path("products.json") - queryMap.entries.forEach { - parameter(it.key, it.value) - } - } - } - - override suspend fun searchProducts(params: ProductSearchParams): Result> { - return ktorClient.getResults { - method = HttpMethod.Get - params.vendor?.let { parameter("vendor", it) } - params.productType?.let { parameter("product_type", it) } - params.collectionId?.let { parameter("collection_id", it) } - params.status?.let { parameter("status", it.restValue) } - parameter("limit", params.limit) - } - } - override suspend fun getProductImages(productId: String): Result> { return ktorClient.getResults { method = HttpMethod.Get - url { + url{ path("products/$productId/images.json") } } @@ -63,7 +38,7 @@ class TrovesApiServiceImpl( override suspend fun getProductById(productId: String): Result { return ktorClient.getResults { method = HttpMethod.Get - url { + url{ path("products/$productId.json") } } diff --git a/data/src/iosMain/kotlin/com/troves/data/local/preferenceses/Datastore.ios.kt b/data/src/iosMain/kotlin/com/troves/data/local/preferenceses/Datastore.ios.kt index a188b722..8797a447 100644 --- a/data/src/iosMain/kotlin/com/troves/data/local/preferenceses/Datastore.ios.kt +++ b/data/src/iosMain/kotlin/com/troves/data/local/preferenceses/Datastore.ios.kt @@ -5,7 +5,6 @@ import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.emptyPreferences -import com.troves.data.source.local.preferenceses.DATA_STORE_FILE_NAME import okio.Path.Companion.toPath import platform.Foundation.NSHomeDirectory diff --git a/data/src/iosMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.ios.kt b/data/src/iosMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.ios.kt index 7b327fd3..c7e7c463 100644 --- a/data/src/iosMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.ios.kt +++ b/data/src/iosMain/kotlin/com/troves/data/repository/AuthenticationRepositoryImpl.ios.kt @@ -1,6 +1,6 @@ package com.troves.data.repository -import com.troves.data.source.local.preferenceses.AppPreferencesDataSource +import com.troves.data.local.preferenceses.AppPreferencesDataSource actual fun createAuthenticationRepository( preferences: AppPreferencesDataSource diff --git a/domain/src/commonMain/kotlin/com/troves/domain/utils/Result.kt b/domain/src/commonMain/kotlin/com/troves/domain/Result.kt similarity index 61% rename from domain/src/commonMain/kotlin/com/troves/domain/utils/Result.kt rename to domain/src/commonMain/kotlin/com/troves/domain/Result.kt index b6496076..4742530b 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/utils/Result.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/Result.kt @@ -1,25 +1,4 @@ -package com.troves.domain.utils - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ +package com.troves.domain sealed interface Result { data class Success(val value: T) : Result @@ -29,7 +8,7 @@ sealed interface Result { inline fun Result.map(transform: (value:T) -> R): Result = - when (this) { + when (this) { is Result.Loading -> Result.Loading is Result.Success -> Result.Success(transform(value)) is Result.Error -> Result.Error(throwable) diff --git a/domain/src/commonMain/kotlin/com/troves/domain/di/DomainModule.kt b/domain/src/commonMain/kotlin/com/troves/domain/di/DomainModule.kt index 1d1d59d2..8841aaf8 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/di/DomainModule.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/di/DomainModule.kt @@ -21,15 +21,12 @@ import com.troves.domain.usecase.products.FilterProductsUseCase import com.troves.domain.usecase.products.GetProductsByBrandUseCase import com.troves.domain.usecase.products.GetProductsByCategoryUseCase import com.troves.domain.usecase.products.SortProductsUseCase -import com.troves.domain.usecase.search.FilterProductsByQueryUseCase -import com.troves.domain.usecase.search.SearchProductsUseCase import com.troves.domain.usecase.shared.GetProductsUseCase import com.troves.domain.usecase.wishlist.GetWishlistUseCase import com.troves.domain.usecase.wishlist.IsProductFavoritedUseCase import com.troves.domain.usecase.wishlist.SyncWishlistUseCase import com.troves.domain.usecase.wishlist.ToggleFavoriteUseCase import org.koin.dsl.module -import kotlin.coroutines.EmptyCoroutineContext.get val domainModule = module { // Use cases — factory creates a new instance per injection site @@ -48,8 +45,6 @@ val domainModule = module { factory { GetCategoriesUseCase(get()) } factory { GetJustForYouProductsUseCase(get()) } factory { GetTrendingProductsUseCase(get()) } - factory { FilterProductsByQueryUseCase(get()) } - factory { SearchProductsUseCase(get()) } // Onboarding factory { IsOnboardingDoneUseCase(get()) } diff --git a/domain/src/commonMain/kotlin/com/troves/domain/entity/ProductSearchParams.kt b/domain/src/commonMain/kotlin/com/troves/domain/entity/ProductSearchParams.kt deleted file mode 100644 index 2a0f09fe..00000000 --- a/domain/src/commonMain/kotlin/com/troves/domain/entity/ProductSearchParams.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.troves.domain.entity - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 01/07/2026 - */ -data class ProductSearchParams( - val query: String? = null, - val vendor: String? = null, - val productType: String? = null, - val collectionId: String? = null, - val status: ProductStatus? = ProductStatus.ACTIVE, - val limit: Int = 50, -) - -enum class ProductStatus { ACTIVE, DRAFT, ARCHIVED; - val restValue get() = name.lowercase() -} \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/com/troves/domain/repository/AuthenticationRepository.kt b/domain/src/commonMain/kotlin/com/troves/domain/repository/AuthenticationRepository.kt index d192f0a9..86419dc3 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/repository/AuthenticationRepository.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/repository/AuthenticationRepository.kt @@ -1,6 +1,4 @@ -package com.troves.domain.repository -import com.troves.domain.utils.Result - +package com.troves.domain interface AuthenticationRepository { suspend fun login(email: String, password: String): Result diff --git a/domain/src/commonMain/kotlin/com/troves/domain/repository/TrovesRepository.kt b/domain/src/commonMain/kotlin/com/troves/domain/repository/TrovesRepository.kt index 8df5c615..97cc4032 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/repository/TrovesRepository.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/repository/TrovesRepository.kt @@ -1,16 +1,13 @@ package com.troves.domain.repository +import com.troves.domain.Result import com.troves.domain.entity.Ad import com.troves.domain.entity.Brand import com.troves.domain.entity.Category import com.troves.domain.entity.Product -import com.troves.domain.entity.ProductSearchParams -import com.troves.domain.utils.Result interface TrovesRepository { suspend fun getAllProducts(): Result> - suspend fun getProductsByQuery(queryMap: Map): Result> - suspend fun searchProducts(params: ProductSearchParams): Result> suspend fun getProductById(productId: String): Result suspend fun getBrands(): Result> suspend fun getCategories(): Result> diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/IsLoggedInUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/IsLoggedInUseCase.kt index 5c729bbb..aa8f8bf5 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/IsLoggedInUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/IsLoggedInUseCase.kt @@ -1,6 +1,6 @@ package com.troves.domain.usecase.auth -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository class IsLoggedInUseCase( private val authenticationRepository: AuthenticationRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LoginUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LoginUseCase.kt index 1dd0c404..82f576f2 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LoginUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LoginUseCase.kt @@ -1,7 +1,7 @@ package com.troves.domain.usecase.auth -import com.troves.domain.utils.Result -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository +import com.troves.domain.Result class LoginUseCase( private val authenticationRepository: AuthenticationRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LogoutUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LogoutUseCase.kt index d6062afe..35066845 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LogoutUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/LogoutUseCase.kt @@ -1,6 +1,6 @@ package com.troves.domain.usecase.auth -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository import com.troves.domain.repository.WishlistRepository class LogoutUseCase( diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/RegisterUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/RegisterUseCase.kt index 26bb3a7f..0699e3c7 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/RegisterUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/RegisterUseCase.kt @@ -1,7 +1,7 @@ package com.troves.domain.usecase.auth -import com.troves.domain.utils.Result -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository +import com.troves.domain.Result class RegisterUseCase( private val authenticationRepository: AuthenticationRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/SignInWithGoogleUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/SignInWithGoogleUseCase.kt index 776b8881..e4685289 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/SignInWithGoogleUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/auth/SignInWithGoogleUseCase.kt @@ -1,7 +1,7 @@ package com.troves.domain.usecase.auth -import com.troves.domain.repository.AuthenticationRepository -import com.troves.domain.utils.Result +import com.troves.domain.AuthenticationRepository +import com.troves.domain.Result class SignInWithGoogleUseCase(private val repository: AuthenticationRepository) { suspend operator fun invoke(idToken: String, accessToken: String? = null): Result { diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetAdsUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetAdsUseCase.kt index f46d7573..1d04547f 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetAdsUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetAdsUseCase.kt @@ -1,8 +1,8 @@ package com.troves.domain.usecase.home +import com.troves.domain.Result import com.troves.domain.entity.Ad import com.troves.domain.repository.TrovesRepository -import com.troves.domain.utils.Result /** Fetches the promotional banners for the Home ad slider. */ class GetAdsUseCase( diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetBrandsUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetBrandsUseCase.kt index c8a78573..8f33bcb3 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetBrandsUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetBrandsUseCase.kt @@ -1,8 +1,8 @@ package com.troves.domain.usecase.home +import com.troves.domain.Result import com.troves.domain.entity.Brand import com.troves.domain.repository.TrovesRepository -import com.troves.domain.utils.Result /** Fetches the brands shown in the Home "Brands" rail. */ class GetBrandsUseCase( diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetCategoriesUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetCategoriesUseCase.kt index b24141ae..4011d690 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetCategoriesUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetCategoriesUseCase.kt @@ -1,8 +1,8 @@ package com.troves.domain.usecase.home +import com.troves.domain.Result import com.troves.domain.entity.Category import com.troves.domain.repository.TrovesRepository -import com.troves.domain.utils.Result /** Fetches the categories shown in the Home "Categories" rail. */ class GetCategoriesUseCase( diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetJustForYouProductsUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetJustForYouProductsUseCase.kt index 351eaf99..b6136e9e 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetJustForYouProductsUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetJustForYouProductsUseCase.kt @@ -1,9 +1,9 @@ package com.troves.domain.usecase.home +import com.troves.domain.Result import com.troves.domain.entity.Product +import com.troves.domain.map import com.troves.domain.usecase.shared.GetProductsUseCase -import com.troves.domain.utils.Result -import com.troves.domain.utils.map /** * "Just For You" has no dedicated endpoint — it's pure business logic over the diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetTrendingProductsUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetTrendingProductsUseCase.kt index e3524103..ec7e923c 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetTrendingProductsUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/home/GetTrendingProductsUseCase.kt @@ -1,9 +1,9 @@ package com.troves.domain.usecase.home +import com.troves.domain.Result import com.troves.domain.entity.Product +import com.troves.domain.map import com.troves.domain.usecase.shared.GetProductsUseCase -import com.troves.domain.utils.Result -import com.troves.domain.utils.map /** * "Trending Now" has no dedicated endpoint — it's pure business logic over the diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/CompleteOnboardingUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/CompleteOnboardingUseCase.kt index e5bf455f..a3aa276f 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/CompleteOnboardingUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/CompleteOnboardingUseCase.kt @@ -1,6 +1,6 @@ package com.troves.domain.usecase.onboarding -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository class CompleteOnboardingUseCase( private val repository: AuthenticationRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/IsOnboardingDoneUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/IsOnboardingDoneUseCase.kt index 09a8816c..0de37305 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/IsOnboardingDoneUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/onboarding/IsOnboardingDoneUseCase.kt @@ -1,6 +1,6 @@ package com.troves.domain.usecase.onboarding -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository class IsOnboardingDoneUseCase( private val repository: AuthenticationRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByBrandUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByBrandUseCase.kt index 07031d52..6f1801ea 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByBrandUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByBrandUseCase.kt @@ -1,6 +1,6 @@ package com.troves.domain.usecase.products -import com.troves.domain.utils.Result +import com.troves.domain.Result import com.troves.domain.entity.Product import com.troves.domain.repository.TrovesRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByCategoryUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByCategoryUseCase.kt index 2d6f846a..3d094327 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByCategoryUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/products/GetProductsByCategoryUseCase.kt @@ -1,6 +1,6 @@ package com.troves.domain.usecase.products -import com.troves.domain.utils.Result +import com.troves.domain.Result import com.troves.domain.entity.Product import com.troves.domain.repository.TrovesRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/search/FilterProductsByQueryUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/search/FilterProductsByQueryUseCase.kt deleted file mode 100644 index cbca508c..00000000 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/search/FilterProductsByQueryUseCase.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.troves.domain.usecase.search - -import com.troves.domain.repository.TrovesRepository - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -class FilterProductsByQueryUseCase( - private val trovesRepository: TrovesRepository -) { - suspend operator fun invoke(queryMap: Map) = - trovesRepository.getProductsByQuery(queryMap = queryMap) -} \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/search/SearchProductsUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/search/SearchProductsUseCase.kt deleted file mode 100644 index 865b57a5..00000000 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/search/SearchProductsUseCase.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.troves.domain.usecase.search - -import com.troves.domain.entity.Product -import com.troves.domain.entity.ProductSearchParams -import com.troves.domain.repository.TrovesRepository -import com.troves.domain.utils.Result - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 01/07/2026 - */ -class SearchProductsUseCase( - private val trovesRepository: TrovesRepository -){ - suspend operator fun invoke(params: ProductSearchParams): Result> { - return trovesRepository.searchProducts(params) - } -} \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/shared/GetProductsUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/shared/GetProductsUseCase.kt index 87d0da3f..6a293472 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/shared/GetProductsUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/shared/GetProductsUseCase.kt @@ -1,8 +1,8 @@ package com.troves.domain.usecase.shared +import com.troves.domain.Result import com.troves.domain.entity.Product import com.troves.domain.repository.TrovesRepository -import com.troves.domain.utils.Result class GetProductsUseCase( private val repository: TrovesRepository diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/SyncWishlistUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/SyncWishlistUseCase.kt index 67ff83c1..1d4c9df7 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/SyncWishlistUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/SyncWishlistUseCase.kt @@ -1,6 +1,6 @@ package com.troves.domain.usecase.wishlist -import com.troves.domain.repository.AuthenticationRepository +import com.troves.domain.AuthenticationRepository import com.troves.domain.repository.WishlistRepository class SyncWishlistUseCase( diff --git a/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/ToggleFavoriteUseCase.kt b/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/ToggleFavoriteUseCase.kt index 0c4db25b..b6422988 100644 --- a/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/ToggleFavoriteUseCase.kt +++ b/domain/src/commonMain/kotlin/com/troves/domain/usecase/wishlist/ToggleFavoriteUseCase.kt @@ -1,7 +1,7 @@ package com.troves.domain.usecase.wishlist +import com.troves.domain.AuthenticationRepository import com.troves.domain.entity.Product -import com.troves.domain.repository.AuthenticationRepository import com.troves.domain.repository.WishlistRepository import kotlinx.coroutines.flow.first diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb5e04c8..1a43825a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,7 +55,6 @@ koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koinCore koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koinCore" } koin-compose-navigation3 = { module = "io.insert-koin:koin-compose-navigation3", version.ref = "koinCore" } koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinCore" } -icons-lucide-cmp = { module = "com.composables:icons-lucide-cmp", version.ref = "iconsLucideCmp" } # Compose / AndroidX kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } diff --git a/presintation/build.gradle.kts b/presintation/build.gradle.kts index 20ec2f5c..2592f79d 100644 --- a/presintation/build.gradle.kts +++ b/presintation/build.gradle.kts @@ -71,7 +71,7 @@ kotlin { implementation(libs.compose.runtime) implementation(libs.compose.uiToolingPreview) implementation(libs.compose.components.resources) - implementation(libs.icons.lucide.cmp) + // ViewModel implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/di/PresentationModule.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/di/PresentationModule.kt index 67b4e34a..50ded676 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/di/PresentationModule.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/di/PresentationModule.kt @@ -9,7 +9,6 @@ import com.troves.presintation.ui.products.ProductsViewModel import com.troves.presintation.ui.home.HomeViewModel import com.troves.presintation.ui.onboarding.OnboardingViewModel import com.troves.presintation.ui.productDetails.ProductDetailsViewModel -import com.troves.presintation.ui.search.SearchScreenViewModel import com.troves.presintation.ui.profile.ProfileViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.dsl.module @@ -21,7 +20,6 @@ val presentationModule = module { viewModelOf(::HomeViewModel) viewModelOf(::ProductDetailsViewModel) viewModelOf(::ProductsViewModel) - viewModelOf(::SearchScreenViewModel) viewModelOf(::WishlistViewModel) viewModelOf(::AllBrandsViewModel) viewModelOf(::CartViewModel) diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppNav.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppNav.kt index 03813f95..5d58669f 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppNav.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppNav.kt @@ -11,7 +11,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.snapshots.Snapshot import androidx.compose.ui.Modifier -import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation3.runtime.NavEntry import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider @@ -33,8 +32,6 @@ import com.troves.presintation.ui.onboarding.OnboardingScreen import com.troves.presintation.ui.productDetails.ProductDetailsScreen import com.troves.presintation.ui.products.ProductsScreen import com.troves.presintation.ui.profile.ProfileScreen -import com.troves.presintation.ui.search.SearchScreen -import com.troves.presintation.ui.search.SearchScreenViewModel import com.troves.presintation.ui.splash.SplashScreen import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic @@ -60,7 +57,6 @@ private val navSavedStateConfiguration = SavedStateConfiguration { subclass(AppRoute.Profile::class, AppRoute.Profile.serializer()) subclass(AppRoute.ProductDetails::class, AppRoute.ProductDetails.serializer()) subclass(AppRoute.Cart::class, AppRoute.Cart.serializer()) - subclass(AppRoute.Search::class, AppRoute.Search.serializer()) } } } @@ -86,7 +82,6 @@ fun AppNav() { val bottomNavRoutes = remember { listOf( AppRoute.Home, - AppRoute.Profile, AppRoute.Favorites, AppRoute.Cart, AppRoute.Profile @@ -120,7 +115,6 @@ fun AppNav() { backStack.add(AppRoute.ProductDetails(productId)) }, onNavigateToRegister = { backStack.add(AppRoute.Register) }, - onNavigateToSearch = {backStack.add(AppRoute.Search)}, onNavigateToCart = { backStack.add(AppRoute.Cart) }, onNavigateToAllBrands = { backStack.add(AppRoute.AllBrands) }, onNavigateToProducts = { sourceType, sourceId, sourceName -> @@ -207,22 +201,6 @@ fun AppNav() { onNavigateToCheckout = { backStack.removeLastOrNull() }, ) } - entry { - val viewModel: SearchScreenViewModel = koinViewModel() - val state by viewModel.state.collectAsStateWithLifecycle() - val onIntent = viewModel::onIntent - SearchScreen( - state = state, - onIntent = onIntent, - effect = viewModel.effect, - onNavigateToDetails = { - backStack.add(AppRoute.ProductDetails(it)) - }, - onNavigateBack = { - backStack.removeLastOrNull() - } - ) - } } Scaffold( diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppRoute.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppRoute.kt index dc3a5893..d90d9412 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppRoute.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/navigation/AppRoute.kt @@ -31,8 +31,6 @@ sealed interface AppRoute : NavKey { @Serializable data object Favorites : AppRoute - @Serializable - data object Search : AppRoute @Serializable data object Profile : AppRoute diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/allbrands/AllBrandsViewModel.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/allbrands/AllBrandsViewModel.kt index f84193d4..a3ed8a1c 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/allbrands/AllBrandsViewModel.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/allbrands/AllBrandsViewModel.kt @@ -2,7 +2,7 @@ package com.troves.presintation.ui.allbrands import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.troves.domain.utils.Result +import com.troves.domain.Result import com.troves.domain.usecase.home.GetBrandsUseCase import com.troves.presintation.core.mvi.DefaultEffectPublisher import com.troves.presintation.core.mvi.DefaultStateHolder diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/auth/AuthViewModel.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/auth/AuthViewModel.kt index 3fc0a998..1ba34883 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/auth/AuthViewModel.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/auth/AuthViewModel.kt @@ -2,9 +2,9 @@ package com.troves.presintation.ui.auth import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.troves.domain.Result import com.troves.domain.usecase.auth.LoginUseCase import com.troves.domain.usecase.auth.RegisterUseCase -import com.troves.domain.utils.Result import com.troves.domain.usecase.auth.SignInWithGoogleUseCase import com.troves.domain.usecase.wishlist.SyncWishlistUseCase import com.troves.presintation.core.mvi.DefaultEffectPublisher diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/components/FilterBottomSheet.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/components/FilterBottomSheet.kt index bea83c9d..ec76dc89 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/components/FilterBottomSheet.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/components/FilterBottomSheet.kt @@ -12,8 +12,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.SheetState -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight @@ -42,19 +40,18 @@ data class FilterOption( @OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterBottomSheet( - categories: List = emptyList(), - subCategories: List = emptyList(), - brands: List = emptyList(), - selectedCategoryIds: Set = emptySet(), - selectedSubCategoryIds: Set = emptySet(), - selectedBrandIds: Set = emptySet(), - onToggleCategory: (String) -> Unit = {}, - onToggleSubCategory: (String) -> Unit = {}, - onToggleBrand: (String) -> Unit = {}, + categories: List, + subCategories: List, + brands: List, + selectedCategoryIds: Set, + selectedSubCategoryIds: Set, + selectedBrandIds: Set, + onToggleCategory: (String) -> Unit, + onToggleSubCategory: (String) -> Unit, + onToggleBrand: (String) -> Unit, onApply: () -> Unit, onReset: () -> Unit, onDismiss: () -> Unit, - sheetState: SheetState = rememberModalBottomSheetState(), modifier: Modifier = Modifier, ) { BaseBottomSheet( @@ -67,7 +64,6 @@ fun FilterBottomSheet( onApply = onApply, ) }, - sheetState = sheetState ) { Column( modifier = Modifier diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeContract.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeContract.kt index 279487f4..fedde735 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeContract.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeContract.kt @@ -29,7 +29,6 @@ sealed interface HomeEffect { data class ShowToast(val message: String) : HomeEffect data object NavigateToAllBrands : HomeEffect data object NavigateToRegister : HomeEffect - data object NavigateToSearch : HomeEffect data object NavigateToCart : HomeEffect data object ShowLoginRequiredDialog : HomeEffect } diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeScreen.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeScreen.kt index 00854319..77bd93cf 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeScreen.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeScreen.kt @@ -71,7 +71,6 @@ fun HomeScreen( onNavigateToProducts: (sourceType: String, sourceId: String, sourceName: String) -> Unit, onNavigateToAllBrands: () -> Unit, onNavigateToRegister: () -> Unit, - onNavigateToSearch: () -> Unit, onNavigateToCart: () -> Unit, viewModel: HomeViewModel = koinViewModel(), ) { @@ -91,7 +90,6 @@ fun HomeScreen( is HomeEffect.NavigateToRegister -> onNavigateToRegister() is HomeEffect.NavigateToCart -> onNavigateToCart() is HomeEffect.ShowToast -> scope.launch { snackbarHostState.showSnackbar(effect.message) } - is HomeEffect.NavigateToSearch -> onNavigateToSearch() is HomeEffect.ShowLoginRequiredDialog -> scope.launch { snackbarHostState.showSnackbar("Please login to continue") } diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeViewModel.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeViewModel.kt index cbcbb881..d3bff8b4 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeViewModel.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/home/HomeViewModel.kt @@ -2,7 +2,9 @@ package com.troves.presintation.ui.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.troves.domain.Result import com.troves.domain.entity.Product +import com.troves.domain.getOrElse import com.troves.domain.usecase.auth.IsLoggedInUseCase import com.troves.domain.usecase.home.GetAdsUseCase import com.troves.domain.usecase.home.GetBrandsUseCase @@ -12,8 +14,6 @@ import com.troves.domain.usecase.home.GetTrendingProductsUseCase import com.troves.domain.usecase.wishlist.GetWishlistUseCase import com.troves.domain.usecase.wishlist.ToggleFavoriteResult import com.troves.domain.usecase.wishlist.ToggleFavoriteUseCase -import com.troves.domain.utils.Result -import com.troves.domain.utils.getOrElse import com.troves.presintation.core.mvi.DefaultEffectPublisher import com.troves.presintation.core.mvi.DefaultStateHolder import com.troves.presintation.core.mvi.EffectPublisher @@ -21,7 +21,6 @@ import com.troves.presintation.core.mvi.StateHolder import kotlinx.coroutines.async import kotlinx.coroutines.launch - class HomeViewModel( private val getAds: GetAdsUseCase, private val getBrands: GetBrandsUseCase, @@ -43,7 +42,7 @@ class HomeViewModel( fun onIntent(intent: HomeIntent) { when (intent) { HomeIntent.Load, HomeIntent.Retry -> loadHomeFeed() - HomeIntent.SearchClicked -> sendEffect(HomeEffect.NavigateToSearch) + HomeIntent.SearchClicked -> sendEffect(HomeEffect.ShowToast("Search is coming soon")) HomeIntent.CartClicked -> onCartClicked() HomeIntent.SignUpPromptConfirmed -> { updateState { copy(showSignUpPrompt = false) } @@ -104,7 +103,7 @@ class HomeViewModel( categories = categoriesResult.getOrElse(emptyList()), justForYou = justForYouResult.getOrElse(emptyList()), trending = trendingResult.getOrElse(emptyList()), - errorMessage = firstError?.message, + errorMessage = firstError?.message ?: firstError?.let { "Something went wrong" }, ) } } diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/productDetails/ProductDetailsViewModel.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/productDetails/ProductDetailsViewModel.kt index d47a005e..2a59db46 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/productDetails/ProductDetailsViewModel.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/productDetails/ProductDetailsViewModel.kt @@ -2,6 +2,7 @@ package com.troves.presintation.ui.productDetails import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.troves.domain.Result import com.troves.domain.entity.Product import com.troves.domain.usecase.cart.AddToCartUseCase import com.troves.domain.usecase.details.GetProductByIdUseCase @@ -14,7 +15,6 @@ import com.troves.presintation.core.mvi.EffectPublisher import com.troves.presintation.core.mvi.StateHolder import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import com.troves.domain.utils.Result class ProductDetailsViewModel( private val getProductByIdUseCase: GetProductByIdUseCase, diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsScreen.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsScreen.kt index 0b1ab740..69d73369 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsScreen.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.text.BasicText -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable @@ -58,7 +57,6 @@ import troves.presintation.generated.resources.products_empty import troves.presintation.generated.resources.products_retry import troves.presintation.generated.resources.sort_title -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ProductsScreen( sourceType: String = "", diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsViewModel.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsViewModel.kt index cb26122c..cee75e18 100644 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsViewModel.kt +++ b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/products/ProductsViewModel.kt @@ -2,7 +2,8 @@ package com.troves.presintation.ui.products import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.troves.domain.utils.getOrElse +import com.troves.domain.Result +import com.troves.domain.getOrElse import com.troves.domain.usecase.home.GetCategoriesUseCase import com.troves.domain.usecase.products.FilterProductsUseCase import com.troves.domain.usecase.products.GetProductsByBrandUseCase @@ -17,7 +18,6 @@ import com.troves.presintation.ui.components.FilterOption import com.troves.presintation.ui.components.SortOption import kotlinx.coroutines.async import kotlinx.coroutines.launch -import com.troves.domain.utils.Result class ProductsViewModel( private val getProducts: GetProductsUseCase, diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreen.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreen.kt deleted file mode 100644 index 66d00499..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreen.kt +++ /dev/null @@ -1,196 +0,0 @@ -package com.troves.presintation.ui.search - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.troves.designsystem.theme.Theme -import com.troves.presintation.core.mvi.ObserveEffect -import com.troves.presintation.ui.components.FilterBottomSheet -import com.troves.presintation.ui.components.FilterOption -import com.troves.presintation.ui.search.component.BrandRow -import com.troves.presintation.ui.search.component.EmptySearchResult -import com.troves.presintation.ui.search.component.ErrorView -import com.troves.presintation.ui.search.component.ProductGrid -import com.troves.presintation.ui.search.component.ProductLoading -import com.troves.presintation.ui.search.component.RecentSearchSection -import com.troves.presintation.ui.search.component.SearchBarSection -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch -import kotlin.collections.buildList - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SearchScreen( - onNavigateBack: () -> Unit, - onNavigateToDetails: (productId: String) -> Unit, - state: SearchUiState, - effect: Flow, - onIntent: (SearchIntent) -> Unit, - modifier: Modifier = Modifier -) { - - val coroutineScope = rememberCoroutineScope() - val snakBarState = remember { SnackbarHostState() } - LaunchedEffect(Unit) { - onIntent(SearchIntent.Load) - } - - - ObserveEffect(effect) { effect -> - when (effect) { - - SearchEffect.NavigateBack -> { - onNavigateBack() - } - - is SearchEffect.ShowMessage -> { - coroutineScope.launch { - snakBarState.showSnackbar(message = effect.message) - } - } - - SearchEffect.HideKeyboard -> { - - } - - is SearchEffect.NavigateToDetails -> { - onNavigateToDetails(effect.productId) - } - } - } - - Scaffold( - modifier = modifier.background( - color = Theme.colors.backGround - ).fillMaxSize() - .statusBarsPadding(), - topBar = { - SearchBarSection( - query = state.query ?: "", - onQueryChange = { onIntent(SearchIntent.SearchQueryChange(it)) }, - onSearch = { - onIntent(SearchIntent.OnSearch(it)) - }, - onFilterClick = { onIntent(SearchIntent.OnFilterClick) }, - searchSuggestions = buildList { - state.categories.map { it.name }.forEach { add(it) } - state.brands.map { it.name }.forEach { add(it) } - }, - ) - }, - containerColor = Theme.colors.backGround, - ) { - Box( - modifier = modifier.background( - color = Theme.colors.backGround - ).fillMaxSize() - ) { - - Column { - RecentSearchSection( - recentSearches = state.recentSearches.toList(), - onSearchClick = {}, - onRemoveClick = {} - ) - - BrandRow( - brands = state.brands.toList(), - selectedBrand = state.selectedBrand, - modifier = Modifier.fillMaxWidth(), - onBrandSelected = { onIntent(SearchIntent.BrandChange(it.name)) } - ) - when { - state.isLoading -> { - ProductLoading( - modifier = Modifier.background( - color = Theme.colors.backGround - ) - ) - } - - state.hasError -> { - ErrorView( - message = state.errorMessage.orEmpty() - ) - } - - state.isEmpty -> { - EmptySearchResult( - modifier = Modifier - .fillMaxSize() - .statusBarsPadding() - ) - } - - else -> { - ProductGrid( - products = state.products, - onProductClick = { - onIntent(SearchIntent.OnProductClick(it.id.toString())) - } - ) - - } - } - } - if (state.showFilterSheet) { - FilterBottomSheet( - categories = state.categories.map { FilterOption(it.id.toString(), it.name) }, - brands = state.brands.map { FilterOption(it.id.toString(), it.name) }, - selectedCategoryIds = state.sheetFilterOptions.selectedCategories, - selectedBrandIds = state.sheetFilterOptions.selectedBrands, - onApply = { onIntent(SearchIntent.ApplyFilters(state.sheetFilterOptions)) }, - onReset = { onIntent(SearchIntent.ClearFilters) }, - onDismiss = { onIntent(SearchIntent.OnBottomSheetDismiss) }, - onToggleCategory = { onIntent(SearchIntent.CategoriesChange(listOf(it))) }, - onToggleBrand = { onIntent(SearchIntent.BrandsChange(listOf(it))) }, - ) - } - SnackbarHost( - hostState = snakBarState, - modifier = Modifier - .align(Alignment.BottomCenter) - .navigationBarsPadding() - .padding(16.dp), - ) - } - - } -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenContract.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenContract.kt deleted file mode 100644 index 2cd78c62..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenContract.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.troves.presintation.ui.search - -import androidx.compose.runtime.Immutable -import com.troves.domain.entity.Brand -import com.troves.domain.entity.Category -import com.troves.domain.entity.Product -import com.troves.presintation.ui.components.FilterOption - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ - -@Immutable -data class SheetFilterOptions( - val selectedCategories: Set = emptySet(), - val selectedBrands: Set = emptySet(), -) - -@Immutable -data class SearchUiState( - val query: String = "", - val isLoading: Boolean = true, - val allProducts: List = emptyList(), - val products: List = emptyList(), - val categories: List = emptyList(), - val brands: List = emptyList(), - val selectedBrand: String = "", - val sheetFilterOptions: SheetFilterOptions = SheetFilterOptions(), - val recentSearches: Set = emptySet(), - val errorMessage: String? = null, - val showFilterSheet: Boolean = false, -) { - val hasError get() = errorMessage != null - val isEmpty get() = !isLoading && products.isEmpty() && !hasError -} - -@Immutable -data class RecentSearchUi( - val id: String, - val query: String -) - -sealed interface SearchIntent { - data object Load : SearchIntent - data object Retry : SearchIntent - data object OnFilterClick : SearchIntent - data object ClearFilters : SearchIntent - data object OnBottomSheetDismiss : SearchIntent - data class OnProductClick(val productId: String) : SearchIntent - data class ApplyFilters(val sheetFilterOptions: SheetFilterOptions) : SearchIntent - data class SearchQueryChange(val newQuery: String) : SearchIntent - data class OnSearch(val query: String) : SearchIntent - data class CategoriesChange(val newCategory: List) : SearchIntent - data class BrandsChange(val newBrands: List) : SearchIntent - data class BrandChange(val newBrand: String) : SearchIntent -} - -sealed interface SearchEffect { - data object NavigateBack : SearchEffect - data object HideKeyboard : SearchEffect - data class NavigateToDetails(val productId: String) : SearchEffect - data class ShowMessage(val message: String) : SearchEffect -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenViewModel.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenViewModel.kt deleted file mode 100644 index 97236236..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/SearchScreenViewModel.kt +++ /dev/null @@ -1,204 +0,0 @@ -package com.troves.presintation.ui.search - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.troves.domain.entity.ProductSearchParams -import com.troves.domain.usecase.home.GetBrandsUseCase -import com.troves.domain.usecase.home.GetCategoriesUseCase -import com.troves.domain.usecase.products.FilterProductsUseCase -import com.troves.domain.usecase.search.FilterProductsByQueryUseCase -import com.troves.domain.usecase.search.SearchProductsUseCase -import com.troves.domain.usecase.shared.GetProductsUseCase -import com.troves.domain.utils.Result -import com.troves.domain.utils.fold -import com.troves.domain.utils.getOrElse -import com.troves.presintation.core.mvi.DefaultEffectPublisher -import com.troves.presintation.core.mvi.DefaultStateHolder -import com.troves.presintation.core.mvi.EffectPublisher -import com.troves.presintation.core.mvi.StateHolder -import com.troves.presintation.ui.search.SearchEffect.* -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.async -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlin.time.Duration.Companion.milliseconds - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ - -@OptIn(FlowPreview::class) -class SearchScreenViewModel( - private val getCategoriesUseCase: GetCategoriesUseCase, - private val getBrandsUseCase: GetBrandsUseCase, - private val getProductsUseCase: GetProductsUseCase, - private val filterProductsByQueryUseCase: FilterProductsByQueryUseCase, - private val filterProductsUseCase: FilterProductsUseCase, - private val searchProductsUseCase: SearchProductsUseCase, -) : ViewModel(), - StateHolder by DefaultStateHolder(SearchUiState()), - EffectPublisher by DefaultEffectPublisher() { - - private val searchQueryFlow = MutableStateFlow("") - - init { - searchQueryFlow - .debounce(350.milliseconds) - .distinctUntilChanged() - .onEach { q -> runSearch(q) } - .launchIn(viewModelScope) - } - - fun onIntent(intent: SearchIntent) { - when (intent) { - SearchIntent.Load, SearchIntent.Retry -> load() - - SearchIntent.OnFilterClick -> updateState { copy(showFilterSheet = true) } - - is SearchIntent.BrandsChange -> { - updateState { - copy( - sheetFilterOptions = sheetFilterOptions.copy( - selectedBrands = toggleAll( - sheetFilterOptions.selectedBrands, - intent.newBrands - ) - ) - ) - } - } - - is SearchIntent.CategoriesChange -> { - updateState { - copy( - sheetFilterOptions = sheetFilterOptions.copy( - selectedCategories = toggleAll( - sheetFilterOptions.selectedCategories, - intent.newCategory - ) - ) - ) - } - } - - is SearchIntent.SearchQueryChange -> { - updateState { copy(query = intent.newQuery) } - searchQueryFlow.value = intent.newQuery - } - - SearchIntent.ClearFilters -> { - updateState { - copy(sheetFilterOptions = SheetFilterOptions(), products = allProducts) - } - } - - is SearchIntent.OnProductClick -> sendEffect(NavigateToDetails(intent.productId)) - - SearchIntent.OnBottomSheetDismiss -> updateState { copy(showFilterSheet = false) } - - is SearchIntent.OnSearch -> { - viewModelScope.launch { runSearch(query = "", vendor = intent.query) } - } - - is SearchIntent.BrandChange -> { - updateState { copy(selectedBrand = intent.newBrand) } - onIntent(SearchIntent.OnSearch(intent.newBrand)) - } - - is SearchIntent.ApplyFilters -> { - viewModelScope.launch { - updateState { - copy(isLoading = true, sheetFilterOptions = intent.sheetFilterOptions, showFilterSheet = false) - } - val categoryNames = state.value.categories - .filter { it.id.toString() in intent.sheetFilterOptions.selectedCategories } - .map { it.name } - .toSet() - val brandNames = state.value.brands - .filter { it.id.toString() in intent.sheetFilterOptions.selectedBrands } - .map { it.name } - .toSet() - - val filtered = filterProductsUseCase( - state.value.allProducts, - brandIds = brandNames, - categoryNames = categoryNames, - ) - updateState { copy(isLoading = false, products = filtered) } - } - } - } - } - - private fun toggleAll(current: Set, ids: List): Set = - ids.fold(current) { acc, id -> if (id in acc) acc - id else acc + id } - - private suspend fun runSearch(query: String, vendor: String? = null) { - updateState { copy(isLoading = true) } - - val params = ProductSearchParams( - query = query.ifBlank { null }, - vendor = vendor, - productType = state.value.sheetFilterOptions.selectedCategories.firstOrNull(), - ) - - searchProductsUseCase(params = params).fold( - onSuccess = { products -> - updateState { copy(isLoading = false, products = products, errorMessage = null) } - }, - onError = { throwable -> - updateState { copy(isLoading = false, errorMessage = throwable.message) } - }, - onLoading = { /* searchProductsUseCase resolves directly to Success/Error; unreachable here */ }, - ) - } - - private fun load() { - viewModelScope.launch { - updateState { copy(isLoading = true, errorMessage = null) } - val categories = async { getCategoriesUseCase() } - val brands = async { getBrandsUseCase() } - val products = async { getProductsUseCase() } - - val categoryResult = categories.await() - val brandsResult = brands.await() - val productsResult = products.await() - - val hasError = listOf(categoryResult, brandsResult, productsResult) - .firstNotNullOfOrNull { (it as? Result.Error)?.throwable } - - val loadedProducts = productsResult.getOrElse { emptyList() } - - updateState { - copy( - isLoading = false, - allProducts = loadedProducts, - products = loadedProducts, - brands = brandsResult.getOrElse { emptyList() }, - categories = categoryResult.getOrElse { emptyList() }, - errorMessage = hasError?.message, - ) - } - } - } -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRow.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRow.kt deleted file mode 100644 index bac860c2..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRow.kt +++ /dev/null @@ -1,205 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import coil3.compose.AsyncImage -import com.composables.icons.lucide.Check -import com.composables.icons.lucide.Lucide -import com.troves.designsystem.theme.Theme -import com.troves.domain.entity.Brand - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ - -@Composable -fun BrandRow( - brands: List, - selectedBrand: String?, - modifier: Modifier = Modifier, - onBrandSelected: (Brand) -> Unit, -) { - - if (brands.isEmpty()) return - - Column( - modifier = modifier.fillMaxWidth() - ) { - - Text( - text = "Brands", - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding( - Theme.spacing.medium - ), - color = Theme.colors.primaryFont - ) - - LazyRow( - contentPadding = PaddingValues(Theme.spacing.medium), - horizontalArrangement = Arrangement.spacedBy(Theme.spacing.medium) - ) { - - items( - items = brands, - key = Brand::id - ) { brand -> - - BrandItem( - brand = brand, - selected = selectedBrand == brand.name, - onClick = { - onBrandSelected(brand) - } - ) - - } - - } - - } - -} - -@Composable -private fun BrandItem( - brand: Brand, - selected: Boolean, - modifier: Modifier = Modifier, - onClick: () -> Unit -) { - - val scale by animateFloatAsState( - targetValue = if (selected) 1.08f else 1f, - label = "BrandScale" - ) - - Column( - modifier = modifier.width(Theme.size.large), - horizontalAlignment = Alignment.CenterHorizontally - ) { - - ElevatedCard( - onClick = onClick, - modifier = Modifier - .size(Theme.size.large) - .graphicsLayer { - scaleX = scale - scaleY = scale - }, - shape = CircleShape, - colors = CardDefaults.elevatedCardColors( - containerColor = if (selected) - Theme.colors.surfaceVariant - else - Theme.colors.surface - ), - ) { - - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - - AsyncImage( - model = brand.logoUrl, - contentDescription = brand.name, - modifier = Modifier - .fillMaxSize() - .clip(CircleShape) - , - contentScale = ContentScale.Crop - ) - - this@ElevatedCard.AnimatedVisibility( - modifier = Modifier.align(Alignment.TopEnd), - visible = selected - ) { - - Surface( - modifier = Modifier.padding(Theme.spacing.small), - shape = CircleShape, - color = Color.Transparent - ) { - - Icon( - imageVector = Lucide.Check, - contentDescription = null, - tint = Theme.colors.primary, - modifier = Modifier - .padding(Theme.spacing.small) - .size(Theme.size.medium) - ) - - } - - } - - } - - } - - Spacer(Modifier.height(8.dp)) - - Text( - text = brand.name, - style = MaterialTheme.typography.labelMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - textAlign = TextAlign.Center, - color = Theme.colors.primaryFont - ) - - } - -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRowSkeleton.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRowSkeleton.kt deleted file mode 100644 index a04bbcab..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/BrandRowSkeleton.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp -import com.troves.designsystem.components.shimmer.shimmerEffect -import com.troves.designsystem.theme.Theme -import com.troves.presintation.ui.productDetails.components.shimmer - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 01/07/2026 - */ - -@Composable -fun BrandRowSkeleton( - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier.fillMaxWidth() - ) { - - Box( - modifier = Modifier - .padding(horizontal = 16.dp) - .size(width = 80.dp, height = 22.dp) - .clip(RoundedCornerShape(8.dp)) - .shimmerEffect() - ) - - Spacer(Modifier.height(16.dp)) - - LazyRow( - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - - items(8) { - - BrandItemSkeleton() - - } - - } - - } -} -@Composable -private fun BrandItemSkeleton() { - - Column( - modifier = Modifier.width(80.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - - Box( - modifier = Modifier - .size(72.dp) - .clip(CircleShape) - .shimmer() - .background(Theme.colors.backGround) - ) - - Spacer(Modifier.height(8.dp)) - - Box( - modifier = Modifier - .height(12.dp) - .width(56.dp) - .clip(RoundedCornerShape(6.dp)) - .shimmer() - .background(Theme.colors.backGround) - ) - - } - -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/EmptySearchResult.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/EmptySearchResult.kt deleted file mode 100644 index ffe58724..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/EmptySearchResult.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SegmentedButtonDefaults.Icon -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.composables.icons.lucide.Lucide -import com.composables.icons.lucide.SearchCheck -import com.troves.designsystem.theme.Theme -import org.jetbrains.compose.resources.painterResource -import troves.designsystem.generated.resources.Res -import troves.designsystem.generated.resources.ic_search - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@Composable -fun EmptySearchResult( - modifier: Modifier = Modifier, - title: String = "No products found", - message: String = "Try another keyword or adjust your filters." -) { - Column( - modifier = modifier - .fillMaxWidth() - .padding(horizontal = Theme.size.medium, vertical = Theme.size.large), - horizontalAlignment = Alignment.CenterHorizontally - ) { - - Icon( - imageVector = Lucide.SearchCheck, - contentDescription = null, - modifier = Modifier.size(Theme.size.large), - tint = Theme.colors.primary - ) - - Spacer(Modifier.height(Theme.spacing.large)) - - Text( - text = title, - style = Theme.typography.title, - textAlign = TextAlign.Center - ) - - Spacer(Modifier.height(Theme.spacing.small)) - - Text( - text = message, - style = Theme.typography.body.medium, - color = Theme.colors.onSurface, - textAlign = TextAlign.Center - ) - } -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ErrorView.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ErrorView.kt deleted file mode 100644 index d9977d64..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ErrorView.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.FilledTonalButton -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.composables.icons.lucide.Lucide -import com.composables.icons.lucide.RefreshCw -import com.composables.icons.lucide.ServerCrash - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@Composable -fun ErrorView( - message: String, - modifier: Modifier = Modifier, - onRetry: (() -> Unit)? = null, -) { - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 32.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - - Icon( - imageVector = Lucide.ServerCrash, - contentDescription = null, - modifier = Modifier.size(72.dp), - tint = MaterialTheme.colorScheme.error - ) - - Spacer(modifier = Modifier.height(24.dp)) - - Text( - text = "Something went wrong", - style = MaterialTheme.typography.headlineSmall, - textAlign = TextAlign.Center - ) - - Spacer(modifier = Modifier.height(8.dp)) - - Text( - text = message, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Center - ) - - if (onRetry != null) { - - Spacer(modifier = Modifier.height(32.dp)) - - FilledTonalButton( - onClick = onRetry - ) { - Icon( - imageVector = Lucide.RefreshCw, - contentDescription = null - ) - - Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) - - Text("Try Again") - } - } - } -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCard.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCard.kt deleted file mode 100644 index d52419d6..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCard.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import coil3.compose.AsyncImage -import com.troves.designsystem.theme.Theme -import com.troves.domain.entity.Product -import kotlin.math.absoluteValue -import kotlin.to - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ - -private val ProductAspectRatios = listOf( - 0.72f, - 0.85f, - 1.1f, - 1.3f -) - -@Composable -private fun rememberAspectRatio(productId: Long): Float { - return remember(productId) { - ProductAspectRatios[(productId.hashCode().absoluteValue) % ProductAspectRatios.size] - } -} - -@Composable -fun ProductCard( - product: Product, - modifier: Modifier = Modifier, - onClick: (Product) -> Unit, -) { - - ElevatedCard( - modifier = modifier, - onClick = { onClick(product) }, - shape = MaterialTheme.shapes.large, - elevation = CardDefaults.elevatedCardElevation( - defaultElevation = 4.dp - ) - ) { - - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(rememberAspectRatio(product.id)) - ) { - - AsyncImage( - modifier = Modifier.fillMaxSize(), - model = product.imageUrl, - contentDescription = product.title, - contentScale = ContentScale.Crop - ) - - ProductGradient() - - Text( - text = product.title, - modifier = Modifier - .align(Alignment.BottomStart) - .padding(16.dp), - maxLines = 2, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onPrimary - ) - - } - - } - -} - -@Composable -private fun ProductGradient() { - Spacer( - modifier = Modifier - .fillMaxSize() - .graphicsLayer { - alpha = .99f - } - .drawWithCache { - val gradient = Brush.verticalGradient( - colorStops = arrayOf( - 0.0f to Color.Transparent, - .55f to Color.Transparent, - 1f to Color.Gray.copy(alpha = 0.82f) - ) - ) - onDrawWithContent { - drawContent() - drawRect(gradient) - } - } - ) -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCardSkeleton.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCardSkeleton.kt deleted file mode 100644 index 0bd44e57..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductCardSkeleton.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp -import com.troves.designsystem.components.shimmer.shimmerEffect -import com.troves.designsystem.theme.Theme -import com.troves.presintation.ui.productDetails.components.shimmer - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@Composable -fun ProductCardSkeleton( - modifier: Modifier = Modifier, -) { - - ElevatedCard( - modifier = modifier, - shape = Theme.shapes.large - ) { - - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(0.85f) - ) { - - Box( - modifier = Modifier - .matchParentSize() - .shimmerEffect() - ) - - Box( - modifier = Modifier - .align(Alignment.BottomStart) - .padding(16.dp) - .width(80.dp) - .height(14.dp) - .clip(RoundedCornerShape(6.dp)) - .background(Theme.colors.surface) - ) - - } - - } - -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductGrid.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductGrid.kt deleted file mode 100644 index 69bcc490..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductGrid.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells -import androidx.compose.foundation.lazy.staggeredgrid.items -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.troves.domain.entity.Product - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@Composable -fun ProductGrid( - products: List, - modifier: Modifier = Modifier, - contentPadding: PaddingValues = PaddingValues(16.dp), - onProductClick: (Product) -> Unit, -) { - LazyVerticalStaggeredGrid( - modifier = modifier.fillMaxSize(), - columns = StaggeredGridCells.Fixed(3), - verticalItemSpacing = 12.dp, - horizontalArrangement = Arrangement.spacedBy(12.dp), - contentPadding = contentPadding - ) { - - items( - items = products, - key = Product::id - ) { product -> - - ProductCard( - product = product, - modifier = Modifier - .fillMaxWidth() - .animateItem(), - onClick = onProductClick - ) - - } - - } -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductLoading.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductLoading.kt deleted file mode 100644 index 424c41fa..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/ProductLoading.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.troves.designsystem.theme.Theme - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@Composable -fun ProductLoading( - modifier: Modifier = Modifier, -) { - SearchLoadingContent(modifier = modifier) -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSection.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSection.kt deleted file mode 100644 index ff044c0a..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSection.kt +++ /dev/null @@ -1,151 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.animateContentSize -import androidx.compose.animation.expandVertically -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkVertically -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.AssistChip -import androidx.compose.material3.AssistChipDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import com.composables.icons.lucide.History -import com.composables.icons.lucide.Lucide -import com.composables.icons.lucide.X -import com.troves.presintation.ui.search.RecentSearchUi - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@Composable -fun RecentSearchSection( - recentSearches: List, - modifier: Modifier = Modifier, - onSearchClick: (String) -> Unit, - onRemoveClick: (String) -> Unit, -) { - AnimatedVisibility( - visible = recentSearches.isNotEmpty(), - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - Column( - modifier = modifier.fillMaxWidth() - ) { - - Text( - text = "Recent Searches", - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding( - horizontal = 16.dp, - vertical = 12.dp - ) - ) - - LazyRow( - modifier = Modifier.fillMaxWidth(), - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - - items( - items = recentSearches, - key = { it } - ) { query -> - - RecentSearchChip( - query = query.query, - onClick = { - onSearchClick(query.query) - }, - onRemoveClick = { - onRemoveClick(query.query) - } - ) - - } - - } - } - } -} -@Composable -private fun RecentSearchChip( - query: String, - onClick: () -> Unit, - onRemoveClick: () -> Unit, - modifier: Modifier = Modifier -) { - - AssistChip( - modifier = modifier.animateContentSize(), - onClick = onClick, - label = { - Text( - text = query, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - }, - leadingIcon = { - Icon( - imageVector = Lucide.History, - contentDescription = null, - modifier = Modifier.size(AssistChipDefaults.IconSize) - ) - }, - trailingIcon = { - IconButton( - onClick = onRemoveClick, - modifier = Modifier.size(20.dp) - ) { - Icon( - imageVector = Lucide.X, - contentDescription = "Remove recent search" - ) - } - } - ) -} -/* -* RecentSearchSection( - recentSearches = state.recentSearches, - onSearchClick = { - onIntent(SearchIntent.SearchQueryChange(it)) - }, - onRemoveClick = { - // ViewModel action - } -)*/ diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSkeleton.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSkeleton.kt deleted file mode 100644 index 327fcb8a..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/RecentSearchSkeleton.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp -import com.troves.designsystem.components.shimmer.shimmerEffect -import com.troves.designsystem.theme.Theme -import com.troves.presintation.ui.productDetails.components.shimmer - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 01/07/2026 - */ -@Composable -fun RecentSearchSkeleton() { - - Column { - - Box( - modifier = Modifier - .padding(start = 16.dp) - .width(140.dp) - .height(20.dp) - .clip(RoundedCornerShape(8.dp)) - .shimmerEffect() - ) - - Spacer(Modifier.height(12.dp)) - - LazyRow( - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - - items(4) { - - Box( - modifier = Modifier - .width(96.dp) - .height(36.dp) - .clip(CircleShape) - .shimmerEffect() - ) - } - - } - - } - -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSection.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSection.kt deleted file mode 100644 index bd77f2bb..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSection.kt +++ /dev/null @@ -1,160 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.DockedSearchBar -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.SearchBarDefaults -import androidx.compose.material3.SearchBarDefaults.inputFieldColors -import androidx.compose.material3.Text -import androidx.compose.material3.TextFieldColors -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.key -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.composables.icons.lucide.ListFilter -import com.composables.icons.lucide.Lucide -import com.composables.icons.lucide.X -import com.troves.designsystem.theme.Theme -import com.troves.domain.entity.Brand -import org.jetbrains.compose.resources.painterResource -import org.koin.core.component.getScopeId -import troves.designsystem.generated.resources.Res -import troves.designsystem.generated.resources.ic_search - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SearchBarSection( - query: String, - modifier: Modifier = Modifier, - onQueryChange: (String) -> Unit, - onSearch: (String) -> Unit, - onFilterClick: () -> Unit, - searchSuggestions: List -) { - var expanded by rememberSaveable { mutableStateOf(false) } - - DockedSearchBar( - colors = SearchBarDefaults.colors(inputFieldColors = inputFieldColors(focusedTextColor = Theme.colors.primary)), - modifier = modifier - .fillMaxWidth().background( - color = Theme.colors.backGround - ) - .padding(horizontal = 16.dp), - inputField = { - SearchBarDefaults.InputField( - modifier = Modifier.background( - color = Theme.colors.surface - ), - query = query, - onQueryChange = onQueryChange, - onSearch = { - expanded = false - onSearch(it) - }, - expanded = expanded, - onExpandedChange = { expanded = it }, - placeholder = { - Text("Search products", color = Theme.colors.primary) - }, - leadingIcon = { - Icon( - painter = painterResource(Res.drawable.ic_search), - contentDescription = null, - tint = Theme.colors.primary - ) - }, - trailingIcon = { - Row { - AnimatedVisibility( - visible = query.isNotEmpty() - ) { - IconButton( - onClick = { onQueryChange("") } - ) { - Icon( - imageVector = Lucide.X, - contentDescription = "Clear search" - ) - } - } - - IconButton( - onClick = onFilterClick - ) { - Icon( - imageVector = Lucide.ListFilter, - contentDescription = "Open filters", - tint = Theme.colors.primary - ) - } - } - }, - ) - }, - expanded = expanded, - onExpandedChange = { - expanded = it - }, - ) { - LazyColumn( - verticalArrangement = Arrangement.Top, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.background( - color = Theme.colors.backGround - ) - ) { - items( - items = searchSuggestions, - itemContent = { - Text( - modifier = Modifier - .fillMaxWidth().padding( - horizontal = Theme.spacing.small, - vertical = Theme.spacing.medium - ).clickable { onQueryChange(it) }, - text = it, - color = Theme.colors.primary - ) - HorizontalDivider(modifier = Modifier.fillMaxWidth()) - } - ) - } - } -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSkeleton.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSkeleton.kt deleted file mode 100644 index 52158390..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchBarSkeleton.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.SearchBarDefaults -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp -import com.troves.designsystem.components.shimmer.shimmerEffect -import com.troves.designsystem.theme.Theme -import com.troves.presintation.ui.productDetails.components.shimmer - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 01/07/2026 - */ -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SearchBarSkeleton() { - - Box( - modifier = Modifier - .fillMaxWidth() - .height(56.dp) - .clip(SearchBarDefaults.inputFieldShape) - .shimmerEffect() - ) - -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchContent.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchContent.kt deleted file mode 100644 index 9e115c35..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchContent.kt +++ /dev/null @@ -1,145 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan -import androidx.compose.foundation.lazy.staggeredgrid.items -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.troves.designsystem.theme.Theme -import com.troves.domain.entity.Product -import com.troves.presintation.ui.search.SearchIntent -import com.troves.presintation.ui.search.SearchUiState - -/** - * Copyright (c) 2026 Wahid Ali Wahid Hussien. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Author: Wahid Ali Wahid Hussien - * Created: 30/06/2026 - */ -@OptIn(ExperimentalFoundationApi::class) -@Composable -fun SearchContent( - state: SearchUiState, - modifier: Modifier = Modifier, - onIntent: (SearchIntent) -> Unit, -) { - - LazyVerticalStaggeredGrid( - modifier = modifier.fillMaxSize().background( - color = Theme.colors.backGround - ), - columns = StaggeredGridCells.Fixed(3), - contentPadding = PaddingValues( - start = 16.dp, - end = 16.dp, - top = 16.dp, - bottom = 32.dp - ), - verticalItemSpacing = 12.dp, - horizontalArrangement = Arrangement.spacedBy(12.dp) - ) { - - item( - span = StaggeredGridItemSpan.FullLine - ) { - - SearchBarSection( - modifier = Modifier.fillMaxWidth(), - query = state.query ?: "", - onQueryChange = { - onIntent( - SearchIntent.SearchQueryChange(it) - ) - }, - onSearch = { - onIntent( - SearchIntent.OnSearch(it) - ) - }, - onFilterClick = { - onIntent(SearchIntent.OnFilterClick) - }, - searchSuggestions = buildList { - state.categories.map { it.name }.forEach { add(it) } - state.brands.map { it.name }.forEach { add(it) } - }, - ) - - } - - item( - span = StaggeredGridItemSpan.FullLine - ) { - - RecentSearchSection( - recentSearches = state.recentSearches.toList(), - onSearchClick = { - onIntent( - SearchIntent.SearchQueryChange(it) - ) - }, - onRemoveClick = { - //TODO() - } - ) - - } - - item( - span = StaggeredGridItemSpan.FullLine - ) { - - BrandRow( - brands = state.brands, - selectedBrand = state.brands[0].name, - onBrandSelected = { - onIntent( - SearchIntent.BrandChange(it.id.toString()) - ) - } - ) - - } - - items( - items = state.products, - key = Product::id - ) { product -> - - ProductCard( - product = product, - modifier = Modifier - .fillMaxWidth() - .animateItem(), - onClick = { - onIntent(SearchIntent.OnProductClick(product.id.toString())) - } - ) - - } - - } - -} \ No newline at end of file diff --git a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchLoadingContent.kt b/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchLoadingContent.kt deleted file mode 100644 index 8a13dfcd..00000000 --- a/presintation/src/commonMain/kotlin/com/troves/presintation/ui/search/component/SearchLoadingContent.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.troves.presintation.ui.search.component - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp - -@Composable -fun SearchLoadingContent( - modifier: Modifier = Modifier, -) { - LazyVerticalStaggeredGrid( - modifier = modifier.fillMaxSize(), - columns = StaggeredGridCells.Fixed(3), - contentPadding = PaddingValues(16.dp), - verticalItemSpacing = 12.dp, - horizontalArrangement = Arrangement.spacedBy(12.dp) - ) { - - item( - span = StaggeredGridItemSpan.FullLine - ) { - SearchBarSkeleton() - } - - item( - span = StaggeredGridItemSpan.FullLine - ) { - RecentSearchSkeleton() - } - - item( - span = StaggeredGridItemSpan.FullLine - ) { - BrandRowSkeleton() - } - - items(18) { - ProductCardSkeleton() - } - } -} \ No newline at end of file