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
1 change: 0 additions & 1 deletion MacMagazine/WatchApp/Helper/FeedDotsIndicatorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public struct FeedDotsIndicatorView: View {
dots
}
.padding(6)
.glassEffect(.clear)
.allowsHitTesting(false)
}

Expand Down
106 changes: 0 additions & 106 deletions MacMagazine/WatchApp/Helper/FlowTagsView.swift

This file was deleted.

2 changes: 1 addition & 1 deletion MacMagazine/WatchApp/MainApp/WatchApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct WatchApp: App {
var body: some Scene {
WindowGroup {
FeedMainView(
viewModel: FeedRootViewModel(
viewModel: FeedMainViewModel(
feedViewModel: FeedViewModel(storage: database)
)
)
Expand Down
78 changes: 29 additions & 49 deletions MacMagazine/WatchApp/ViewModel/FeedRootViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,61 @@ import SwiftData
import WatchKit

@MainActor
final class FeedRootViewModel: ObservableObject {
final class FeedMainViewModel: ObservableObject {

// MARK: - Published

@Published private(set) var status: FeedViewModel.Status = .loading
@Published var selectedIndex: Int = 0
@Published var showActions: Bool = false
@Published var showContextMenu: Bool = false
@Published var selectedPostForDetail: SelectedPost?
@Published private(set) var isRefreshing: Bool = false

// MARK: - Private

private let feedViewModel: FeedViewModel
private var didLoadInitial: Bool = false

// MARK: - Init

init(feedViewModel: FeedViewModel) {
self.feedViewModel = feedViewModel
status = feedViewModel.status
}

// MARK: - Public API

func refresh() async {
_ = try? await feedViewModel.getWatchFeed()
status = feedViewModel.status
}
func loadInitialIfNeeded(hasItems: Bool, modelContext: ModelContext) async {
guard !didLoadInitial else { return }
didLoadInitial = true

func toggleActions() {
showActions.toggle()
if hasItems {
status = .done
return
}

await refresh(modelContext: modelContext)
}

func hideActions() {
showActions = false
func refresh(modelContext: ModelContext) async {
guard !isRefreshing else { return }
isRefreshing = true

defer {
isRefreshing = false
}

_ = try? await feedViewModel.getWatchFeed()
status = feedViewModel.status
}

func toggleFavorite(post: FeedDB) {
let context = feedViewModel.context
func toggleFavorite(post: FeedDB, modelContext: ModelContext) {
post.favorite.toggle()
try? context.save()
try? modelContext.save()
showActions = true
}

// MARK: - Index Calculation
// MARK: - Index / Helpers

func computeSelectedIndexByMidY(items: [FeedDB], positions: [String: CGPoint]) -> Int {
guard !items.isEmpty else { return 0 }
Expand All @@ -71,45 +83,13 @@ final class FeedRootViewModel: ObservableObject {
return bestIndex
}

func computeSelectedIndexByMidX(items: [FeedDB], positions: [String: CGPoint]) -> Int {
guard !items.isEmpty else { return 0 }

let screenMidX = WKInterfaceDevice.current().screenBounds.midX

var bestIndex = 0
var bestDistance = CGFloat.greatestFiniteMagnitude

for (index, item) in items.enumerated() {
guard let point = positions[item.postId] else { continue }
let distance = abs(point.x - screenMidX)

if distance < bestDistance {
bestDistance = distance
bestIndex = index
}
}

return bestIndex
}

func clampIndex(_ index: Int, quantity: Int) -> Int {
guard quantity > 0 else { return 0 }
return min(max(index, 0), quantity - 1)
}
}

// MARK: - Preview Support

#if DEBUG
extension FeedRootViewModel {
static func preview() -> FeedRootViewModel {
let database = Database(models: [FeedDB.self], inMemory: true)
let feedVM = FeedViewModel(storage: database)
let viewModel = FeedRootViewModel(feedViewModel: feedVM)

viewModel.status = .done

return viewModel
@MainActor
func setStatusForPreview(_ status: FeedViewModel.Status) {
self.status = status
}
}
#endif
34 changes: 7 additions & 27 deletions MacMagazine/WatchApp/Views/FeedDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import SwiftUI

struct FeedDetailView: View {

@Environment(\.modelContext) private var modelContext

// MARK: - Properties

let post: FeedDB

@StateObject private var viewModel: FeedRootViewModel
@StateObject private var viewModel: FeedMainViewModel

// MARK: - Init

init(viewModel: FeedRootViewModel, post: FeedDB) {
init(viewModel: FeedMainViewModel, post: FeedDB) {
self.post = post
_viewModel = StateObject(wrappedValue: viewModel)
}
Expand All @@ -22,7 +24,6 @@ struct FeedDetailView: View {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
header
categories
bodyText
footer
}
Expand All @@ -49,28 +50,6 @@ struct FeedDetailView: View {
}
}

// MARK: - Categories (Flow Tags)

@ViewBuilder
private var categories: some View {
if !post.categories.isEmpty {
FlowTagsView(
tags: post.categories.map { $0.uppercased() },
horizontalSpacing: 6,
verticalSpacing: 6
) { tag in
Text(tag)
.font(.caption2)
.foregroundStyle(.primary)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(.secondary.opacity(0.4))
.clipShape(Capsule())
}
.allowsHitTesting(false)
}
}

// MARK: - Body

@ViewBuilder
Expand All @@ -93,7 +72,7 @@ struct FeedDetailView: View {
.padding(.top, 4)

Button {
viewModel.toggleFavorite(post: post)
viewModel.toggleFavorite(post: post, modelContext: modelContext)
} label: {
Image(systemName: post.favorite ? "star.fill" : "star")
}
Expand All @@ -105,6 +84,7 @@ struct FeedDetailView: View {
// MARK: - Preview
#if DEBUG
#Preview {
FeedDetailView(viewModel: .preview(), post: .previewItem)
FeedDetailView(viewModel: .preview(status: .done),
post: .previewItem)
}
#endif
Loading