Use intermediate representation for AddRecipe draft
This commit is contained in:
@@ -120,9 +120,6 @@ dependencies {
|
||||
implementation(libs.kirich1409.viewBinding)
|
||||
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(libs.androidx.datastore.datastore)
|
||||
|
||||
implementation(libs.androidx.security.crypto)
|
||||
|
||||
implementation(platform(libs.google.firebase.bom))
|
||||
implementation(libs.google.firebase.analyticsKtx)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.add
|
||||
|
||||
import gq.kirmanak.mealient.data.add.models.AddRecipeRequest
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AddRecipeStorage {
|
||||
|
||||
val updates: Flow<AddRecipeRequest>
|
||||
|
||||
suspend fun save(addRecipeRequest: AddRecipeRequest)
|
||||
|
||||
suspend fun clear()
|
||||
}
|
||||
@@ -2,10 +2,15 @@ package gq.kirmanak.mealient.data.add.impl
|
||||
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.data.add.models.AddRecipeIngredient
|
||||
import gq.kirmanak.mealient.data.add.models.AddRecipeInstruction
|
||||
import gq.kirmanak.mealient.data.add.models.AddRecipeRequest
|
||||
import gq.kirmanak.mealient.data.add.models.AddRecipeSettings
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeDraft
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorage
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -17,11 +22,32 @@ class AddRecipeRepoImpl @Inject constructor(
|
||||
) : AddRecipeRepo {
|
||||
|
||||
override val addRecipeRequestFlow: Flow<AddRecipeRequest>
|
||||
get() = addRecipeStorage.updates
|
||||
get() = addRecipeStorage.updates.map { it ->
|
||||
AddRecipeRequest(
|
||||
name = it.recipeName,
|
||||
description = it.recipeDescription,
|
||||
recipeYield = it.recipeYield,
|
||||
recipeIngredient = it.recipeIngredients.map { AddRecipeIngredient(note = it) },
|
||||
recipeInstructions = it.recipeInstructions.map { AddRecipeInstruction(text = it) },
|
||||
settings = AddRecipeSettings(
|
||||
public = it.isRecipePublic,
|
||||
disableComments = it.areCommentsDisabled,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun preserve(recipe: AddRecipeRequest) {
|
||||
Timber.v("preserveRecipe() called with: recipe = $recipe")
|
||||
addRecipeStorage.save(recipe)
|
||||
val input = AddRecipeDraft(
|
||||
recipeName = recipe.name,
|
||||
recipeDescription = recipe.description,
|
||||
recipeYield = recipe.recipeYield,
|
||||
recipeInstructions = recipe.recipeInstructions.map { it.text },
|
||||
recipeIngredients = recipe.recipeIngredient.map { it.note },
|
||||
isRecipePublic = recipe.settings.public,
|
||||
areCommentsDisabled = recipe.settings.disableComments,
|
||||
)
|
||||
addRecipeStorage.save(input)
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.add.impl
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.data.add.models.AddRecipeRequest
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeInput
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AddRecipeStorageImpl @Inject constructor(
|
||||
private val dataStore: DataStore<AddRecipeInput>,
|
||||
) : AddRecipeStorage {
|
||||
|
||||
override val updates: Flow<AddRecipeRequest>
|
||||
get() = dataStore.data.map { AddRecipeRequest(it) }
|
||||
|
||||
override suspend fun save(addRecipeRequest: AddRecipeRequest) {
|
||||
Timber.v("saveRecipeInput() called with: addRecipeRequest = $addRecipeRequest")
|
||||
dataStore.updateData { addRecipeRequest.toInput() }
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
Timber.v("clearRecipeInput() called")
|
||||
dataStore.updateData { AddRecipeInput.getDefaultInstance() }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.add.models
|
||||
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeInput
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeDraft
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -21,27 +21,27 @@ data class AddRecipeRequest(
|
||||
@SerialName("assets") val assets: List<String> = emptyList(),
|
||||
@SerialName("settings") val settings: AddRecipeSettings = AddRecipeSettings(),
|
||||
) {
|
||||
constructor(input: AddRecipeInput) : this(
|
||||
constructor(input: AddRecipeDraft) : this(
|
||||
name = input.recipeName,
|
||||
description = input.recipeDescription,
|
||||
recipeYield = input.recipeYield,
|
||||
recipeIngredient = input.recipeIngredientsList.map { AddRecipeIngredient(note = it) },
|
||||
recipeInstructions = input.recipeInstructionsList.map { AddRecipeInstruction(text = it) },
|
||||
recipeIngredient = input.recipeIngredients.map { AddRecipeIngredient(note = it) },
|
||||
recipeInstructions = input.recipeInstructions.map { AddRecipeInstruction(text = it) },
|
||||
settings = AddRecipeSettings(
|
||||
public = input.isRecipePublic,
|
||||
disableComments = input.areCommentsDisabled,
|
||||
)
|
||||
)
|
||||
|
||||
fun toInput(): AddRecipeInput = AddRecipeInput.newBuilder()
|
||||
.setRecipeName(name)
|
||||
.setRecipeDescription(description)
|
||||
.setRecipeYield(recipeYield)
|
||||
.setIsRecipePublic(settings.public)
|
||||
.setAreCommentsDisabled(settings.disableComments)
|
||||
.addAllRecipeIngredients(recipeIngredient.map { it.note })
|
||||
.addAllRecipeInstructions(recipeInstructions.map { it.text })
|
||||
.build()
|
||||
fun 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,
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -4,7 +4,7 @@ import android.content.SharedPreferences
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.content.edit
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.di.AuthModule.Companion.ENCRYPTED
|
||||
import gq.kirmanak.mealient.datastore.DataStoreModule.Companion.ENCRYPTED
|
||||
import gq.kirmanak.mealient.extensions.prefsChangeFlow
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@@ -7,15 +7,15 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.data.add.impl.AddRecipeDataSourceImpl
|
||||
import gq.kirmanak.mealient.data.add.impl.AddRecipeRepoImpl
|
||||
import gq.kirmanak.mealient.data.add.impl.AddRecipeService
|
||||
import gq.kirmanak.mealient.data.add.impl.AddRecipeStorageImpl
|
||||
import gq.kirmanak.mealient.data.baseurl.BaseURLStorage
|
||||
import gq.kirmanak.mealient.data.network.RetrofitBuilder
|
||||
import gq.kirmanak.mealient.data.network.ServiceFactory
|
||||
import gq.kirmanak.mealient.data.network.createServiceFactory
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorageImpl
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Named
|
||||
|
||||
@@ -2,9 +2,6 @@ package gq.kirmanak.mealient.di
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKeys
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
@@ -32,7 +29,6 @@ import javax.inject.Singleton
|
||||
interface AuthModule {
|
||||
|
||||
companion object {
|
||||
const val ENCRYPTED = "encrypted"
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@@ -49,23 +45,6 @@ interface AuthModule {
|
||||
fun provideAccountManager(@ApplicationContext context: Context): AccountManager {
|
||||
return AccountManager.get(context)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(ENCRYPTED)
|
||||
fun provideEncryptedSharedPreferences(
|
||||
@ApplicationContext applicationContext: Context,
|
||||
): SharedPreferences {
|
||||
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
|
||||
val mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
|
||||
return EncryptedSharedPreferences.create(
|
||||
ENCRYPTED,
|
||||
mainKeyAlias,
|
||||
applicationContext,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "gq.kirmanak.mealient.data.add.models";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message AddRecipeInput {
|
||||
string recipeName = 1;
|
||||
string recipeDescription = 2;
|
||||
string recipeYield = 3;
|
||||
repeated string recipeInstructions = 4;
|
||||
repeated string recipeIngredients = 5;
|
||||
bool isRecipePublic = 6;
|
||||
bool areCommentsDisabled = 7;
|
||||
}
|
||||
@@ -1,21 +1,22 @@
|
||||
package gq.kirmanak.mealient.data.add.models
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeDraft
|
||||
import org.junit.Test
|
||||
|
||||
class AddRecipeRequestTest {
|
||||
|
||||
@Test
|
||||
fun `when construct from input then fills fields correctly`() {
|
||||
val input = AddRecipeInput.newBuilder()
|
||||
.setRecipeName("Recipe name")
|
||||
.setRecipeDescription("Recipe description")
|
||||
.setRecipeYield("Recipe yield")
|
||||
.addAllRecipeIngredients(listOf("Recipe ingredient 1", "Recipe ingredient 2"))
|
||||
.addAllRecipeInstructions(listOf("Recipe instruction 1", "Recipe instruction 2"))
|
||||
.setIsRecipePublic(false)
|
||||
.setAreCommentsDisabled(true)
|
||||
.build()
|
||||
val input = AddRecipeDraft(
|
||||
recipeName = "Recipe name",
|
||||
recipeDescription = "Recipe description",
|
||||
recipeYield = "Recipe yield",
|
||||
recipeInstructions = listOf("Recipe instruction 1", "Recipe instruction 2"),
|
||||
recipeIngredients = listOf("Recipe ingredient 1", "Recipe ingredient 2"),
|
||||
isRecipePublic = false,
|
||||
areCommentsDisabled = true,
|
||||
)
|
||||
|
||||
val expected = AddRecipeRequest(
|
||||
name = "Recipe name",
|
||||
@@ -58,16 +59,16 @@ class AddRecipeRequestTest {
|
||||
)
|
||||
)
|
||||
|
||||
val expected = AddRecipeInput.newBuilder()
|
||||
.setRecipeName("Recipe name")
|
||||
.setRecipeDescription("Recipe description")
|
||||
.setRecipeYield("Recipe yield")
|
||||
.addAllRecipeIngredients(listOf("Recipe ingredient 1", "Recipe ingredient 2"))
|
||||
.addAllRecipeInstructions(listOf("Recipe instruction 1", "Recipe instruction 2"))
|
||||
.setIsRecipePublic(false)
|
||||
.setAreCommentsDisabled(true)
|
||||
.build()
|
||||
val expected = AddRecipeDraft(
|
||||
recipeName = "Recipe name",
|
||||
recipeDescription = "Recipe description",
|
||||
recipeYield = "Recipe yield",
|
||||
recipeInstructions = listOf("Recipe instruction 1", "Recipe instruction 2"),
|
||||
recipeIngredients = listOf("Recipe ingredient 1", "Recipe ingredient 2"),
|
||||
isRecipePublic = false,
|
||||
areCommentsDisabled = true,
|
||||
)
|
||||
|
||||
assertThat(request.toInput()).isEqualTo(expected)
|
||||
assertThat(request.toDraft()).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user