Skip to content

Commit

Permalink
Merge pull request #246 from hieuwu/feature/overview-meal-planning
Browse files Browse the repository at this point in the history
Meal planning
  • Loading branch information
hieuwu committed Apr 29, 2024
2 parents 84a7a5d + dba0dfa commit 8bb1f24
Show file tree
Hide file tree
Showing 38 changed files with 1,304 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.build()

var rapidApiHost = "tasty.p.rapidapi.com"
var rapidApiKey = "25623d5122mshdf7d5b25fc85d92p16d42bjsn846c1ccf5e93"

interface RecipesApiService {
@Headers(
"x-rapidapi-host: tasty.p.rapidapi.com",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.hieuwu.groceriesstore.data.network.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class Meal(
@SerialName("id")
val id: String,

@SerialName("name")
val name: String,

@SerialName("ingredients")
val ingredients: List<String>,

@SerialName("week_day")
val weekDay: String,

@SerialName("creator")
val creatorId: String,

@SerialName("meal_type")
val mealType: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.hieuwu.groceriesstore.data.repository

import com.hieuwu.groceriesstore.domain.models.MealModel

interface MealPlanRepository {
suspend fun addMealToPlan(
weekDay: String,
name: String,
ingredients: List<String>,
mealType: String
)

suspend fun retrieveMealByType(type: String, weekDayValue: String): List<MealModel>
suspend fun removeMealFromPlan(id: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.hieuwu.groceriesstore.data.repository.impl

import com.hieuwu.groceriesstore.data.network.dto.Meal
import com.hieuwu.groceriesstore.data.network.dto.ProductDto
import com.hieuwu.groceriesstore.data.repository.MealPlanRepository
import com.hieuwu.groceriesstore.data.repository.UserRepository
import com.hieuwu.groceriesstore.domain.models.MealModel
import io.github.jan.supabase.postgrest.Postgrest
import java.util.UUID
import kotlinx.coroutines.flow.first
import timber.log.Timber
import javax.inject.Inject

class MealPlanRepositoryImpl @Inject constructor(
private val postgrest: Postgrest,
private val userRepository: UserRepository
) : MealPlanRepository {
override suspend fun addMealToPlan(
weekDay: String,
name: String,
ingredients: List<String>,
mealType: String
) {
val userId = userRepository.getCurrentUser().first()?.id ?: ""
postgrest["meal_plans"].insert<Meal>(
Meal(
id = UUID.randomUUID().toString(),
name = name,
ingredients = ingredients,
creatorId = userId,
weekDay = weekDay,
mealType = mealType,
)
)
}

override suspend fun retrieveMealByType(type: String, weekDayValue: String): List<MealModel> {
val userId = userRepository.getCurrentUser().first()?.id ?: ""
val result = postgrest["meal_plans"].select {
filter {
eq("creator", userId)
eq("week_day", weekDayValue)
eq("meal_type", type)
}
}.decodeList<Meal>().map {
Timber.d(it.toString())
MealModel(
id = it.id,
name = it.name,
ingredients = it.ingredients,
weekDay = it.weekDay,
creatorId = it.creatorId,
mealType = it.mealType
)
}
return result
}

override suspend fun removeMealFromPlan(id: String) {
postgrest["meal_plans"].delete {
filter {
eq("id", id)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ class UserRepositoryImpl @Inject constructor(
this.password = password
}

val userDto = postgrest[CollectionNames.users].select().decodeSingle<UserDto>()
val userDto = postgrest[CollectionNames.users].select()
{
filter {
UserDto::email eq email
}
}
.decodeSingle<UserDto>()
val user = SupabaseMapper.mapDtoToEntity(userDto)
userDao.insert(user)
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import com.hieuwu.groceriesstore.data.repository.impl.ProductRepositoryImpl
import com.hieuwu.groceriesstore.data.repository.impl.RecipeRepositoryImpl
import com.hieuwu.groceriesstore.data.repository.impl.UserRepositoryImpl
import com.hieuwu.groceriesstore.data.repository.CategoryRepository
import com.hieuwu.groceriesstore.data.repository.MealPlanRepository
import com.hieuwu.groceriesstore.data.repository.OrderRepository
import com.hieuwu.groceriesstore.data.repository.ProductRepository
import com.hieuwu.groceriesstore.data.repository.RecipeRepository
import com.hieuwu.groceriesstore.data.repository.UserRepository
import com.hieuwu.groceriesstore.data.repository.impl.MealPlanRepositoryImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -39,4 +41,8 @@ abstract class RepositoryModule {
@Singleton
@Binds
abstract fun bindRecipeRepository(impl: RecipeRepositoryImpl): RecipeRepository

@Singleton
@Binds
abstract fun bindMealRepository(impl: MealPlanRepositoryImpl): MealPlanRepository
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import io.github.jan.supabase.gotrue.Auth
import io.github.jan.supabase.gotrue.auth
import io.github.jan.supabase.postgrest.Postgrest
import io.github.jan.supabase.postgrest.postgrest
import io.github.jan.supabase.serializer.KotlinXSerializer
import io.ktor.client.plugins.*
import kotlinx.serialization.json.Json
import javax.inject.Singleton

@InstallIn(SingletonComponent::class)
Expand All @@ -23,8 +25,9 @@ object SupabaseModule {
fun provideSupabaseClient(): SupabaseClient {
return createSupabaseClient(
supabaseUrl = BuildConfig.SUPABASE_URL,
supabaseKey = BuildConfig.API_KEY
supabaseKey = BuildConfig.API_KEY,
) {
defaultSerializer = KotlinXSerializer(Json { ignoreUnknownKeys = true })
install(Postgrest)
install(Auth)
}
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/java/com/hieuwu/groceriesstore/di/UseCaseModule.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.hieuwu.groceriesstore.di

import com.hieuwu.groceriesstore.domain.usecases.AddMealToPlanUseCase
import com.hieuwu.groceriesstore.domain.usecases.AddToCartUseCase
import com.hieuwu.groceriesstore.domain.usecases.impl.AddToCartUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.CreateNewOrderUseCase
Expand All @@ -18,6 +19,8 @@ import com.hieuwu.groceriesstore.domain.usecases.impl.GetProductsListUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.GetProfileUseCase
import com.hieuwu.groceriesstore.domain.usecases.impl.GetProfileUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.RefreshAppDataUseCase
import com.hieuwu.groceriesstore.domain.usecases.RemoveMealFromPlanUseCase
import com.hieuwu.groceriesstore.domain.usecases.RetrieveMealByTypeUseCase
import com.hieuwu.groceriesstore.domain.usecases.impl.RefreshAppDataUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.SearchProductUseCase
import com.hieuwu.groceriesstore.domain.usecases.impl.SearchProductUseCaseImpl
Expand All @@ -32,7 +35,10 @@ import com.hieuwu.groceriesstore.domain.usecases.impl.UpdateCartItemUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.UpdateProfileUseCase
import com.hieuwu.groceriesstore.domain.usecases.impl.UpdateProfileUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.UserSettingsUseCase
import com.hieuwu.groceriesstore.domain.usecases.impl.AddMealToPlanUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.impl.GetOrderListUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.impl.RemoveMealFromPlanUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.impl.RetrieveMealByTypeUseCaseImpl
import com.hieuwu.groceriesstore.domain.usecases.impl.UserSettingsUseCaseImpl
import dagger.Binds
import dagger.Module
Expand Down Expand Up @@ -111,4 +117,17 @@ abstract class UseCaseModule {
@Binds
abstract fun bindGetOrdersUseCase(impl: GetOrderListUseCaseImpl): GetOrderListUseCase

@ViewModelScoped
@Binds
abstract fun bindAddMealToPlan(impl: AddMealToPlanUseCaseImpl): AddMealToPlanUseCase

@ViewModelScoped
@Binds
abstract fun bindRetrieveMealByType(impl: RetrieveMealByTypeUseCaseImpl): RetrieveMealByTypeUseCase


@ViewModelScoped
@Binds
abstract fun bindRemoveMealFromPlan(impl: RemoveMealFromPlanUseCaseImpl): RemoveMealFromPlanUseCase

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.hieuwu.groceriesstore.domain.models

data class MealModel(
val id: String,
val name: String,
val ingredients: List<String>,
val weekDay: String,
val creatorId: String,
val mealType: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.hieuwu.groceriesstore.domain.usecases

import com.hieuwu.groceriesstore.presentation.mealplanning.overview.state.MealType

interface AddMealToPlanUseCase :
SuspendUseCase<AddMealToPlanUseCase.Input, AddMealToPlanUseCase.Output> {
class Input(val name: String, val weekDay: String, val ingredients: List<String>,
val mealType: MealType
)
object Output
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.hieuwu.groceriesstore.domain.usecases

interface RemoveMealFromPlanUseCase :
SuspendUseCase<RemoveMealFromPlanUseCase.Input, RemoveMealFromPlanUseCase.Output> {
class Input(val id: String)
sealed class Output {
object Success: Output()
object Failure: Output()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.hieuwu.groceriesstore.domain.usecases

import com.hieuwu.groceriesstore.domain.models.MealModel
import com.hieuwu.groceriesstore.presentation.mealplanning.overview.state.MealType
import com.hieuwu.groceriesstore.presentation.mealplanning.overview.state.WeekDayValue

interface RetrieveMealByTypeUseCase :
SuspendUseCase<RetrieveMealByTypeUseCase.Input, RetrieveMealByTypeUseCase.Output> {
class Input(
val dayValue: WeekDayValue,
val mealType: MealType,
)

sealed class Output {
class Success(val data: List<MealModel>) : Output()
data object Failure : Output()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.hieuwu.groceriesstore.domain.usecases.impl

import com.hieuwu.groceriesstore.data.repository.MealPlanRepository
import com.hieuwu.groceriesstore.domain.usecases.AddMealToPlanUseCase
import javax.inject.Inject

class AddMealToPlanUseCaseImpl @Inject constructor(
private val mealPlanRepository: MealPlanRepository
) : AddMealToPlanUseCase {
override suspend fun execute(input: AddMealToPlanUseCase.Input): AddMealToPlanUseCase.Output {
mealPlanRepository.addMealToPlan(
weekDay = input.weekDay,
name = input.name,
ingredients = input.ingredients,
mealType = input.mealType.value
)
return AddMealToPlanUseCase.Output
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.hieuwu.groceriesstore.domain.usecases.impl

import com.hieuwu.groceriesstore.data.repository.MealPlanRepository
import com.hieuwu.groceriesstore.di.IoDispatcher
import com.hieuwu.groceriesstore.domain.usecases.RemoveMealFromPlanUseCase
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import javax.inject.Inject

class RemoveMealFromPlanUseCaseImpl @Inject constructor(
private val mealPlanRepository: MealPlanRepository,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : RemoveMealFromPlanUseCase {
override suspend fun execute(input: RemoveMealFromPlanUseCase.Input): RemoveMealFromPlanUseCase.Output {
return withContext(ioDispatcher) {
try {
mealPlanRepository.removeMealFromPlan(input.id)
RemoveMealFromPlanUseCase.Output.Success
} catch (e: Exception) {
RemoveMealFromPlanUseCase.Output.Failure
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.hieuwu.groceriesstore.domain.usecases.impl

import com.hieuwu.groceriesstore.data.repository.MealPlanRepository
import com.hieuwu.groceriesstore.di.IoDispatcher
import com.hieuwu.groceriesstore.domain.models.MealModel
import com.hieuwu.groceriesstore.domain.usecases.RetrieveMealByTypeUseCase
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import javax.inject.Inject

class RetrieveMealByTypeUseCaseImpl @Inject constructor(
private val mealPlanRepository: MealPlanRepository,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : RetrieveMealByTypeUseCase {
override suspend fun execute(input: RetrieveMealByTypeUseCase.Input): RetrieveMealByTypeUseCase.Output {
return withContext(ioDispatcher) {
try {
val result = mealPlanRepository.retrieveMealByType(
type = input.mealType.value,
weekDayValue = input.dayValue.dayValue
)
RetrieveMealByTypeUseCase.Output.Success(data = result)
} catch (e: Exception) {
RetrieveMealByTypeUseCase.Output.Failure
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class AccountFragment : Fragment() {
onProfileSettingsClick = ::navigateToProfileSettings,
onNotificationSettingsClick = ::navigateToNotificationsSettings,
onOrderHistorySettingsClick = ::navigateToOrderHistory,
onMealPlanningClick = ::navigateToMealPlanning,
)
}
}
Expand All @@ -49,4 +50,8 @@ class AccountFragment : Fragment() {
private fun navigateToOrderHistory() {
findNavController().navigate(R.id.action_accountFragment_to_orderHistoryFragment)
}

private fun navigateToMealPlanning() {
findNavController().navigate(R.id.action_accountFragment_to_overviewFragment)
}
}
Loading

0 comments on commit 8bb1f24

Please sign in to comment.