Implement saving recipes by URLs
This commit is contained in:
@@ -19,14 +19,27 @@
|
||||
tools:ignore="UnusedAttribute">
|
||||
<activity
|
||||
android:name=".ui.activity.MainActivity"
|
||||
android:windowSoftInputMode="adjustPan"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustPan">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.share.ShareRecipeActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.service.chooser.chooser_target_service"
|
||||
android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -8,11 +8,18 @@ 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.NetworkError
|
||||
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.*
|
||||
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.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -24,7 +31,7 @@ class MealieDataSourceWrapper @Inject constructor(
|
||||
private val v0Source: MealieDataSourceV0,
|
||||
private val v1Source: MealieDataSourceV1,
|
||||
private val logger: Logger,
|
||||
) : AddRecipeDataSource, RecipeDataSource {
|
||||
) : AddRecipeDataSource, RecipeDataSource, ParseRecipeDataSource {
|
||||
|
||||
override suspend fun addRecipe(
|
||||
recipe: AddRecipeInfo,
|
||||
@@ -64,6 +71,19 @@ class MealieDataSourceWrapper @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun parseRecipeFromURL(
|
||||
parseRecipeURLInfo: ParseRecipeURLInfo,
|
||||
): String = makeCall { token, url, version ->
|
||||
when (version) {
|
||||
ServerVersion.V0 -> {
|
||||
v0Source.parseRecipeFromURL(url, token, parseRecipeURLInfo.toV0Request())
|
||||
}
|
||||
ServerVersion.V1 -> {
|
||||
v1Source.parseRecipeFromURL(url, token, parseRecipeURLInfo.toV1Request())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun <T> makeCall(block: (String?, String, ServerVersion) -> T): T {
|
||||
val authHeader = authRepo.getAuthHeader()
|
||||
val url = serverInfoRepo.requireUrl()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
interface ParseRecipeDataSource {
|
||||
|
||||
suspend fun parseRecipeFromURL(parseRecipeURLInfo: ParseRecipeURLInfo): String
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
data class ParseRecipeURLInfo(
|
||||
val url: String,
|
||||
val includeTags: Boolean
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
interface ShareRecipeRepo {
|
||||
|
||||
suspend fun saveRecipeByURL(url: CharSequence): String
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ShareRecipeRepoImpl @Inject constructor(
|
||||
private val logger: Logger,
|
||||
private val parseRecipeDataSource: ParseRecipeDataSource,
|
||||
) : ShareRecipeRepo {
|
||||
|
||||
override suspend fun saveRecipeByURL(url: CharSequence): String {
|
||||
logger.v { "saveRecipeByURL() called with: url = $url" }
|
||||
val request = ParseRecipeURLInfo(url = url.toString(), includeTags = true)
|
||||
return parseRecipeDataSource.parseRecipeFromURL(request)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.data.network.MealieDataSourceWrapper
|
||||
import gq.kirmanak.mealient.data.share.ParseRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.share.ShareRecipeRepo
|
||||
import gq.kirmanak.mealient.data.share.ShareRecipeRepoImpl
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface ShareRecipeModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindShareRecipeRepo(shareRecipeRepoImpl: ShareRecipeRepoImpl): ShareRecipeRepo
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindParseRecipeDataSource(mealieDataSourceWrapper: MealieDataSourceWrapper): ParseRecipeDataSource
|
||||
}
|
||||
@@ -9,12 +9,32 @@ 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.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.*
|
||||
import gq.kirmanak.mealient.datasource.v1.models.*
|
||||
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.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.*
|
||||
|
||||
@@ -170,4 +190,13 @@ 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,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package gq.kirmanak.mealient.ui.share
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.extensions.showLongToast
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ShareRecipeActivity : AppCompatActivity() {
|
||||
|
||||
private val viewModel: ShareRecipeViewModel by viewModels()
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
logger.v { "onCreate() called with: savedInstanceState = $savedInstanceState" }
|
||||
|
||||
if (intent.action != Intent.ACTION_SEND || intent.type != "text/plain") {
|
||||
logger.w { "onCreate: intent.action = ${intent.action}, intent.type = ${intent.type}" }
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val url: CharSequence? = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)
|
||||
if (url == null) {
|
||||
logger.w { "onCreate: Intent's EXTRA_TEXT was null" }
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
viewModel.saveOperationResult.observe(this) {
|
||||
showLongToast(
|
||||
if (it.isSuccess) R.string.activity_share_recipe_success_toast
|
||||
else R.string.activity_share_recipe_failure_toast
|
||||
)
|
||||
finish()
|
||||
}
|
||||
|
||||
viewModel.saveRecipeByURL(url)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package gq.kirmanak.mealient.ui.share
|
||||
|
||||
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.data.share.ShareRecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ShareRecipeViewModel @Inject constructor(
|
||||
private val shareRecipeRepo: ShareRecipeRepo,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _saveOperationResult = MutableLiveData<Result<String>>()
|
||||
val saveOperationResult: LiveData<Result<String>> get() = _saveOperationResult
|
||||
|
||||
fun saveRecipeByURL(url: CharSequence) {
|
||||
logger.v { "saveRecipeByURL() called with: url = $url" }
|
||||
viewModelScope.launch {
|
||||
runCatchingExceptCancel {
|
||||
shareRecipeRepo.saveRecipeByURL(url)
|
||||
}.onSuccess {
|
||||
logger.d { "Successfully saved recipe by URL" }
|
||||
_saveOperationResult.postValue(Result.success(it))
|
||||
}.onFailure {
|
||||
logger.e(it) { "Can't save recipe by URL" }
|
||||
_saveOperationResult.postValue(Result.failure(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,4 +51,6 @@
|
||||
<string name="search_recipes_hint">Найти рецепты</string>
|
||||
<string name="view_toolbar_navigation_icon_content_description">Открыть меню навигации</string>
|
||||
<string name="fragment_recipes_list_no_recipes">Нет рецептов</string>
|
||||
<string name="activity_share_recipe_success_toast">Рецепт успешно сохранен.</string>
|
||||
<string name="activity_share_recipe_failure_toast">Что-то пошло не так.</string>
|
||||
</resources>
|
||||
@@ -54,4 +54,6 @@
|
||||
<string name="menu_navigation_drawer_header" translatable="false">@string/app_name</string>
|
||||
<string name="view_toolbar_navigation_icon_content_description">Open navigation drawer</string>
|
||||
<string name="fragment_recipes_list_no_recipes">No recipes</string>
|
||||
<string name="activity_share_recipe_success_toast">Recipe saved successfully.</string>
|
||||
<string name="activity_share_recipe_failure_toast">Something went wrong.</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user