Implement shopping lists screen (#129)
* Initialize shopping lists feature * Start shopping lists screen with Compose * Add icon to shopping list name * Add shopping lists to menu * Set max size for the list * Replace compose-adapter with accompanist * Remove unused fields from shopping lists response * Show list of shopping lists from BE * Hide shopping lists if Mealie is 0.5.6 * Add shopping list item click listener * Create material app theme for Compose * Use shorter names * Load shopping lists by pages and save to db * Make page handling logic match recipes * Add swipe to refresh to shopping lists * Extract SwipeToRefresh Composable * Make LazyPagingColumn generic * Show refresh only when mediator is refreshing * Do not refresh automatically * Allow controlling Activity state from modules * Implement navigating to shopping list screen * Move Compose libraries setup to a plugin * Implement loading full shopping list info * Move Storage classes to database module * Save shopping list items to DB * Use separate names for separate ids * Do only one DB version update * Use unique names for all columns * Display shopping list items * Move OperationUiState to ui module * Subscribe to shopping lists updates * Indicate progress with progress bar * Use strings from resources * Format shopping list item quantities * Hide unit/food/note/quantity if they are not set * Implement updating shopping list item checked state * Remove unnecessary null checks * Disable checkbox when it is being updated * Split shopping list screen into composables * Show items immediately if they are saved * Fix showing "list is empty" before the items * Show Snackbar when error happens * Reduce shopping list items paddings * Remove shopping lists when URL is changed * Add error/empty state handling to shopping lists * Fix empty error state * Fix tests compilation * Add margin between text and button * Add divider between checked and unchecked items * Move divider to the item * Refresh the shopping lists on authentication * Use retry when necessary * Remove excessive logging * Fix pages bounds check * Move FlowExtensionsTest * Update Compose version * Fix showing loading indicator for shopping lists * Add Russian translation * Fix SDK version lint check * Rename parameter to match interface * Add DB migration TODO * Get rid of DB migrations * Do not use pagination with shopping lists * Cleanup after the pagination removal * Load shopping list items * Remove shopping lists storage * Rethrow CancellationException in LoadingHelper * Add pull-to-refresh on shopping list screen * Extract LazyColumnWithLoadingState * Split refresh errors and loading state * Reuse LazyColumnWithLoadingState for shopping list items * Remove paging-compose dependency * Refresh shopping list items on authentication * Disable missing translation lint check * Update Compose and Kotlin versions * Fix order of checked items * Hide useless information from a shopping list item
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.add
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
|
||||
interface AddRecipeDataSource {
|
||||
|
||||
suspend fun addRecipe(recipe: AddRecipeInfo): String
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.add
|
||||
|
||||
data class AddRecipeInfo(
|
||||
val name: String,
|
||||
val description: String,
|
||||
val recipeYield: String,
|
||||
val recipeIngredient: List<AddRecipeIngredientInfo>,
|
||||
val recipeInstructions: List<AddRecipeInstructionInfo>,
|
||||
val settings: AddRecipeSettingsInfo,
|
||||
)
|
||||
|
||||
data class AddRecipeSettingsInfo(
|
||||
val disableComments: Boolean,
|
||||
val public: Boolean,
|
||||
)
|
||||
|
||||
data class AddRecipeIngredientInfo(
|
||||
val note: String,
|
||||
)
|
||||
|
||||
data class AddRecipeInstructionInfo(
|
||||
val text: String,
|
||||
)
|
||||
@@ -1,5 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.add
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AddRecipeRepo {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package gq.kirmanak.mealient.data.add.impl
|
||||
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.extensions.toAddRecipeInfo
|
||||
import gq.kirmanak.mealient.extensions.toDraft
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -18,14 +17,15 @@ class AddRecipeRepoImpl @Inject constructor(
|
||||
private val addRecipeDataSource: AddRecipeDataSource,
|
||||
private val addRecipeStorage: AddRecipeStorage,
|
||||
private val logger: Logger,
|
||||
private val modelMapper: ModelMapper,
|
||||
) : AddRecipeRepo {
|
||||
|
||||
override val addRecipeRequestFlow: Flow<AddRecipeInfo>
|
||||
get() = addRecipeStorage.updates.map { it.toAddRecipeInfo() }
|
||||
get() = addRecipeStorage.updates.map { modelMapper.toAddRecipeInfo(it) }
|
||||
|
||||
override suspend fun preserve(recipe: AddRecipeInfo) {
|
||||
logger.v { "preserveRecipe() called with: recipe = $recipe" }
|
||||
addRecipeStorage.save(recipe.toDraft())
|
||||
addRecipeStorage.save(modelMapper.toDraft(recipe))
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package gq.kirmanak.mealient.data.auth
|
||||
|
||||
import gq.kirmanak.mealient.shopping_lists.repo.ShoppingListsAuthRepo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthRepo {
|
||||
interface AuthRepo : ShoppingListsAuthRepo {
|
||||
|
||||
val isAuthorizedFlow: Flow<Boolean>
|
||||
override val isAuthorizedFlow: Flow<Boolean>
|
||||
|
||||
suspend fun authenticate(email: String, password: String)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ServerInfoRepo {
|
||||
|
||||
suspend fun getUrl(): String?
|
||||
@@ -8,5 +10,7 @@ interface ServerInfoRepo {
|
||||
|
||||
suspend fun tryBaseURL(baseURL: String): Result<Unit>
|
||||
|
||||
fun versionUpdates(): Flow<ServerVersion>
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ package gq.kirmanak.mealient.data.baseurl
|
||||
import gq.kirmanak.mealient.datasource.ServerUrlProvider
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -55,4 +58,11 @@ class ServerInfoRepoImpl @Inject constructor(
|
||||
serverInfoStorage.storeBaseURL(oldBaseUrl, oldVersion)
|
||||
}
|
||||
}
|
||||
|
||||
override fun versionUpdates(): Flow<ServerVersion> {
|
||||
return serverInfoStorage
|
||||
.serverVersionUpdates()
|
||||
.filterNotNull()
|
||||
.map { determineServerVersion(it) }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ServerInfoStorage {
|
||||
|
||||
suspend fun getBaseURL(): String?
|
||||
@@ -12,4 +14,5 @@ interface ServerInfoStorage {
|
||||
|
||||
suspend fun getServerVersion(): String?
|
||||
|
||||
fun serverVersionUpdates(): Flow<String?>
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.VersionInfo
|
||||
|
||||
interface VersionDataSource {
|
||||
|
||||
suspend fun getVersionInfo(): VersionInfo
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.VersionInfo
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
|
||||
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
|
||||
import gq.kirmanak.mealient.extensions.toVersionInfo
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
@@ -14,15 +15,16 @@ import javax.inject.Singleton
|
||||
class VersionDataSourceImpl @Inject constructor(
|
||||
private val v0Source: MealieDataSourceV0,
|
||||
private val v1Source: MealieDataSourceV1,
|
||||
private val modelMapper: ModelMapper,
|
||||
) : VersionDataSource {
|
||||
|
||||
override suspend fun getVersionInfo(): VersionInfo {
|
||||
val responses = coroutineScope {
|
||||
val v0Deferred = async {
|
||||
runCatchingExceptCancel { v0Source.getVersionInfo().toVersionInfo() }
|
||||
runCatchingExceptCancel { modelMapper.toVersionInfo(v0Source.getVersionInfo()) }
|
||||
}
|
||||
val v1Deferred = async {
|
||||
runCatchingExceptCancel { v1Source.getVersionInfo().toVersionInfo() }
|
||||
runCatchingExceptCancel { modelMapper.toVersionInfo(v1Source.getVersionInfo()) }
|
||||
}
|
||||
listOf(v0Deferred, v1Deferred).awaitAll()
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
|
||||
data class VersionInfo(
|
||||
val version: String,
|
||||
)
|
||||
@@ -3,6 +3,7 @@ package gq.kirmanak.mealient.data.baseurl.impl
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoStorage
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -49,6 +50,10 @@ class ServerInfoStorageImpl @Inject constructor(
|
||||
preferencesStorage.storeValues(Pair(serverVersionKey, version))
|
||||
}
|
||||
|
||||
override fun serverVersionUpdates(): Flow<String?> {
|
||||
return preferencesStorage.valueUpdates(serverVersionKey)
|
||||
}
|
||||
|
||||
private suspend fun <T> getValue(key: Preferences.Key<T>): T? = preferencesStorage.getValue(key)
|
||||
|
||||
}
|
||||
@@ -1,22 +1,17 @@
|
||||
package gq.kirmanak.mealient.data.network
|
||||
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerVersion
|
||||
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeSummaryInfo
|
||||
import gq.kirmanak.mealient.data.share.ParseRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.share.ParseRecipeURLInfo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.datasource.models.FullRecipeInfo
|
||||
import gq.kirmanak.mealient.datasource.models.ParseRecipeURLInfo
|
||||
import gq.kirmanak.mealient.datasource.models.RecipeSummaryInfo
|
||||
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
|
||||
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
|
||||
import gq.kirmanak.mealient.extensions.toFullRecipeInfo
|
||||
import gq.kirmanak.mealient.extensions.toRecipeSummaryInfo
|
||||
import gq.kirmanak.mealient.extensions.toV0Request
|
||||
import gq.kirmanak.mealient.extensions.toV1CreateRequest
|
||||
import gq.kirmanak.mealient.extensions.toV1Request
|
||||
import gq.kirmanak.mealient.extensions.toV1UpdateRequest
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -25,15 +20,16 @@ class MealieDataSourceWrapper @Inject constructor(
|
||||
private val serverInfoRepo: ServerInfoRepo,
|
||||
private val v0Source: MealieDataSourceV0,
|
||||
private val v1Source: MealieDataSourceV1,
|
||||
private val modelMapper: ModelMapper,
|
||||
) : AddRecipeDataSource, RecipeDataSource, ParseRecipeDataSource {
|
||||
|
||||
private suspend fun getVersion(): ServerVersion = serverInfoRepo.getVersion()
|
||||
|
||||
override suspend fun addRecipe(recipe: AddRecipeInfo): String = when (getVersion()) {
|
||||
ServerVersion.V0 -> v0Source.addRecipe(recipe.toV0Request())
|
||||
ServerVersion.V0 -> v0Source.addRecipe(modelMapper.toV0Request(recipe))
|
||||
ServerVersion.V1 -> {
|
||||
val slug = v1Source.createRecipe(recipe.toV1CreateRequest())
|
||||
v1Source.updateRecipe(slug, recipe.toV1UpdateRequest())
|
||||
val slug = v1Source.createRecipe(modelMapper.toV1CreateRequest(recipe))
|
||||
v1Source.updateRecipe(slug, modelMapper.toV1UpdateRequest(recipe))
|
||||
slug
|
||||
}
|
||||
}
|
||||
@@ -43,25 +39,25 @@ class MealieDataSourceWrapper @Inject constructor(
|
||||
limit: Int,
|
||||
): List<RecipeSummaryInfo> = when (getVersion()) {
|
||||
ServerVersion.V0 -> {
|
||||
v0Source.requestRecipes(start, limit).map { it.toRecipeSummaryInfo() }
|
||||
v0Source.requestRecipes(start, limit).map { modelMapper.toRecipeSummaryInfo(it) }
|
||||
}
|
||||
ServerVersion.V1 -> {
|
||||
// Imagine start is 30 and limit is 15. It means that we already have page 1 and 2, now we need page 3
|
||||
val page = start / limit + 1
|
||||
v1Source.requestRecipes(page, limit).map { it.toRecipeSummaryInfo() }
|
||||
v1Source.requestRecipes(page, limit).map { modelMapper.toRecipeSummaryInfo(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun requestRecipeInfo(slug: String): FullRecipeInfo = when (getVersion()) {
|
||||
ServerVersion.V0 -> v0Source.requestRecipeInfo(slug).toFullRecipeInfo()
|
||||
ServerVersion.V1 -> v1Source.requestRecipeInfo(slug).toFullRecipeInfo()
|
||||
ServerVersion.V0 -> modelMapper.toFullRecipeInfo(v0Source.requestRecipeInfo(slug))
|
||||
ServerVersion.V1 -> modelMapper.toFullRecipeInfo(v1Source.requestRecipeInfo(slug))
|
||||
}
|
||||
|
||||
override suspend fun parseRecipeFromURL(
|
||||
parseRecipeURLInfo: ParseRecipeURLInfo,
|
||||
): String = when (getVersion()) {
|
||||
ServerVersion.V0 -> v0Source.parseRecipeFromURL(parseRecipeURLInfo.toV0Request())
|
||||
ServerVersion.V1 -> v1Source.parseRecipeFromURL(parseRecipeURLInfo.toV1Request())
|
||||
ServerVersion.V0 -> v0Source.parseRecipeFromURL(modelMapper.toV0Request(parseRecipeURLInfo))
|
||||
ServerVersion.V1 -> v1Source.parseRecipeFromURL(modelMapper.toV1Request(parseRecipeURLInfo))
|
||||
}
|
||||
|
||||
override suspend fun getFavoriteRecipes(): List<String> = when (getVersion()) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.recipes
|
||||
|
||||
import androidx.paging.Pager
|
||||
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeWithSummaryAndIngredientsAndInstructions
|
||||
|
||||
interface RecipeRepo {
|
||||
|
||||
@@ -12,7 +12,7 @@ interface RecipeRepo {
|
||||
|
||||
suspend fun refreshRecipeInfo(recipeSlug: String): Result<Unit>
|
||||
|
||||
suspend fun loadRecipeInfo(recipeId: String): FullRecipeEntity?
|
||||
suspend fun loadRecipeInfo(recipeId: String): RecipeWithSummaryAndIngredientsAndInstructions?
|
||||
|
||||
fun updateNameQuery(name: String?)
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.recipes.db
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
|
||||
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
|
||||
interface RecipeStorage {
|
||||
suspend fun saveRecipes(recipes: List<RecipeSummaryEntity>)
|
||||
|
||||
fun queryRecipes(query: String?): PagingSource<Int, RecipeSummaryEntity>
|
||||
|
||||
suspend fun refreshAll(recipes: List<RecipeSummaryEntity>)
|
||||
|
||||
suspend fun clearAllLocalData()
|
||||
|
||||
suspend fun saveRecipeInfo(recipe: FullRecipeInfo)
|
||||
|
||||
suspend fun queryRecipeInfo(recipeId: String): FullRecipeEntity?
|
||||
|
||||
suspend fun updateFavoriteRecipes(favorites: List<String>)
|
||||
|
||||
suspend fun deleteRecipe(entity: RecipeSummaryEntity)
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.recipes.db
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.withTransaction
|
||||
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
|
||||
import gq.kirmanak.mealient.database.AppDb
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeDao
|
||||
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.extensions.toRecipeEntity
|
||||
import gq.kirmanak.mealient.extensions.toRecipeIngredientEntity
|
||||
import gq.kirmanak.mealient.extensions.toRecipeInstructionEntity
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RecipeStorageImpl @Inject constructor(
|
||||
private val db: AppDb,
|
||||
private val logger: Logger,
|
||||
) : RecipeStorage {
|
||||
private val recipeDao: RecipeDao by lazy { db.recipeDao() }
|
||||
|
||||
override suspend fun saveRecipes(recipes: List<RecipeSummaryEntity>) {
|
||||
logger.v { "saveRecipes() called with $recipes" }
|
||||
db.withTransaction { recipeDao.insertRecipes(recipes) }
|
||||
}
|
||||
|
||||
override fun queryRecipes(query: String?): PagingSource<Int, RecipeSummaryEntity> {
|
||||
logger.v { "queryRecipes() called with: query = $query" }
|
||||
return if (query == null) recipeDao.queryRecipesByPages()
|
||||
else recipeDao.queryRecipesByPages(query)
|
||||
}
|
||||
|
||||
override suspend fun refreshAll(recipes: List<RecipeSummaryEntity>) {
|
||||
logger.v { "refreshAll() called with: recipes = $recipes" }
|
||||
db.withTransaction {
|
||||
recipeDao.removeAllRecipes()
|
||||
saveRecipes(recipes)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clearAllLocalData() {
|
||||
logger.v { "clearAllLocalData() called" }
|
||||
db.withTransaction {
|
||||
recipeDao.removeAllRecipes()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun saveRecipeInfo(recipe: FullRecipeInfo) {
|
||||
logger.v { "saveRecipeInfo() called with: recipe = $recipe" }
|
||||
db.withTransaction {
|
||||
recipeDao.insertRecipe(recipe.toRecipeEntity())
|
||||
|
||||
recipeDao.deleteRecipeIngredients(recipe.remoteId)
|
||||
val ingredients = recipe.recipeIngredients.map {
|
||||
it.toRecipeIngredientEntity(recipe.remoteId)
|
||||
}
|
||||
recipeDao.insertRecipeIngredients(ingredients)
|
||||
|
||||
recipeDao.deleteRecipeInstructions(recipe.remoteId)
|
||||
val instructions = recipe.recipeInstructions.map {
|
||||
it.toRecipeInstructionEntity(recipe.remoteId)
|
||||
}
|
||||
recipeDao.insertRecipeInstructions(instructions)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun queryRecipeInfo(recipeId: String): FullRecipeEntity? {
|
||||
logger.v { "queryRecipeInfo() called with: recipeId = $recipeId" }
|
||||
val fullRecipeInfo = recipeDao.queryFullRecipeInfo(recipeId)
|
||||
logger.v { "queryRecipeInfo() returned: $fullRecipeInfo" }
|
||||
return fullRecipeInfo
|
||||
}
|
||||
|
||||
override suspend fun updateFavoriteRecipes(favorites: List<String>) {
|
||||
logger.v { "updateFavoriteRecipes() called with: favorites = $favorites" }
|
||||
db.withTransaction {
|
||||
recipeDao.setFavorite(favorites)
|
||||
recipeDao.setNonFavorite(favorites)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteRecipe(entity: RecipeSummaryEntity) {
|
||||
logger.v { "deleteRecipeBySlug() called with: entity = $entity" }
|
||||
recipeDao.deleteRecipe(entity)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.recipes.impl
|
||||
|
||||
import androidx.paging.InvalidatingPagingSourceFactory
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -4,12 +4,13 @@ import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeWithSummaryAndIngredientsAndInstructions
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -21,6 +22,7 @@ class RecipeRepoImpl @Inject constructor(
|
||||
private val pagingSourceFactory: RecipePagingSourceFactory,
|
||||
private val dataSource: RecipeDataSource,
|
||||
private val logger: Logger,
|
||||
private val modelMapper: ModelMapper,
|
||||
) : RecipeRepo {
|
||||
|
||||
override fun createPager(): Pager<Int, RecipeSummaryEntity> {
|
||||
@@ -45,13 +47,21 @@ class RecipeRepoImpl @Inject constructor(
|
||||
override suspend fun refreshRecipeInfo(recipeSlug: String): Result<Unit> {
|
||||
logger.v { "refreshRecipeInfo() called with: recipeSlug = $recipeSlug" }
|
||||
return runCatchingExceptCancel {
|
||||
storage.saveRecipeInfo(dataSource.requestRecipeInfo(recipeSlug))
|
||||
val info = dataSource.requestRecipeInfo(recipeSlug)
|
||||
val entity = modelMapper.toRecipeEntity(info)
|
||||
val ingredients = info.recipeIngredients.map {
|
||||
modelMapper.toRecipeIngredientEntity(it, entity.remoteId)
|
||||
}
|
||||
val instructions = info.recipeInstructions.map {
|
||||
modelMapper.toRecipeInstructionEntity(it, entity.remoteId)
|
||||
}
|
||||
storage.saveRecipeInfo(entity, ingredients, instructions)
|
||||
}.onFailure {
|
||||
logger.e(it) { "loadRecipeInfo: can't update full recipe info" }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadRecipeInfo(recipeId: String): FullRecipeEntity? {
|
||||
override suspend fun loadRecipeInfo(recipeId: String): RecipeWithSummaryAndIngredientsAndInstructions? {
|
||||
logger.v { "loadRecipeInfo() called with: recipeId = $recipeId" }
|
||||
val recipeInfo = storage.queryRecipeInfo(recipeId)
|
||||
logger.v { "loadRecipeInfo() returned: $recipeInfo" }
|
||||
|
||||
@@ -5,12 +5,12 @@ import androidx.paging.*
|
||||
import androidx.paging.LoadType.PREPEND
|
||||
import androidx.paging.LoadType.REFRESH
|
||||
import gq.kirmanak.mealient.architecture.configuration.AppDispatchers
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.extensions.toRecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -24,6 +24,7 @@ class RecipesRemoteMediator @Inject constructor(
|
||||
private val network: RecipeDataSource,
|
||||
private val pagingSourceFactory: RecipePagingSourceFactory,
|
||||
private val logger: Logger,
|
||||
private val modelMapper: ModelMapper,
|
||||
private val dispatchers: AppDispatchers,
|
||||
) : RemoteMediator<Int, RecipeSummaryEntity>() {
|
||||
|
||||
@@ -75,7 +76,7 @@ class RecipesRemoteMediator @Inject constructor(
|
||||
val entities = withContext(dispatchers.default) {
|
||||
recipes.map { recipe ->
|
||||
val isFavorite = favorites.contains(recipe.slug)
|
||||
recipe.toRecipeSummaryEntity(isFavorite)
|
||||
modelMapper.toRecipeSummaryEntity(recipe, isFavorite)
|
||||
}
|
||||
}
|
||||
if (loadType == REFRESH) storage.refreshAll(entities)
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.recipes.network
|
||||
|
||||
data class FullRecipeInfo(
|
||||
val remoteId: String,
|
||||
val name: String,
|
||||
val recipeYield: String,
|
||||
val recipeIngredients: List<RecipeIngredientInfo>,
|
||||
val recipeInstructions: List<RecipeInstructionInfo>,
|
||||
val settings: RecipeSettingsInfo,
|
||||
)
|
||||
|
||||
data class RecipeSettingsInfo(
|
||||
val disableAmounts: Boolean,
|
||||
)
|
||||
|
||||
data class RecipeIngredientInfo(
|
||||
val note: String,
|
||||
val quantity: Double?,
|
||||
val unit: String?,
|
||||
val food: String?,
|
||||
val title: String?,
|
||||
)
|
||||
|
||||
data class RecipeInstructionInfo(
|
||||
val text: String,
|
||||
)
|
||||
@@ -1,5 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.recipes.network
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.FullRecipeInfo
|
||||
import gq.kirmanak.mealient.datasource.models.RecipeSummaryInfo
|
||||
|
||||
interface RecipeDataSource {
|
||||
suspend fun requestRecipes(start: Int, limit: Int): List<RecipeSummaryInfo>
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.recipes.network
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
|
||||
data class RecipeSummaryInfo(
|
||||
val remoteId: String,
|
||||
val name: String,
|
||||
val slug: String,
|
||||
val description: String = "",
|
||||
val imageId: String,
|
||||
val dateAdded: LocalDate,
|
||||
val dateUpdated: LocalDateTime
|
||||
)
|
||||
@@ -1,5 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.ParseRecipeURLInfo
|
||||
|
||||
interface ParseRecipeDataSource {
|
||||
|
||||
suspend fun parseRecipeFromURL(parseRecipeURLInfo: ParseRecipeURLInfo): String
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
data class ParseRecipeURLInfo(
|
||||
val url: String,
|
||||
val includeTags: Boolean
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
import androidx.core.util.PatternsCompat
|
||||
import gq.kirmanak.mealient.datasource.models.ParseRecipeURLInfo
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -15,6 +15,7 @@ import gq.kirmanak.mealient.data.auth.impl.AuthDataSourceImpl
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthRepoImpl
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthStorageImpl
|
||||
import gq.kirmanak.mealient.datasource.AuthenticationProvider
|
||||
import gq.kirmanak.mealient.shopping_lists.repo.ShoppingListsAuthRepo
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@@ -45,4 +46,8 @@ interface AuthModule {
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindAuthStorage(authStorageImpl: AuthStorageImpl): AuthStorage
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindShoppingListsAuthRepo(impl: AuthRepoImpl): ShoppingListsAuthRepo
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.network.MealieDataSourceWrapper
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorageImpl
|
||||
import gq.kirmanak.mealient.data.recipes.impl.*
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
@@ -27,10 +25,6 @@ interface RecipeModule {
|
||||
@Singleton
|
||||
fun provideRecipeDataSource(mealieDataSourceWrapper: MealieDataSourceWrapper): RecipeDataSource
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideRecipeStorage(recipeStorageImpl: RecipeStorageImpl): RecipeStorage
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideRecipeRepo(recipeRepoImpl: RecipeRepoImpl): RecipeRepo
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package gq.kirmanak.mealient.extensions
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
|
||||
fun <T> Flow<T>.valueUpdatesOnly(): Flow<T> = when (this) {
|
||||
is ValueUpdateOnlyFlowImpl<T> -> this
|
||||
else -> ValueUpdateOnlyFlowImpl(this)
|
||||
}
|
||||
|
||||
private class ValueUpdateOnlyFlowImpl<T>(private val upstream: Flow<T>) : Flow<T> {
|
||||
|
||||
override suspend fun collect(collector: FlowCollector<T>) {
|
||||
var previousValue: T? = null
|
||||
upstream.collect { value ->
|
||||
if (previousValue != null && previousValue != value) {
|
||||
collector.emit(value)
|
||||
}
|
||||
previousValue = value
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
package gq.kirmanak.mealient.extensions
|
||||
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeIngredientInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInstructionInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeSettingsInfo
|
||||
import gq.kirmanak.mealient.data.baseurl.VersionInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeIngredientInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeInstructionInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeSettingsInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeSummaryInfo
|
||||
import gq.kirmanak.mealient.data.share.ParseRecipeURLInfo
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeInstructionEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeIngredientV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeInstructionV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeSettingsV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeIngredientResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeInstructionResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.ParseRecipeURLRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.VersionResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v1.models.AddRecipeIngredientV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.AddRecipeInstructionV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.AddRecipeSettingsV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeIngredientResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeInstructionResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSettingsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.ParseRecipeURLRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeDraft
|
||||
import java.util.*
|
||||
|
||||
fun FullRecipeInfo.toRecipeEntity() = RecipeEntity(
|
||||
remoteId = remoteId,
|
||||
recipeYield = recipeYield,
|
||||
disableAmounts = settings.disableAmounts,
|
||||
)
|
||||
|
||||
fun RecipeIngredientInfo.toRecipeIngredientEntity(remoteId: String) = RecipeIngredientEntity(
|
||||
recipeId = remoteId,
|
||||
note = note,
|
||||
unit = unit,
|
||||
food = food,
|
||||
quantity = quantity,
|
||||
title = title,
|
||||
)
|
||||
|
||||
fun RecipeInstructionInfo.toRecipeInstructionEntity(remoteId: String) = RecipeInstructionEntity(
|
||||
recipeId = remoteId,
|
||||
text = text
|
||||
)
|
||||
|
||||
fun GetRecipeSummaryResponseV0.toRecipeSummaryInfo() = RecipeSummaryInfo(
|
||||
remoteId = remoteId.toString(),
|
||||
name = name,
|
||||
slug = slug,
|
||||
description = description,
|
||||
dateAdded = dateAdded,
|
||||
dateUpdated = dateUpdated,
|
||||
imageId = slug,
|
||||
)
|
||||
|
||||
fun GetRecipeSummaryResponseV1.toRecipeSummaryInfo() = RecipeSummaryInfo(
|
||||
remoteId = remoteId,
|
||||
name = name,
|
||||
slug = slug,
|
||||
description = description,
|
||||
dateAdded = dateAdded,
|
||||
dateUpdated = dateUpdated,
|
||||
imageId = remoteId,
|
||||
)
|
||||
|
||||
fun RecipeSummaryInfo.toRecipeSummaryEntity(isFavorite: Boolean) = RecipeSummaryEntity(
|
||||
remoteId = remoteId,
|
||||
name = name,
|
||||
slug = slug,
|
||||
description = description,
|
||||
dateAdded = dateAdded,
|
||||
dateUpdated = dateUpdated,
|
||||
imageId = imageId,
|
||||
isFavorite = isFavorite,
|
||||
)
|
||||
|
||||
fun VersionResponseV0.toVersionInfo() = VersionInfo(version)
|
||||
|
||||
fun VersionResponseV1.toVersionInfo() = VersionInfo(version)
|
||||
|
||||
fun AddRecipeDraft.toAddRecipeInfo() = AddRecipeInfo(
|
||||
name = recipeName,
|
||||
description = recipeDescription,
|
||||
recipeYield = recipeYield,
|
||||
recipeIngredient = recipeIngredients.map { AddRecipeIngredientInfo(note = it) },
|
||||
recipeInstructions = recipeInstructions.map { AddRecipeInstructionInfo(text = it) },
|
||||
settings = AddRecipeSettingsInfo(
|
||||
public = isRecipePublic,
|
||||
disableComments = areCommentsDisabled,
|
||||
)
|
||||
)
|
||||
|
||||
fun AddRecipeInfo.toDraft(): AddRecipeDraft = AddRecipeDraft(
|
||||
recipeName = name,
|
||||
recipeDescription = description,
|
||||
recipeYield = recipeYield,
|
||||
recipeInstructions = recipeInstructions.map { it.text },
|
||||
recipeIngredients = recipeIngredient.map { it.note },
|
||||
isRecipePublic = settings.public,
|
||||
areCommentsDisabled = settings.disableComments,
|
||||
)
|
||||
|
||||
fun GetRecipeResponseV0.toFullRecipeInfo() = FullRecipeInfo(
|
||||
remoteId = remoteId.toString(),
|
||||
name = name,
|
||||
recipeYield = recipeYield,
|
||||
recipeIngredients = recipeIngredients.map { it.toRecipeIngredientInfo() },
|
||||
recipeInstructions = recipeInstructions.map { it.toRecipeInstructionInfo() },
|
||||
settings = RecipeSettingsInfo(disableAmounts = true)
|
||||
)
|
||||
|
||||
fun GetRecipeIngredientResponseV0.toRecipeIngredientInfo() = RecipeIngredientInfo(
|
||||
note = note,
|
||||
unit = null,
|
||||
food = null,
|
||||
quantity = 1.0,
|
||||
title = null,
|
||||
)
|
||||
|
||||
fun GetRecipeInstructionResponseV0.toRecipeInstructionInfo() = RecipeInstructionInfo(
|
||||
text = text
|
||||
)
|
||||
|
||||
fun GetRecipeResponseV1.toFullRecipeInfo() = FullRecipeInfo(
|
||||
remoteId = remoteId,
|
||||
name = name,
|
||||
recipeYield = recipeYield,
|
||||
recipeIngredients = recipeIngredients.map { it.toRecipeIngredientInfo() },
|
||||
recipeInstructions = recipeInstructions.map { it.toRecipeInstructionInfo() },
|
||||
settings = settings.toRecipeSettingsInfo(),
|
||||
)
|
||||
|
||||
private fun GetRecipeSettingsResponseV1.toRecipeSettingsInfo() = RecipeSettingsInfo(
|
||||
disableAmounts = disableAmount,
|
||||
)
|
||||
|
||||
fun GetRecipeIngredientResponseV1.toRecipeIngredientInfo() = RecipeIngredientInfo(
|
||||
note = note,
|
||||
unit = unit?.name,
|
||||
food = food?.name,
|
||||
quantity = quantity,
|
||||
title = title,
|
||||
)
|
||||
|
||||
fun GetRecipeInstructionResponseV1.toRecipeInstructionInfo() = RecipeInstructionInfo(
|
||||
text = text
|
||||
)
|
||||
|
||||
fun AddRecipeInfo.toV0Request() = AddRecipeRequestV0(
|
||||
name = name,
|
||||
description = description,
|
||||
recipeYield = recipeYield,
|
||||
recipeIngredient = recipeIngredient.map { it.toV0Ingredient() },
|
||||
recipeInstructions = recipeInstructions.map { it.toV0Instruction() },
|
||||
settings = settings.toV0Settings(),
|
||||
)
|
||||
|
||||
private fun AddRecipeSettingsInfo.toV0Settings() = AddRecipeSettingsV0(
|
||||
disableComments = disableComments,
|
||||
public = public,
|
||||
)
|
||||
|
||||
private fun AddRecipeIngredientInfo.toV0Ingredient() = AddRecipeIngredientV0(
|
||||
note = note,
|
||||
)
|
||||
|
||||
private fun AddRecipeInstructionInfo.toV0Instruction() = AddRecipeInstructionV0(
|
||||
text = text,
|
||||
)
|
||||
|
||||
|
||||
fun AddRecipeInfo.toV1CreateRequest() = CreateRecipeRequestV1(
|
||||
name = name,
|
||||
)
|
||||
|
||||
fun AddRecipeInfo.toV1UpdateRequest() = UpdateRecipeRequestV1(
|
||||
description = description,
|
||||
recipeYield = recipeYield,
|
||||
recipeIngredient = recipeIngredient.map { it.toV1Ingredient() },
|
||||
recipeInstructions = recipeInstructions.map { it.toV1Instruction() },
|
||||
settings = settings.toV1Settings(),
|
||||
)
|
||||
|
||||
private fun AddRecipeSettingsInfo.toV1Settings() = AddRecipeSettingsV1(
|
||||
disableComments = disableComments,
|
||||
public = public,
|
||||
)
|
||||
|
||||
private fun AddRecipeIngredientInfo.toV1Ingredient() = AddRecipeIngredientV1(
|
||||
id = UUID.randomUUID().toString(),
|
||||
note = note,
|
||||
)
|
||||
|
||||
private fun AddRecipeInstructionInfo.toV1Instruction() = AddRecipeInstructionV1(
|
||||
id = UUID.randomUUID().toString(),
|
||||
text = text,
|
||||
ingredientReferences = emptyList(),
|
||||
)
|
||||
|
||||
fun ParseRecipeURLInfo.toV1Request() = ParseRecipeURLRequestV1(
|
||||
url = url,
|
||||
includeTags = includeTags,
|
||||
)
|
||||
|
||||
fun ParseRecipeURLInfo.toV0Request() = ParseRecipeURLRequestV0(
|
||||
url = url,
|
||||
)
|
||||
@@ -1,61 +0,0 @@
|
||||
package gq.kirmanak.mealient.ui
|
||||
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import androidx.core.view.isVisible
|
||||
|
||||
sealed class OperationUiState<T> {
|
||||
|
||||
val exceptionOrNull: Throwable?
|
||||
get() = (this as? Failure)?.exception
|
||||
|
||||
val isSuccess: Boolean
|
||||
get() = this is Success
|
||||
|
||||
val isProgress: Boolean
|
||||
get() = this is Progress
|
||||
|
||||
val isFailure: Boolean
|
||||
get() = this is Failure
|
||||
|
||||
fun updateButtonState(button: Button) {
|
||||
button.isEnabled = !isProgress
|
||||
button.isClickable = !isProgress
|
||||
}
|
||||
|
||||
fun updateProgressState(progressBar: ProgressBar) {
|
||||
progressBar.isVisible = isProgress
|
||||
}
|
||||
|
||||
class Initial<T> : OperationUiState<T>() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return javaClass.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
class Progress<T> : OperationUiState<T>() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return javaClass.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
data class Failure<T>(val exception: Throwable) : OperationUiState<T>()
|
||||
|
||||
data class Success<T>(val value: T) : OperationUiState<T>()
|
||||
|
||||
companion object {
|
||||
fun <T> fromResult(result: Result<T>) = result.fold({ Success(it) }, { Failure(it) })
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,14 @@ import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalAddRecipeFr
|
||||
import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalAuthenticationFragment
|
||||
import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalBaseURLFragment
|
||||
import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalRecipesListFragment
|
||||
import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalShoppingListsFragment
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.databinding.MainActivityBinding
|
||||
import gq.kirmanak.mealient.extensions.collectWhenResumed
|
||||
import gq.kirmanak.mealient.extensions.observeOnce
|
||||
import gq.kirmanak.mealient.ui.ActivityUiState
|
||||
import gq.kirmanak.mealient.ui.BaseActivity
|
||||
import gq.kirmanak.mealient.ui.CheckableMenuItem
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : BaseActivity<MainActivityBinding>(
|
||||
@@ -60,7 +63,7 @@ class MainActivity : BaseActivity<MainActivityBinding>(
|
||||
viewModel.onSearchQuery(query.trim().takeUnless { it.isEmpty() })
|
||||
}
|
||||
binding.navigationView.setNavigationItemSelectedListener(::onNavigationItemSelected)
|
||||
viewModel.uiStateLive.observe(this, ::onUiStateChange)
|
||||
collectWhenResumed(viewModel.uiState, ::onUiStateChange)
|
||||
collectWhenResumed(viewModel.clearSearchViewFocus) {
|
||||
logger.d { "clearSearchViewFocus(): received event" }
|
||||
binding.toolbar.clearSearchFocus()
|
||||
@@ -77,6 +80,7 @@ class MainActivity : BaseActivity<MainActivityBinding>(
|
||||
val directions = when (menuItem.itemId) {
|
||||
R.id.add_recipe -> actionGlobalAddRecipeFragment()
|
||||
R.id.recipes_list -> actionGlobalRecipesListFragment()
|
||||
R.id.shopping_lists -> actionGlobalShoppingListsFragment()
|
||||
R.id.change_url -> actionGlobalBaseURLFragment(false)
|
||||
R.id.login -> actionGlobalAuthenticationFragment()
|
||||
R.id.logout -> {
|
||||
@@ -90,15 +94,24 @@ class MainActivity : BaseActivity<MainActivityBinding>(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onUiStateChange(uiState: MainActivityUiState) {
|
||||
private fun onUiStateChange(uiState: ActivityUiState) {
|
||||
logger.v { "onUiStateChange() called with: uiState = $uiState" }
|
||||
val checkedMenuItem = when (uiState.checkedMenuItem) {
|
||||
CheckableMenuItem.ShoppingLists -> R.id.shopping_lists
|
||||
CheckableMenuItem.RecipesList -> R.id.recipes_list
|
||||
CheckableMenuItem.AddRecipe -> R.id.add_recipe
|
||||
CheckableMenuItem.ChangeUrl -> R.id.change_url
|
||||
CheckableMenuItem.Login -> R.id.login
|
||||
null -> null
|
||||
}
|
||||
for (menuItem in binding.navigationView.menu.iterator()) {
|
||||
val itemId = menuItem.itemId
|
||||
when (itemId) {
|
||||
R.id.logout -> menuItem.isVisible = uiState.canShowLogout
|
||||
R.id.login -> menuItem.isVisible = uiState.canShowLogin
|
||||
R.id.shopping_lists -> menuItem.isVisible = uiState.v1MenuItemsVisible
|
||||
}
|
||||
menuItem.isChecked = itemId == uiState.checkedMenuItemId
|
||||
menuItem.isChecked = itemId == checkedMenuItem
|
||||
}
|
||||
|
||||
binding.toolbar.isVisible = uiState.navigationVisible
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package gq.kirmanak.mealient.ui.activity
|
||||
|
||||
import androidx.annotation.IdRes
|
||||
|
||||
data class MainActivityUiState(
|
||||
val isAuthorized: Boolean = false,
|
||||
val navigationVisible: Boolean = false,
|
||||
val searchVisible: Boolean = false,
|
||||
@IdRes val checkedMenuItemId: Int? = null,
|
||||
) {
|
||||
val canShowLogin: Boolean get() = !isAuthorized
|
||||
|
||||
val canShowLogout: Boolean get() = isAuthorized
|
||||
}
|
||||
@@ -1,16 +1,23 @@
|
||||
package gq.kirmanak.mealient.ui.activity
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerVersion
|
||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.ActivityUiState
|
||||
import gq.kirmanak.mealient.ui.ActivityUiStateController
|
||||
import gq.kirmanak.mealient.ui.baseurl.BaseURLFragmentArgs
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
@@ -24,14 +31,10 @@ class MainActivityViewModel @Inject constructor(
|
||||
private val disclaimerStorage: DisclaimerStorage,
|
||||
private val serverInfoRepo: ServerInfoRepo,
|
||||
private val recipeRepo: RecipeRepo,
|
||||
private val activityUiStateController: ActivityUiStateController,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableLiveData(MainActivityUiState())
|
||||
val uiStateLive: LiveData<MainActivityUiState>
|
||||
get() = _uiState.distinctUntilChanged()
|
||||
var uiState: MainActivityUiState
|
||||
get() = checkNotNull(_uiState.value) { "UiState must not be null" }
|
||||
private set(value) = _uiState.postValue(value)
|
||||
val uiState: StateFlow<ActivityUiState> = activityUiStateController.getUiStateFlow()
|
||||
|
||||
private val _startDestination = MutableLiveData<StartDestinationInfo>()
|
||||
val startDestination: LiveData<StartDestinationInfo> = _startDestination
|
||||
@@ -44,6 +47,10 @@ class MainActivityViewModel @Inject constructor(
|
||||
.onEach { isAuthorized -> updateUiState { it.copy(isAuthorized = isAuthorized) } }
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
serverInfoRepo.versionUpdates()
|
||||
.onEach { version -> updateUiState { it.copy(v1MenuItemsVisible = version == ServerVersion.V1) } }
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
viewModelScope.launch {
|
||||
_startDestination.value = when {
|
||||
!disclaimerStorage.isDisclaimerAccepted() -> {
|
||||
@@ -59,8 +66,8 @@ class MainActivityViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateUiState(updater: (MainActivityUiState) -> MainActivityUiState) {
|
||||
uiState = updater(uiState)
|
||||
fun updateUiState(updater: (ActivityUiState) -> ActivityUiState) {
|
||||
activityUiStateController.updateUiState(updater)
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
|
||||
@@ -12,15 +12,16 @@ import androidx.fragment.app.viewModels
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeIngredientInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInstructionInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeSettingsInfo
|
||||
import gq.kirmanak.mealient.databinding.FragmentAddRecipeBinding
|
||||
import gq.kirmanak.mealient.databinding.ViewSingleInputBinding
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeIngredientInfo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInstructionInfo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeSettingsInfo
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.collectWhenViewResumed
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.CheckableMenuItem
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -41,7 +42,7 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
it.copy(
|
||||
navigationVisible = true,
|
||||
searchVisible = false,
|
||||
checkedMenuItemId = R.id.add_recipe,
|
||||
checkedMenuItem = CheckableMenuItem.AddRecipe,
|
||||
)
|
||||
}
|
||||
viewModel.loadPreservedRequest()
|
||||
|
||||
@@ -3,8 +3,8 @@ package gq.kirmanak.mealient.ui.add
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
|
||||
@@ -13,6 +13,7 @@ import gq.kirmanak.mealient.databinding.FragmentAuthenticationBinding
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.CheckableMenuItem
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import javax.inject.Inject
|
||||
@@ -32,7 +33,11 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
binding.button.setOnClickListener { onLoginClicked() }
|
||||
activityViewModel.updateUiState {
|
||||
it.copy(navigationVisible = true, searchVisible = false, checkedMenuItemId = R.id.login)
|
||||
it.copy(
|
||||
navigationVisible = true,
|
||||
searchVisible = false,
|
||||
checkedMenuItem = CheckableMenuItem.AddRecipe
|
||||
)
|
||||
}
|
||||
viewModel.uiState.observe(viewLifecycleOwner, ::onUiStateChange)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import gq.kirmanak.mealient.databinding.FragmentBaseUrlBinding
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.CheckableMenuItem
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import gq.kirmanak.mealient.ui.baseurl.BaseURLFragmentDirections.Companion.actionBaseURLFragmentToRecipesListFragment
|
||||
@@ -39,7 +40,7 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
||||
it.copy(
|
||||
navigationVisible = !args.isOnboarding,
|
||||
searchVisible = false,
|
||||
checkedMenuItemId = R.id.change_url,
|
||||
checkedMenuItem = CheckableMenuItem.ChangeUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.shopping_lists.repo.ShoppingListsRepo
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
@@ -20,6 +21,7 @@ class BaseURLViewModel @Inject constructor(
|
||||
private val authRepo: AuthRepo,
|
||||
private val recipeRepo: RecipeRepo,
|
||||
private val logger: Logger,
|
||||
private val shoppingListsRepo: ShoppingListsRepo,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableLiveData<OperationUiState<Unit>>(OperationUiState.Initial())
|
||||
|
||||
@@ -58,7 +58,11 @@ class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer) {
|
||||
}
|
||||
viewModel.startCountDown()
|
||||
activityViewModel.updateUiState {
|
||||
it.copy(navigationVisible = false, searchVisible = false, checkedMenuItemId = null)
|
||||
it.copy(
|
||||
navigationVisible = false,
|
||||
searchVisible = false,
|
||||
checkedMenuItem = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,13 @@ import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.architecture.valueUpdatesOnly
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.databinding.FragmentRecipesListBinding
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.extensions.*
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.CheckableMenuItem
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import gq.kirmanak.mealient.ui.recipes.RecipesListFragmentDirections.Companion.actionRecipesFragmentToRecipeInfoFragment
|
||||
import gq.kirmanak.mealient.ui.recipes.images.RecipePreloaderFactory
|
||||
@@ -54,7 +56,7 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) {
|
||||
it.copy(
|
||||
navigationVisible = true,
|
||||
searchVisible = true,
|
||||
checkedMenuItemId = R.id.recipes_list
|
||||
checkedMenuItem = CheckableMenuItem.RecipesList,
|
||||
)
|
||||
}
|
||||
collectWhenViewResumed(viewModel.showFavoriteIcon) { showFavoriteIcon ->
|
||||
|
||||
@@ -7,10 +7,10 @@ import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.architecture.valueUpdatesOnly
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.extensions.valueUpdatesOnly
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
android:icon="@drawable/ic_add"
|
||||
android:title="@string/menu_navigation_drawer_add_recipe" />
|
||||
|
||||
<item
|
||||
android:id="@+id/shopping_lists"
|
||||
android:visible="false"
|
||||
android:checkable="true"
|
||||
android:icon="@drawable/ic_shopping_cart"
|
||||
android:title="@string/menu_navigation_drawer_shopping_lists" />
|
||||
|
||||
<item
|
||||
android:id="@+id/change_url"
|
||||
android:checkable="true"
|
||||
|
||||
@@ -63,6 +63,11 @@
|
||||
android:label="fragment_add_recipe"
|
||||
tools:layout="@layout/fragment_add_recipe" />
|
||||
|
||||
|
||||
<fragment
|
||||
android:id="@+id/shoppingListsFragment"
|
||||
android:name="gq.kirmanak.mealient.shopping_lists.ui.ShoppingListsFragment" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_authenticationFragment"
|
||||
app:destination="@id/authenticationFragment" />
|
||||
@@ -81,4 +86,9 @@
|
||||
android:id="@+id/action_global_baseURLFragment"
|
||||
app:destination="@id/baseURLFragment"
|
||||
app:popUpTo="@id/recipesListFragment" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_shoppingListsFragment"
|
||||
app:destination="@id/shoppingListsFragment"
|
||||
app:popUpTo="@id/recipesListFragment" />
|
||||
</navigation>
|
||||
@@ -65,4 +65,5 @@
|
||||
<string name="view_holder_recipe_delete_content_description">Удалить рецепт</string>
|
||||
<string name="fragment_recipes_favorite_added">%1$s добавлено в избранное</string>
|
||||
<string name="fragment_recipes_favorite_removed">%1$s удалено из избранного</string>
|
||||
<string name="menu_navigation_drawer_shopping_lists">Списки покупок</string>
|
||||
</resources>
|
||||
|
||||
@@ -68,4 +68,5 @@
|
||||
<string name="view_holder_recipe_delete_content_description">Delete recipe</string>
|
||||
<string name="fragment_recipes_favorite_added">Added %1$s to favorites</string>
|
||||
<string name="fragment_recipes_favorite_removed">Removed %1$s from favorites</string>
|
||||
<string name="menu_navigation_drawer_shopping_lists">Shopping lists</string>
|
||||
</resources>
|
||||
@@ -3,10 +3,12 @@ package gq.kirmanak.mealient.data.add.impl
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_ADD_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.datastore_test.PORRIDGE_RECIPE_DRAFT
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapperImpl
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_ADD_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_DRAFT
|
||||
import io.mockk.*
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -24,12 +26,14 @@ class AddRecipeRepoTest : BaseUnitTest() {
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var storage: AddRecipeStorage
|
||||
|
||||
private val modelMapper: ModelMapper = ModelMapperImpl()
|
||||
|
||||
private lateinit var subject: AddRecipeRepo
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
subject = AddRecipeRepoImpl(dataSource, storage, logger)
|
||||
subject = AddRecipeRepoImpl(dataSource, storage, logger, modelMapper)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.datasource.models.VersionInfo
|
||||
import gq.kirmanak.mealient.datasource_test.VERSION_INFO_V0
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_BASE_URL
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_VERSION
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.VERSION_INFO_V0
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.impl.annotations.MockK
|
||||
|
||||
@@ -5,6 +5,18 @@ import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
|
||||
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_ADD_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_ADD_RECIPE_REQUEST_V0
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_CREATE_RECIPE_REQUEST_V1
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_RECIPE_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_RECIPE_SUMMARY_RESPONSE_V0
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_RECIPE_SUMMARY_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_UPDATE_RECIPE_REQUEST_V1
|
||||
import gq.kirmanak.mealient.datasource_test.RECIPE_SUMMARY_PORRIDGE_V0
|
||||
import gq.kirmanak.mealient.datasource_test.RECIPE_SUMMARY_PORRIDGE_V1
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapperImpl
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.FAVORITE_RECIPES_LIST
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_AUTH_HEADER
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_SERVER_VERSION_V0
|
||||
@@ -12,16 +24,6 @@ import gq.kirmanak.mealient.test.AuthImplTestData.TEST_SERVER_VERSION_V1
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.USER_INFO_V0
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.USER_INFO_V1
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_ADD_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_ADD_RECIPE_REQUEST_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_CREATE_RECIPE_REQUEST_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_RESPONSE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_UPDATE_RECIPE_REQUEST_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.RECIPE_SUMMARY_PORRIDGE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.RECIPE_SUMMARY_PORRIDGE_V1
|
||||
import io.mockk.*
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -45,12 +47,14 @@ class MealieDataSourceWrapperTest : BaseUnitTest() {
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var v1Source: MealieDataSourceV1
|
||||
|
||||
private val modelMapper: ModelMapper = ModelMapperImpl()
|
||||
|
||||
lateinit var subject: MealieDataSourceWrapper
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
subject = MealieDataSourceWrapper(serverInfoRepo, v0Source, v1Source)
|
||||
subject = MealieDataSourceWrapper(serverInfoRepo, v0Source, v1Source, modelMapper)
|
||||
coEvery { v0Source.requestUserInfo() } returns USER_INFO_V0
|
||||
coEvery { v1Source.requestUserInfo() } returns USER_INFO_V1
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.recipes.db
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import gq.kirmanak.mealient.database.AppDb
|
||||
import gq.kirmanak.mealient.test.HiltRobolectricTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.BREAD_INGREDIENT
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_BREAD_RECIPE_INGREDIENT_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.FULL_CAKE_INFO_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.FULL_PORRIDGE_INFO_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_CAKE_RECIPE_INSTRUCTION_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_INSTRUCTION
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARY_ENTITIES
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RecipeStorageImplTest : HiltRobolectricTest() {
|
||||
|
||||
@Inject
|
||||
lateinit var subject: RecipeStorageImpl
|
||||
|
||||
@Inject
|
||||
lateinit var appDb: AppDb
|
||||
|
||||
@Test
|
||||
fun `when saveRecipes then saves recipes`() = runTest {
|
||||
subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
|
||||
val actualTags = appDb.recipeDao().queryAllRecipes()
|
||||
assertThat(actualTags).containsExactly(
|
||||
CAKE_RECIPE_SUMMARY_ENTITY,
|
||||
PORRIDGE_RECIPE_SUMMARY_ENTITY
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when refreshAll then old recipes aren't preserved`() = runTest {
|
||||
subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
|
||||
subject.refreshAll(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
|
||||
val actual = appDb.recipeDao().queryAllRecipes()
|
||||
assertThat(actual).containsExactly(CAKE_RECIPE_SUMMARY_ENTITY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when clearAllLocalData then recipes aren't preserved`() = runTest {
|
||||
subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
|
||||
subject.clearAllLocalData()
|
||||
val actual = appDb.recipeDao().queryAllRecipes()
|
||||
assertThat(actual).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when saveRecipeInfo then saves recipe info`() = runTest {
|
||||
subject.saveRecipes(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
|
||||
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
|
||||
val actual = appDb.recipeDao().queryFullRecipeInfo("1")
|
||||
assertThat(actual).isEqualTo(FULL_CAKE_INFO_ENTITY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when saveRecipeInfo with two then saves second`() = runTest {
|
||||
subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
|
||||
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
|
||||
subject.saveRecipeInfo(PORRIDGE_FULL_RECIPE_INFO)
|
||||
val actual = appDb.recipeDao().queryFullRecipeInfo("2")
|
||||
assertThat(actual).isEqualTo(FULL_PORRIDGE_INFO_ENTITY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when saveRecipeInfo secondly then overwrites ingredients`() = runTest {
|
||||
subject.saveRecipes(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
|
||||
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
|
||||
val newRecipe = CAKE_FULL_RECIPE_INFO.copy(recipeIngredients = listOf(BREAD_INGREDIENT))
|
||||
subject.saveRecipeInfo(newRecipe)
|
||||
val actual = appDb.recipeDao().queryFullRecipeInfo("1")?.recipeIngredients
|
||||
val expected = listOf(CAKE_BREAD_RECIPE_INGREDIENT_ENTITY.copy(localId = 3))
|
||||
assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when saveRecipeInfo secondly then overwrites instructions`() = runTest {
|
||||
subject.saveRecipes(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
|
||||
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
|
||||
val newRecipe = CAKE_FULL_RECIPE_INFO.copy(recipeInstructions = listOf(MIX_INSTRUCTION))
|
||||
subject.saveRecipeInfo(newRecipe)
|
||||
val actual = appDb.recipeDao().queryFullRecipeInfo("1")?.recipeInstructions
|
||||
val expected = listOf(MIX_CAKE_RECIPE_INSTRUCTION_ENTITY.copy(localId = 3))
|
||||
assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@ package gq.kirmanak.mealient.data.recipes.impl
|
||||
import androidx.paging.PagingSource
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
|
||||
import gq.kirmanak.mealient.database.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.database.PORRIDGE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.database.TEST_RECIPE_SUMMARY_ENTITIES
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.test.HiltRobolectricTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARY_ENTITIES
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
@@ -3,13 +3,20 @@ package gq.kirmanak.mealient.data.recipes.impl
|
||||
import androidx.paging.LoadType
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import gq.kirmanak.mealient.database.BAKE_CAKE_RECIPE_INSTRUCTION_ENTITY
|
||||
import gq.kirmanak.mealient.database.CAKE_BREAD_RECIPE_INGREDIENT_ENTITY
|
||||
import gq.kirmanak.mealient.database.CAKE_RECIPE_ENTITY
|
||||
import gq.kirmanak.mealient.database.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.database.CAKE_SUGAR_RECIPE_INGREDIENT_ENTITY
|
||||
import gq.kirmanak.mealient.database.FULL_CAKE_INFO_ENTITY
|
||||
import gq.kirmanak.mealient.database.MIX_CAKE_RECIPE_INSTRUCTION_ENTITY
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.datasource.NetworkError.Unauthorized
|
||||
import gq.kirmanak.mealient.datasource_test.CAKE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapperImpl
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.FULL_CAKE_INFO_ENTITY
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.coVerifyOrder
|
||||
@@ -36,12 +43,21 @@ class RecipeRepoTest : BaseUnitTest() {
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var pagingSourceFactory: RecipePagingSourceFactory
|
||||
|
||||
private val modelMapper: ModelMapper = ModelMapperImpl()
|
||||
|
||||
lateinit var subject: RecipeRepo
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
subject = RecipeRepoImpl(remoteMediator, storage, pagingSourceFactory, dataSource, logger)
|
||||
subject = RecipeRepoImpl(
|
||||
remoteMediator,
|
||||
storage,
|
||||
pagingSourceFactory,
|
||||
dataSource,
|
||||
logger,
|
||||
modelMapper,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -55,7 +71,18 @@ class RecipeRepoTest : BaseUnitTest() {
|
||||
fun `when refreshRecipeInfo expect call to storage`() = runTest {
|
||||
coEvery { dataSource.requestRecipeInfo(eq("cake")) } returns CAKE_FULL_RECIPE_INFO
|
||||
subject.refreshRecipeInfo("cake")
|
||||
coVerify { storage.saveRecipeInfo(eq(CAKE_FULL_RECIPE_INFO)) }
|
||||
coVerify {
|
||||
storage.saveRecipeInfo(
|
||||
eq(CAKE_RECIPE_ENTITY),
|
||||
eq(
|
||||
listOf(
|
||||
CAKE_SUGAR_RECIPE_INGREDIENT_ENTITY,
|
||||
CAKE_BREAD_RECIPE_INGREDIENT_ENTITY
|
||||
)
|
||||
),
|
||||
eq(listOf(MIX_CAKE_RECIPE_INSTRUCTION_ENTITY, BAKE_CAKE_RECIPE_INSTRUCTION_ENTITY))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -3,13 +3,15 @@ package gq.kirmanak.mealient.data.recipes.impl
|
||||
import androidx.paging.*
|
||||
import androidx.paging.LoadType.*
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import gq.kirmanak.mealient.database.TEST_RECIPE_SUMMARY_ENTITIES
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.datasource.NetworkError.Unauthorized
|
||||
import gq.kirmanak.mealient.datasource_test.TEST_RECIPE_SUMMARIES
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapperImpl
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARIES
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARY_ENTITIES
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.impl.annotations.MockK
|
||||
@@ -41,6 +43,8 @@ class RecipesRemoteMediatorTest : BaseUnitTest() {
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var pagingSourceFactory: RecipePagingSourceFactory
|
||||
|
||||
private val modelMapper: ModelMapper = ModelMapperImpl()
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
@@ -50,6 +54,7 @@ class RecipesRemoteMediatorTest : BaseUnitTest() {
|
||||
pagingSourceFactory = pagingSourceFactory,
|
||||
logger = logger,
|
||||
dispatchers = dispatchers,
|
||||
modelMapper = modelMapper,
|
||||
)
|
||||
coEvery { dataSource.getFavoriteRecipes() } returns emptyList()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.ParseRecipeURLInfo
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package gq.kirmanak.mealient.extensions
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class FlowExtensionsKtTest : BaseUnitTest() {
|
||||
|
||||
@Test
|
||||
fun `when flow has an update then valueUpdatesOnly sends updated value`() = runTest {
|
||||
val flow = flowOf(1, 2)
|
||||
assertThat(flow.valueUpdatesOnly().toList()).isEqualTo(listOf(2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when flow has repeated values then valueUpdatesOnly sends updated value`() = runTest {
|
||||
val flow = flowOf(1, 1, 1, 2)
|
||||
assertThat(flow.valueUpdatesOnly().toList()).isEqualTo(listOf(2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when flow has one value then valueUpdatesOnly is empty`() = runTest {
|
||||
val flow = flowOf(1)
|
||||
assertThat(flow.valueUpdatesOnly().toList()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when flow has two updates then valueUpdatesOnly sends both`() = runTest {
|
||||
val flow = flowOf(1, 2, 1)
|
||||
assertThat(flow.valueUpdatesOnly().toList()).isEqualTo(listOf(2, 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when flow has three updates then valueUpdatesOnly sends all`() = runTest {
|
||||
val flow = flowOf(1, 2, 1, 3)
|
||||
assertThat(flow.valueUpdatesOnly().toList()).isEqualTo(listOf(2, 1, 3))
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
package gq.kirmanak.mealient.extensions
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_SUGAR_RECIPE_INGREDIENT_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MILK_RECIPE_INGREDIENT_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MILK_RECIPE_INGREDIENT_RESPONSE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MILK_RECIPE_INGREDIENT_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_CAKE_RECIPE_INSTRUCTION_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_INSTRUCTION
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_RECIPE_INSTRUCTION_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_RECIPE_INSTRUCTION_RESPONSE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_RECIPE_INSTRUCTION_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_ADD_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_ADD_RECIPE_REQUEST_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_CREATE_RECIPE_REQUEST_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_FULL_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_DRAFT
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_RESPONSE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_RESPONSE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_RESPONSE_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_UPDATE_RECIPE_REQUEST_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.RECIPE_SUMMARY_PORRIDGE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.RECIPE_SUMMARY_PORRIDGE_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.SUGAR_INGREDIENT
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.VERSION_INFO_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.VERSION_INFO_V1
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.VERSION_RESPONSE_V0
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.VERSION_RESPONSE_V1
|
||||
import org.junit.Test
|
||||
|
||||
class ModelMappingsTest : BaseUnitTest() {
|
||||
|
||||
@Test
|
||||
fun `when toAddRecipeRequest then fills fields correctly`() {
|
||||
assertThat(PORRIDGE_RECIPE_DRAFT.toAddRecipeInfo()).isEqualTo(PORRIDGE_ADD_RECIPE_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when toDraft then fills fields correctly`() {
|
||||
assertThat(PORRIDGE_ADD_RECIPE_INFO.toDraft()).isEqualTo(PORRIDGE_RECIPE_DRAFT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when full recipe info to entity expect correct entity`() {
|
||||
assertThat(CAKE_FULL_RECIPE_INFO.toRecipeEntity()).isEqualTo(CAKE_RECIPE_ENTITY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when ingredient info to entity expect correct entity`() {
|
||||
val actual = SUGAR_INGREDIENT.toRecipeIngredientEntity("1")
|
||||
assertThat(actual).isEqualTo(CAKE_SUGAR_RECIPE_INGREDIENT_ENTITY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when instruction info to entity expect correct entity`() {
|
||||
val actual = MIX_INSTRUCTION.toRecipeInstructionEntity("1")
|
||||
assertThat(actual).isEqualTo(MIX_CAKE_RECIPE_INSTRUCTION_ENTITY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when summary v0 to info expect correct info`() {
|
||||
val actual = PORRIDGE_RECIPE_SUMMARY_RESPONSE_V0.toRecipeSummaryInfo()
|
||||
assertThat(actual).isEqualTo(RECIPE_SUMMARY_PORRIDGE_V0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when summary v1 to info expect correct info`() {
|
||||
val actual = PORRIDGE_RECIPE_SUMMARY_RESPONSE_V1.toRecipeSummaryInfo()
|
||||
assertThat(actual).isEqualTo(RECIPE_SUMMARY_PORRIDGE_V1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when summary info to entity expect correct entity`() {
|
||||
val actual = RECIPE_SUMMARY_PORRIDGE_V0.toRecipeSummaryEntity(isFavorite = false)
|
||||
assertThat(actual).isEqualTo(PORRIDGE_RECIPE_SUMMARY_ENTITY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when version response v0 to info expect correct info`() {
|
||||
assertThat(VERSION_RESPONSE_V0.toVersionInfo()).isEqualTo(VERSION_INFO_V0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when version response v1 to info expect correct info`() {
|
||||
assertThat(VERSION_RESPONSE_V1.toVersionInfo()).isEqualTo(VERSION_INFO_V1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when recipe ingredient response v0 to info expect correct info`() {
|
||||
val actual = MILK_RECIPE_INGREDIENT_RESPONSE_V0.toRecipeIngredientInfo()
|
||||
assertThat(actual).isEqualTo(MILK_RECIPE_INGREDIENT_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when recipe ingredient response v1 to info expect correct info`() {
|
||||
val actual = MILK_RECIPE_INGREDIENT_RESPONSE_V1.toRecipeIngredientInfo()
|
||||
assertThat(actual).isEqualTo(MILK_RECIPE_INGREDIENT_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when recipe instruction response v0 to info expect correct info`() {
|
||||
val actual = MIX_RECIPE_INSTRUCTION_RESPONSE_V0.toRecipeInstructionInfo()
|
||||
assertThat(actual).isEqualTo(MIX_RECIPE_INSTRUCTION_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when recipe instruction response v1 to info expect correct info`() {
|
||||
val actual = MIX_RECIPE_INSTRUCTION_RESPONSE_V1.toRecipeInstructionInfo()
|
||||
assertThat(actual).isEqualTo(MIX_RECIPE_INSTRUCTION_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when recipe response v0 to info expect correct info`() {
|
||||
val actual = PORRIDGE_RECIPE_RESPONSE_V0.toFullRecipeInfo()
|
||||
assertThat(actual).isEqualTo(PORRIDGE_FULL_RECIPE_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when recipe response v1 to info expect correct info`() {
|
||||
val actual = PORRIDGE_RECIPE_RESPONSE_V1.toFullRecipeInfo()
|
||||
assertThat(actual).isEqualTo(PORRIDGE_FULL_RECIPE_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when add recipe info to request v0 expect correct request`() {
|
||||
val actual = PORRIDGE_ADD_RECIPE_INFO.toV0Request()
|
||||
assertThat(actual).isEqualTo(PORRIDGE_ADD_RECIPE_REQUEST_V0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when add recipe info to create request v1 expect correct request`() {
|
||||
val actual = PORRIDGE_ADD_RECIPE_INFO.toV1CreateRequest()
|
||||
assertThat(actual).isEqualTo(PORRIDGE_CREATE_RECIPE_REQUEST_V1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when add recipe info to update request v1 expect correct request`() {
|
||||
val actual = PORRIDGE_ADD_RECIPE_INFO.toV1UpdateRequest()
|
||||
assertThat(actual).isEqualTo(PORRIDGE_UPDATE_RECIPE_REQUEST_V1)
|
||||
}
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
package gq.kirmanak.mealient.test
|
||||
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeIngredientInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeInstructionInfo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeSettingsInfo
|
||||
import gq.kirmanak.mealient.data.baseurl.VersionInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.FullRecipeInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeIngredientInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeInstructionInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeSettingsInfo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeSummaryInfo
|
||||
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeInstructionEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeIngredientV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeInstructionV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeSettingsV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeIngredientResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeInstructionResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.VersionResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v1.models.AddRecipeIngredientV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.AddRecipeInstructionV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.AddRecipeSettingsV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeIngredientResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeInstructionResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSettingsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeDraft
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
|
||||
object RecipeImplTestData {
|
||||
val RECIPE_SUMMARY_CAKE = RecipeSummaryInfo(
|
||||
remoteId = "1",
|
||||
name = "Cake",
|
||||
slug = "cake",
|
||||
description = "A tasty cake",
|
||||
dateAdded = LocalDate.parse("2021-11-13"),
|
||||
dateUpdated = LocalDateTime.parse("2021-11-13T15:30:13"),
|
||||
imageId = "cake",
|
||||
)
|
||||
|
||||
val RECIPE_SUMMARY_PORRIDGE_V0 = RecipeSummaryInfo(
|
||||
remoteId = "2",
|
||||
name = "Porridge",
|
||||
slug = "porridge",
|
||||
description = "A tasty porridge",
|
||||
dateAdded = LocalDate.parse("2021-11-12"),
|
||||
dateUpdated = LocalDateTime.parse("2021-10-13T17:35:23"),
|
||||
imageId = "porridge",
|
||||
)
|
||||
|
||||
val RECIPE_SUMMARY_PORRIDGE_V1 = RecipeSummaryInfo(
|
||||
remoteId = "2",
|
||||
name = "Porridge",
|
||||
slug = "porridge",
|
||||
description = "A tasty porridge",
|
||||
dateAdded = LocalDate.parse("2021-11-12"),
|
||||
dateUpdated = LocalDateTime.parse("2021-10-13T17:35:23"),
|
||||
imageId = "2",
|
||||
)
|
||||
|
||||
val TEST_RECIPE_SUMMARIES = listOf(RECIPE_SUMMARY_CAKE, RECIPE_SUMMARY_PORRIDGE_V0)
|
||||
|
||||
val CAKE_RECIPE_SUMMARY_ENTITY = RecipeSummaryEntity(
|
||||
remoteId = "1",
|
||||
name = "Cake",
|
||||
slug = "cake",
|
||||
description = "A tasty cake",
|
||||
dateAdded = LocalDate.parse("2021-11-13"),
|
||||
dateUpdated = LocalDateTime.parse("2021-11-13T15:30:13"),
|
||||
imageId = "cake",
|
||||
isFavorite = false,
|
||||
)
|
||||
|
||||
val PORRIDGE_RECIPE_SUMMARY_ENTITY = RecipeSummaryEntity(
|
||||
remoteId = "2",
|
||||
name = "Porridge",
|
||||
slug = "porridge",
|
||||
description = "A tasty porridge",
|
||||
dateAdded = LocalDate.parse("2021-11-12"),
|
||||
dateUpdated = LocalDateTime.parse("2021-10-13T17:35:23"),
|
||||
imageId = "porridge",
|
||||
isFavorite = false,
|
||||
)
|
||||
|
||||
val TEST_RECIPE_SUMMARY_ENTITIES =
|
||||
listOf(CAKE_RECIPE_SUMMARY_ENTITY, PORRIDGE_RECIPE_SUMMARY_ENTITY)
|
||||
|
||||
val SUGAR_INGREDIENT = RecipeIngredientInfo(
|
||||
note = "2 oz of white sugar",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
val BREAD_INGREDIENT = RecipeIngredientInfo(
|
||||
note = "2 oz of white bread",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
private val MILK_INGREDIENT = RecipeIngredientInfo(
|
||||
note = "2 oz of white milk",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
val MIX_INSTRUCTION = RecipeInstructionInfo(
|
||||
text = "Mix the ingredients"
|
||||
)
|
||||
|
||||
private val BAKE_INSTRUCTION = RecipeInstructionInfo(
|
||||
text = "Bake the ingredients"
|
||||
)
|
||||
|
||||
private val BOIL_INSTRUCTION = RecipeInstructionInfo(
|
||||
text = "Boil the ingredients"
|
||||
)
|
||||
|
||||
val CAKE_FULL_RECIPE_INFO = FullRecipeInfo(
|
||||
remoteId = "1",
|
||||
name = "Cake",
|
||||
recipeYield = "4 servings",
|
||||
recipeIngredients = listOf(SUGAR_INGREDIENT, BREAD_INGREDIENT),
|
||||
recipeInstructions = listOf(MIX_INSTRUCTION, BAKE_INSTRUCTION),
|
||||
settings = RecipeSettingsInfo(disableAmounts = true)
|
||||
)
|
||||
|
||||
val PORRIDGE_FULL_RECIPE_INFO = FullRecipeInfo(
|
||||
remoteId = "2",
|
||||
name = "Porridge",
|
||||
recipeYield = "3 servings",
|
||||
recipeIngredients = listOf(SUGAR_INGREDIENT, MILK_INGREDIENT),
|
||||
recipeInstructions = listOf(MIX_INSTRUCTION, BOIL_INSTRUCTION),
|
||||
settings = RecipeSettingsInfo(disableAmounts = true)
|
||||
)
|
||||
|
||||
val MIX_CAKE_RECIPE_INSTRUCTION_ENTITY = RecipeInstructionEntity(
|
||||
recipeId = "1",
|
||||
text = "Mix the ingredients",
|
||||
)
|
||||
|
||||
private val BAKE_CAKE_RECIPE_INSTRUCTION_ENTITY = RecipeInstructionEntity(
|
||||
recipeId = "1",
|
||||
text = "Bake the ingredients",
|
||||
)
|
||||
|
||||
val CAKE_RECIPE_ENTITY = RecipeEntity(
|
||||
remoteId = "1",
|
||||
recipeYield = "4 servings",
|
||||
disableAmounts = true,
|
||||
)
|
||||
|
||||
val CAKE_SUGAR_RECIPE_INGREDIENT_ENTITY = RecipeIngredientEntity(
|
||||
recipeId = "1",
|
||||
note = "2 oz of white sugar",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
val CAKE_BREAD_RECIPE_INGREDIENT_ENTITY = RecipeIngredientEntity(
|
||||
recipeId = "1",
|
||||
note = "2 oz of white bread",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
val FULL_CAKE_INFO_ENTITY = FullRecipeEntity(
|
||||
recipeEntity = CAKE_RECIPE_ENTITY,
|
||||
recipeSummaryEntity = CAKE_RECIPE_SUMMARY_ENTITY,
|
||||
recipeIngredients = listOf(
|
||||
CAKE_SUGAR_RECIPE_INGREDIENT_ENTITY,
|
||||
CAKE_BREAD_RECIPE_INGREDIENT_ENTITY,
|
||||
),
|
||||
recipeInstructions = listOf(
|
||||
MIX_CAKE_RECIPE_INSTRUCTION_ENTITY,
|
||||
BAKE_CAKE_RECIPE_INSTRUCTION_ENTITY,
|
||||
),
|
||||
)
|
||||
|
||||
private val PORRIDGE_RECIPE_ENTITY_FULL = RecipeEntity(
|
||||
remoteId = "2",
|
||||
recipeYield = "3 servings",
|
||||
disableAmounts = true,
|
||||
)
|
||||
|
||||
private val PORRIDGE_MILK_RECIPE_INGREDIENT_ENTITY = RecipeIngredientEntity(
|
||||
recipeId = "2",
|
||||
note = "2 oz of white milk",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
private val PORRIDGE_SUGAR_RECIPE_INGREDIENT_ENTITY = RecipeIngredientEntity(
|
||||
recipeId = "2",
|
||||
note = "2 oz of white sugar",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
private val PORRIDGE_MIX_RECIPE_INSTRUCTION_ENTITY = RecipeInstructionEntity(
|
||||
recipeId = "2",
|
||||
text = "Mix the ingredients"
|
||||
)
|
||||
|
||||
private val PORRIDGE_BOIL_RECIPE_INSTRUCTION_ENTITY = RecipeInstructionEntity(
|
||||
recipeId = "2",
|
||||
text = "Boil the ingredients"
|
||||
)
|
||||
|
||||
val FULL_PORRIDGE_INFO_ENTITY = FullRecipeEntity(
|
||||
recipeEntity = PORRIDGE_RECIPE_ENTITY_FULL,
|
||||
recipeSummaryEntity = PORRIDGE_RECIPE_SUMMARY_ENTITY,
|
||||
recipeIngredients = listOf(
|
||||
PORRIDGE_SUGAR_RECIPE_INGREDIENT_ENTITY,
|
||||
PORRIDGE_MILK_RECIPE_INGREDIENT_ENTITY,
|
||||
),
|
||||
recipeInstructions = listOf(
|
||||
PORRIDGE_MIX_RECIPE_INSTRUCTION_ENTITY,
|
||||
PORRIDGE_BOIL_RECIPE_INSTRUCTION_ENTITY,
|
||||
)
|
||||
)
|
||||
|
||||
val SUGAR_ADD_RECIPE_INGREDIENT_INFO = AddRecipeIngredientInfo("2 oz of white sugar")
|
||||
|
||||
val MILK_ADD_RECIPE_INGREDIENT_INFO = AddRecipeIngredientInfo("2 oz of white milk")
|
||||
|
||||
val BOIL_ADD_RECIPE_INSTRUCTION_INFO = AddRecipeInstructionInfo("Boil the ingredients")
|
||||
|
||||
val MIX_ADD_RECIPE_INSTRUCTION_INFO = AddRecipeInstructionInfo("Mix the ingredients")
|
||||
|
||||
val ADD_RECIPE_INFO_SETTINGS = AddRecipeSettingsInfo(disableComments = false, public = true)
|
||||
|
||||
val PORRIDGE_ADD_RECIPE_INFO = AddRecipeInfo(
|
||||
name = "Porridge",
|
||||
description = "A tasty porridge",
|
||||
recipeYield = "3 servings",
|
||||
recipeIngredient = listOf(
|
||||
MILK_ADD_RECIPE_INGREDIENT_INFO,
|
||||
SUGAR_ADD_RECIPE_INGREDIENT_INFO,
|
||||
),
|
||||
recipeInstructions = listOf(
|
||||
MIX_ADD_RECIPE_INSTRUCTION_INFO,
|
||||
BOIL_ADD_RECIPE_INSTRUCTION_INFO,
|
||||
),
|
||||
settings = ADD_RECIPE_INFO_SETTINGS,
|
||||
)
|
||||
|
||||
val PORRIDGE_RECIPE_DRAFT = AddRecipeDraft(
|
||||
recipeName = "Porridge",
|
||||
recipeDescription = "A tasty porridge",
|
||||
recipeYield = "3 servings",
|
||||
recipeInstructions = listOf("Mix the ingredients", "Boil the ingredients"),
|
||||
recipeIngredients = listOf("2 oz of white milk", "2 oz of white sugar"),
|
||||
isRecipePublic = true,
|
||||
areCommentsDisabled = false,
|
||||
)
|
||||
|
||||
val PORRIDGE_RECIPE_SUMMARY_RESPONSE_V0 = GetRecipeSummaryResponseV0(
|
||||
remoteId = 2,
|
||||
name = "Porridge",
|
||||
slug = "porridge",
|
||||
description = "A tasty porridge",
|
||||
dateAdded = LocalDate.parse("2021-11-12"),
|
||||
dateUpdated = LocalDateTime.parse("2021-10-13T17:35:23"),
|
||||
)
|
||||
|
||||
val PORRIDGE_RECIPE_SUMMARY_RESPONSE_V1 = GetRecipeSummaryResponseV1(
|
||||
remoteId = "2",
|
||||
name = "Porridge",
|
||||
slug = "porridge",
|
||||
description = "A tasty porridge",
|
||||
dateAdded = LocalDate.parse("2021-11-12"),
|
||||
dateUpdated = LocalDateTime.parse("2021-10-13T17:35:23"),
|
||||
)
|
||||
|
||||
val VERSION_RESPONSE_V0 = VersionResponseV0("v0.5.6")
|
||||
|
||||
val VERSION_INFO_V0 = VersionInfo("v0.5.6")
|
||||
|
||||
val VERSION_RESPONSE_V1 = VersionResponseV1("v1.0.0-beta05")
|
||||
|
||||
val VERSION_INFO_V1 = VersionInfo("v1.0.0-beta05")
|
||||
|
||||
val MILK_RECIPE_INGREDIENT_RESPONSE_V0 = GetRecipeIngredientResponseV0("2 oz of white milk")
|
||||
|
||||
val SUGAR_RECIPE_INGREDIENT_RESPONSE_V0 = GetRecipeIngredientResponseV0("2 oz of white sugar")
|
||||
|
||||
val MILK_RECIPE_INGREDIENT_RESPONSE_V1 = GetRecipeIngredientResponseV1(
|
||||
note = "2 oz of white milk",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
val SUGAR_RECIPE_INGREDIENT_RESPONSE_V1 = GetRecipeIngredientResponseV1(
|
||||
note = "2 oz of white sugar",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
val MILK_RECIPE_INGREDIENT_INFO = RecipeIngredientInfo(
|
||||
note = "2 oz of white milk",
|
||||
quantity = 1.0,
|
||||
unit = null,
|
||||
food = null,
|
||||
title = null,
|
||||
)
|
||||
|
||||
val MIX_RECIPE_INSTRUCTION_RESPONSE_V0 = GetRecipeInstructionResponseV0("Mix the ingredients")
|
||||
|
||||
val BOIL_RECIPE_INSTRUCTION_RESPONSE_V0 = GetRecipeInstructionResponseV0("Boil the ingredients")
|
||||
|
||||
val MIX_RECIPE_INSTRUCTION_RESPONSE_V1 = GetRecipeInstructionResponseV1("Mix the ingredients")
|
||||
|
||||
val BOIL_RECIPE_INSTRUCTION_RESPONSE_V1 = GetRecipeInstructionResponseV1("Boil the ingredients")
|
||||
|
||||
val MIX_RECIPE_INSTRUCTION_INFO = RecipeInstructionInfo("Mix the ingredients")
|
||||
|
||||
val PORRIDGE_RECIPE_RESPONSE_V0 = GetRecipeResponseV0(
|
||||
remoteId = 2,
|
||||
name = "Porridge",
|
||||
recipeYield = "3 servings",
|
||||
recipeIngredients = listOf(
|
||||
SUGAR_RECIPE_INGREDIENT_RESPONSE_V0,
|
||||
MILK_RECIPE_INGREDIENT_RESPONSE_V0,
|
||||
),
|
||||
recipeInstructions = listOf(
|
||||
MIX_RECIPE_INSTRUCTION_RESPONSE_V0,
|
||||
BOIL_RECIPE_INSTRUCTION_RESPONSE_V0
|
||||
),
|
||||
)
|
||||
|
||||
val PORRIDGE_RECIPE_RESPONSE_V1 = GetRecipeResponseV1(
|
||||
remoteId = "2",
|
||||
name = "Porridge",
|
||||
recipeYield = "3 servings",
|
||||
recipeIngredients = listOf(
|
||||
SUGAR_RECIPE_INGREDIENT_RESPONSE_V1,
|
||||
MILK_RECIPE_INGREDIENT_RESPONSE_V1,
|
||||
),
|
||||
recipeInstructions = listOf(
|
||||
MIX_RECIPE_INSTRUCTION_RESPONSE_V1,
|
||||
BOIL_RECIPE_INSTRUCTION_RESPONSE_V1
|
||||
),
|
||||
settings = GetRecipeSettingsResponseV1(disableAmount = true),
|
||||
)
|
||||
|
||||
val MIX_ADD_RECIPE_INSTRUCTION_REQUEST_V0 = AddRecipeInstructionV0("Mix the ingredients")
|
||||
|
||||
val BOIL_ADD_RECIPE_INSTRUCTION_REQUEST_V0 = AddRecipeInstructionV0("Boil the ingredients")
|
||||
|
||||
val SUGAR_ADD_RECIPE_INGREDIENT_REQUEST_V0 = AddRecipeIngredientV0("2 oz of white sugar")
|
||||
|
||||
val MILK_ADD_RECIPE_INGREDIENT_REQUEST_V0 = AddRecipeIngredientV0("2 oz of white milk")
|
||||
|
||||
val ADD_RECIPE_REQUEST_SETTINGS_V0 = AddRecipeSettingsV0(disableComments = false, public = true)
|
||||
|
||||
val PORRIDGE_ADD_RECIPE_REQUEST_V0 = AddRecipeRequestV0(
|
||||
name = "Porridge",
|
||||
description = "A tasty porridge",
|
||||
recipeYield = "3 servings",
|
||||
recipeInstructions = listOf(
|
||||
MIX_ADD_RECIPE_INSTRUCTION_REQUEST_V0,
|
||||
BOIL_ADD_RECIPE_INSTRUCTION_REQUEST_V0,
|
||||
),
|
||||
recipeIngredient = listOf(
|
||||
MILK_ADD_RECIPE_INGREDIENT_REQUEST_V0,
|
||||
SUGAR_ADD_RECIPE_INGREDIENT_REQUEST_V0,
|
||||
),
|
||||
settings = ADD_RECIPE_REQUEST_SETTINGS_V0
|
||||
)
|
||||
|
||||
val MIX_ADD_RECIPE_INSTRUCTION_REQUEST_V1 = AddRecipeInstructionV1(
|
||||
id = "1",
|
||||
text = "Mix the ingredients",
|
||||
ingredientReferences = emptyList()
|
||||
)
|
||||
|
||||
val BOIL_ADD_RECIPE_INSTRUCTION_REQUEST_V1 = AddRecipeInstructionV1(
|
||||
id = "2",
|
||||
text = "Boil the ingredients",
|
||||
ingredientReferences = emptyList()
|
||||
)
|
||||
|
||||
val SUGAR_ADD_RECIPE_INGREDIENT_REQUEST_V1 = AddRecipeIngredientV1(
|
||||
id = "3",
|
||||
note = "2 oz of white sugar"
|
||||
)
|
||||
|
||||
val MILK_ADD_RECIPE_INGREDIENT_REQUEST_V1 = AddRecipeIngredientV1(
|
||||
id = "4",
|
||||
note = "2 oz of white milk"
|
||||
)
|
||||
|
||||
val ADD_RECIPE_REQUEST_SETTINGS_V1 = AddRecipeSettingsV1(disableComments = false, public = true)
|
||||
|
||||
val PORRIDGE_CREATE_RECIPE_REQUEST_V1 = CreateRecipeRequestV1(name = "Porridge")
|
||||
|
||||
val PORRIDGE_UPDATE_RECIPE_REQUEST_V1 = UpdateRecipeRequestV1(
|
||||
description = "A tasty porridge",
|
||||
recipeYield = "3 servings",
|
||||
recipeInstructions = listOf(
|
||||
MIX_ADD_RECIPE_INSTRUCTION_REQUEST_V1,
|
||||
BOIL_ADD_RECIPE_INSTRUCTION_REQUEST_V1,
|
||||
),
|
||||
recipeIngredient = listOf(
|
||||
MILK_ADD_RECIPE_INGREDIENT_REQUEST_V1,
|
||||
SUGAR_ADD_RECIPE_INGREDIENT_REQUEST_V1,
|
||||
),
|
||||
settings = ADD_RECIPE_REQUEST_SETTINGS_V1
|
||||
)
|
||||
}
|
||||
@@ -6,10 +6,13 @@ import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_BASE_URL
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.ui.ActivityUiState
|
||||
import gq.kirmanak.mealient.ui.ActivityUiStateController
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@@ -28,6 +31,9 @@ class MainActivityViewModelTest : BaseUnitTest() {
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var recipeRepo: RecipeRepo
|
||||
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var activityUiStateController: ActivityUiStateController
|
||||
|
||||
private lateinit var subject: MainActivityViewModel
|
||||
|
||||
@Before
|
||||
@@ -36,12 +42,17 @@ class MainActivityViewModelTest : BaseUnitTest() {
|
||||
every { authRepo.isAuthorizedFlow } returns emptyFlow()
|
||||
coEvery { disclaimerStorage.isDisclaimerAccepted() } returns true
|
||||
coEvery { serverInfoRepo.getUrl() } returns TEST_BASE_URL
|
||||
every { activityUiStateController.getUiStateFlow() } returns MutableStateFlow(
|
||||
ActivityUiState()
|
||||
)
|
||||
coEvery { serverInfoRepo.versionUpdates() } returns emptyFlow()
|
||||
subject = MainActivityViewModel(
|
||||
authRepo = authRepo,
|
||||
logger = logger,
|
||||
disclaimerStorage = disclaimerStorage,
|
||||
serverInfoRepo = serverInfoRepo,
|
||||
recipeRepo = recipeRepo,
|
||||
activityUiStateController = activityUiStateController,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ package gq.kirmanak.mealient.ui.add
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.datasource_test.PORRIDGE_ADD_RECIPE_INFO
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_ADD_RECIPE_INFO
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.impl.annotations.MockK
|
||||
|
||||
@@ -5,6 +5,7 @@ import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.shopping_lists.repo.ShoppingListsRepo
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_BASE_URL
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
@@ -31,6 +32,9 @@ class BaseURLViewModelTest : BaseUnitTest() {
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var recipeRepo: RecipeRepo
|
||||
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var shoppingListsRepo: ShoppingListsRepo
|
||||
|
||||
lateinit var subject: BaseURLViewModel
|
||||
|
||||
@Before
|
||||
@@ -41,6 +45,7 @@ class BaseURLViewModelTest : BaseUnitTest() {
|
||||
authRepo = authRepo,
|
||||
recipeRepo = recipeRepo,
|
||||
logger = logger,
|
||||
shoppingListsRepo = shoppingListsRepo,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import androidx.lifecycle.asFlow
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.database.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
|
||||
@@ -3,8 +3,8 @@ package gq.kirmanak.mealient.ui.recipes.info
|
||||
import androidx.lifecycle.asFlow
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.database.FULL_CAKE_INFO_ENTITY
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.FULL_CAKE_INFO_ENTITY
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
Reference in New Issue
Block a user