Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion EatHub/EatHub/Application/AppDependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ struct AppDependencies {
let mealsService: MealsService
let detailsViewModelBuilder: (DetailsViewModuleInput) -> DetailsViewModel
let launchScreenStateManager: LaunchScreenStateManager
let favoritesManager: FavoritesManagerInterface

init() {
let apiRequester = APIRequester()
let mealsService = MealsService(requester: apiRequester)
self.mealsService = mealsService

let favoritesManager = FavoritesManager()
self.favoritesManager = favoritesManager

let detailsViewModelBuilder: ((DetailsViewModuleInput) -> DetailsViewModel) = { input in
DetailsViewModel(
id: input.id,
Expand All @@ -39,6 +43,10 @@ struct AppDependencies {
}

func makeFavoriteViewModel() -> FavoriteViewModel {
FavoriteViewModel(favoritesManager: FavoritesManager(), mealsService: mealsService)
FavoriteViewModel(
favoritesManager: favoritesManager,
mealsService: mealsService,
detailsViewModelBuilder: detailsViewModelBuilder
)
}
}
6 changes: 0 additions & 6 deletions EatHub/EatHub/Mappers/MealMapper/MealMapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,3 @@ extension MealItemResponseModel {
)
}
}

extension Meal {
func mapToRecipe() -> RecipeViewModel {
RecipeViewModel(id: id, name: name, imageName: thumbnail ?? "MealTemplate")
}
}
76 changes: 61 additions & 15 deletions EatHub/EatHub/Modules/Favorite/FavoriteView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,38 @@ import SwiftUI

struct FavoriteView: View {
@ObservedObject var viewModel: FavoriteViewModel
@State private var selectedItem: RecipeViewModel?
@State private var showDetail: Bool = false
@Namespace private var animationNamespace

private enum Constants {
static let animationDuration: TimeInterval = 0.5
static let detailZIndex: Double = 1
static let detailTransition: AnyTransition = .asymmetric(
insertion: .move(edge: .bottom),
removal: .move(edge: .bottom)
)
}

var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
favoritesTitle
favoritesList
ZStack {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
favoritesTitle
favoritesList
}
}
.background(Color(.systemGroupedBackground))
.onAppear {
viewModel.refreshFavorites()
}

if let selectedItem, showDetail {
openDetailsView(for: selectedItem)
}
}
.background(Color(.systemGroupedBackground))
.navigationBarHidden(true)
.onAppear {
viewModel.refreshFavorites()
}
}
}
}
Expand All @@ -30,20 +48,48 @@ private extension FavoriteView {

var favoritesList: some View {
LazyVStack(spacing: 8) {
ForEach(viewModel.likedRecipes) { recipe in
// TODO: Добавить переход
// NavigationLink(destination: RecipeDetailView(recipe: recipe)) {
ForEach(viewModel.likedRecipes) { recipeViewModel in
RecipeRow(
recipe: recipe,
recipe: recipeViewModel,
onToggleFavorite: {
viewModel.toggleFavorite(for: recipe)
viewModel.toggleFavorite(for: recipeViewModel)
}
)
// }
.buttonStyle(PlainButtonStyle())
.onTapGesture {
withAnimation(.easeInOut(duration: Constants.animationDuration)) {
selectedItem = recipeViewModel
showDetail = true
}
}
}
}
.padding(.horizontal)
.padding(.top, 8)
}

@ViewBuilder
func openDetailsView(for recipeViewModel: RecipeViewModel) -> some View {
let viewModel = viewModel.detailsViewModelBuilder(
DetailsViewModuleInput(
id: recipeViewModel.id,
name: recipeViewModel.name,
thumbnail: recipeViewModel.thumbnail
)
)
DetailsView(
viewModel: viewModel,
onClose: {
withAnimation(.easeInOut(duration: Constants.animationDuration)) {
showDetail = false
viewModel.isCloseButtonHidden = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.animationDuration) {
selectedItem = nil
}
},
namespace: animationNamespace
)
.zIndex(Constants.detailZIndex)
.transition(Constants.detailTransition)
}
}
25 changes: 20 additions & 5 deletions EatHub/EatHub/Modules/Favorite/FavoriteViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ import Combine
final class FavoriteViewModel: ObservableObject {
@Published var likedRecipes: [RecipeViewModel] = []
var recipesIdentifiers: [String] = []
var detailsViewModelBuilder: (DetailsViewModuleInput) -> DetailsViewModel

let title = "Favourites"

private let favoritesManager: FavoritesManagerInterface
private let mealsService: MealsServiceInterface
private var cancellables = Set<AnyCancellable>()

init(favoritesManager: FavoritesManagerInterface, mealsService: MealsServiceInterface) {
init(
favoritesManager: FavoritesManagerInterface,
mealsService: MealsServiceInterface,
detailsViewModelBuilder: @escaping ((DetailsViewModuleInput) -> DetailsViewModel)
) {
self.favoritesManager = favoritesManager
self.mealsService = mealsService
self.detailsViewModelBuilder = detailsViewModelBuilder

// убрать
favoritesManager.populateInitialFavorites(with: ["52943", "52869", "52883", "52823"])
Expand Down Expand Up @@ -48,11 +54,20 @@ final class FavoriteViewModel: ObservableObject {
for id in ids {
mealsService.fetchMeal(id: id)
.receive(on: DispatchQueue.main)
.sink { _ in } receiveValue: { [weak self] meal in
if let recipe = meal?.mapToRecipe() {
self?.likedRecipes.append(recipe)
.sink(
receiveCompletion: { _ in },
receiveValue: { [weak self] meal in
guard let self else { return }
guard let meal else { return }

let recipeRowViewModel = RecipeViewModel(
id: meal.id,
name: meal.name,
thumbnail: meal.thumbnail
)
likedRecipes.append(recipeRowViewModel)
}
}
)
.store(in: &cancellables)
}
}
Expand Down
14 changes: 10 additions & 4 deletions EatHub/EatHub/Modules/Favorite/Models/RecipeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import Foundation
class RecipeViewModel: ObservableObject, Identifiable {
let id: String
@Published var name: String
@Published var imageName: String
@Published var isFavorite: Bool = true
@Published var thumbnail: String?
@Published var isFavorite: Bool

init(id: String, name: String, imageName: String) {
init(
id: String,
name: String,
thumbnail: String?,
isFavorite: Bool = true
) {
self.id = id
self.name = name
self.imageName = imageName
self.thumbnail = thumbnail
self.isFavorite = isFavorite
}
}
2 changes: 1 addition & 1 deletion EatHub/EatHub/Modules/Favorite/RecipeRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct RecipeRow: View {

var body: some View {
HStack {
if let url = URL(string: recipe.imageName) {
if let url = URL(string: recipe.thumbnail ?? "") {
CachedAsyncImage(url: url) { image in
image
.resizable()
Expand Down