diff --git a/apple/OmnivoreKit/Sources/App/Views/ApplyLabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/ApplyLabelsView.swift index a24826f8f..ffb6214fb 100644 --- a/apple/OmnivoreKit/Sources/App/Views/ApplyLabelsView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/ApplyLabelsView.swift @@ -2,6 +2,7 @@ import Combine import Models import Services import SwiftUI +import Views final class ApplyLabelsViewModel: ObservableObject { private var hasLoadedInitialLabels = false @@ -25,10 +26,20 @@ final class ApplyLabelsViewModel: ObservableObject { ) .store(in: &subscriptions) } + + func saveChanges(itemID: String, dataService: DataService, onComplete: @escaping ([FeedItemLabel]) -> Void) { + dataService.updateArticleLabelsPublisher(itemID: itemID, labelIDs: selectedLabels.map(\.id)).sink( + receiveCompletion: { _ in }, + receiveValue: { onComplete($0) } + ) + .store(in: &subscriptions) + } } struct ApplyLabelsView: View { let item: FeedItem + let commitLabelChanges: ([FeedItemLabel]) -> Void + @EnvironmentObject var dataService: DataService @Environment(\.presentationMode) private var presentationMode @StateObject var viewModel = ApplyLabelsViewModel() @@ -39,7 +50,11 @@ struct ApplyLabelsView: View { EmptyView() } else { List(viewModel.labels, id: \.self, selection: $viewModel.selectedLabels) { label in - Text(label.name) + if let textChip = TextChip(feedItemLabel: label) { + textChip + } else { + Text(label.name) + } } .environment(\.editMode, .constant(EditMode.active)) .navigationTitle("Apply Labels") @@ -54,8 +69,10 @@ struct ApplyLabelsView: View { ToolbarItem(placement: .navigationBarTrailing) { Button( action: { - print("saving") - presentationMode.wrappedValue.dismiss() + viewModel.saveChanges(itemID: item.id, dataService: dataService) { labels in + commitLabelChanges(labels) + presentationMode.wrappedValue.dismiss() + } }, label: { Text("Save") } ) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 1f908cbe6..535eb577e 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -167,8 +167,10 @@ import Views } } } - .sheet(item: $viewModel.itemUnderLabelEdit, onDismiss: { print("edit label modal dismissed") }) { item in - ApplyLabelsView(item: item) + .sheet(item: $viewModel.itemUnderLabelEdit) { item in + ApplyLabelsView(item: item) { labels in + viewModel.updateLabels(itemID: item.id, labels: labels) + } } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index bf71dfd18..f5e304094 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -183,4 +183,11 @@ final class HomeFeedViewModel: ObservableObject { items[index].readingProgress = progress } } + + func updateLabels(itemID: String, labels: [FeedItemLabel]) { + guard let item = items.first(where: { $0.id == itemID }) else { return } + if let index = items.firstIndex(of: item) { + items[index].labels = labels + } + } } diff --git a/apple/OmnivoreKit/Sources/Models/FeedItem.swift b/apple/OmnivoreKit/Sources/Models/FeedItem.swift index 6d716919b..42ff019ea 100644 --- a/apple/OmnivoreKit/Sources/Models/FeedItem.swift +++ b/apple/OmnivoreKit/Sources/Models/FeedItem.swift @@ -28,7 +28,7 @@ public struct FeedItem: Identifiable, Hashable, Decodable { public let slug: String public let isArchived: Bool public let contentReader: String? - public let labels: [FeedItemLabel] + public var labels: [FeedItemLabel] public init( id: String, diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/UpdateArticleLabelsPublisher.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/UpdateArticleLabelsPublisher.swift new file mode 100644 index 000000000..fce103d6a --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/UpdateArticleLabelsPublisher.swift @@ -0,0 +1,57 @@ +import Combine +import Foundation +import Models +import SwiftGraphQL + +public extension DataService { + func updateArticleLabelsPublisher(itemID: String, labelIDs: [String]) -> AnyPublisher<[FeedItemLabel], BasicError> { + enum MutationResult { + case saved(feedItem: [FeedItemLabel]) + case error(errorCode: Enums.SetLabelsErrorCode) + } + + let selection = Selection { + try $0.on( + setLabelsSuccess: .init { .saved(feedItem: try $0.labels(selection: feedItemLabelSelection.list)) }, + setLabelsError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) } + ) + } + + let mutation = Selection.Mutation { + try $0.setLabels( + input: InputObjects.SetLabelsInput( + linkId: itemID, + labelIds: labelIDs + ), + selection: selection + ) + } + + let path = appEnvironment.graphqlPath + let headers = networker.defaultHeaders + + return Deferred { + Future { promise in + send(mutation, to: path, headers: headers) { result in + switch result { + case let .success(payload): + if let graphqlError = payload.errors { + promise(.failure(.message(messageText: graphqlError.first.debugDescription))) + } + + switch payload.data { + case let .saved(labels): + promise(.success(labels)) + case .error: + promise(.failure(.message(messageText: "failed to set labels"))) + } + case .failure: + promise(.failure(.message(messageText: "failed to set labels"))) + } + } + } + } + .receive(on: DispatchQueue.main) + .eraseToAnyPublisher() + } +} diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index ced09d0c4..55fef0e3b 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -164,8 +164,9 @@ public struct GridCard: View { if FeatureFlag.enableLabels { ScrollView(.horizontal, showsIndicators: false) { HStack { - TextChip(text: "label", color: .red) - TextChip(text: "longer label", color: .blue) + ForEach(item.labels, id: \.self) { + TextChip(feedItemLabel: $0) + } Spacer() } .frame(height: 30) diff --git a/apple/OmnivoreKit/Sources/Views/TextChip.swift b/apple/OmnivoreKit/Sources/Views/TextChip.swift index 775ad6854..b25bb7c94 100644 --- a/apple/OmnivoreKit/Sources/Views/TextChip.swift +++ b/apple/OmnivoreKit/Sources/Views/TextChip.swift @@ -1,11 +1,25 @@ +import Models import SwiftUI +import Utils + +public struct TextChip: View { + public init(text: String, color: Color) { + self.text = text + self.color = color + } + + public init?(feedItemLabel: FeedItemLabel) { + guard let color = Color(hex: feedItemLabel.color) else { return nil } + + self.text = feedItemLabel.name + self.color = color + } -struct TextChip: View { let text: String let color: Color let cornerRadius = 20.0 - var body: some View { + public var body: some View { Text(text) .padding(.horizontal, 10) .padding(.vertical, 5)