Labels editor view and offline sync of label changes using CreateLabelInput instead of IDs

This commit is contained in:
Jackson Harper 2023-11-01 19:38:44 +08:00
parent e1076f9ac4
commit 5db74f5fe9
5 changed files with 71 additions and 88 deletions

View file

@ -52,80 +52,50 @@ public struct EditLabelsSheet: View {
}
var content: some View {
VStack(spacing: 15) {
VStack {
LabelsEntryView(
searchTerm: $labelsViewModel.labelSearchFilter,
viewModel: labelsViewModel
)
.padding(.horizontal, 10)
.padding(.vertical, 20)
if labelsViewModel.labelSearchFilter.count >= 63 {
Text("The maximum length of a label is 64 chars.").foregroundColor(Color.red).font(.footnote)
}
List {
Section {
ForEach(labelsViewModel.labels.applySearchFilter(labelsViewModel.labelSearchFilter), id: \.self) { label in
Button(
action: {
// if labelsViewModel.selectedLabels.contains(label) {
// if let idx = viewModel.selectedLabels.firstIndex(of: label) {
// viewModel.selectedLabels.remove(at: idx)
// }
// } else {
// viewModel.labelSearchFilter = ZWSP
// viewModel.selectedLabels.append(label)
// }
},
label: {
HStack {
TextChip(feedItemLabel: label).allowsHitTesting(false)
Spacer()
if isSelected(label) {
Image(systemName: "checkmark")
}
}
.contentShape(Rectangle())
ForEach(labelsViewModel.labels.applySearchFilter(labelsViewModel.labelSearchFilter), id: \.self) { label in
Button(
action: {
if let idx = labelsViewModel.selectedLabels.firstIndex(of: label) {
labelsViewModel.selectedLabels.remove(at: idx)
} else {
labelsViewModel.labelSearchFilter = ZWSP
labelsViewModel.selectedLabels.append(label)
}
)
.padding(.vertical, 5)
.frame(maxWidth: .infinity, alignment: .leading)
#if os(macOS)
.buttonStyle(PlainButtonStyle())
#endif
}
// createLabelButton
},
label: {
HStack {
TextChip(feedItemLabel: label).allowsHitTesting(false)
Spacer()
if isSelected(label) {
Image(systemName: "checkmark")
}
}
.contentShape(Rectangle())
}
)
.padding(.vertical, 5)
.frame(maxWidth: .infinity, alignment: .leading)
#if os(macOS)
.buttonStyle(PlainButtonStyle())
#endif
}
}
.listStyle(PlainListStyle())
Spacer()
// // swiftlint:disable line_length
// ScrollView {
// LabelsMasonaryView(
// labels: labelsViewModel.labels.applySearchFilter(labelsViewModel.labelSearchFilter),
// selectedLabels: labelsViewModel.selectedLabels.applySearchFilter(labelsViewModel.labelSearchFilter),
// onLabelTap: onLabelTap
// )
//
// Button(
// action: { labelsViewModel.showCreateLabelModal = true },
// label: {
// HStack {
// let trimmedLabelName = labelsViewModel.labelSearchFilter.trimmingCharacters(in: .whitespacesAndNewlines)
// Image(systemName: "tag").foregroundColor(.blue)
// Text(
// labelsViewModel.labelSearchFilter.count > 0 ?
// "Create: \"\(trimmedLabelName)\" label" :
// LocalText.createLabelMessage
// ).foregroundColor(.blue)
// .font(Font.system(size: 14))
// Spacer()
// }
// }
// )
// .buttonStyle(PlainButtonStyle())
// .padding(10)
// }
.listStyle(.plain)
.background(Color.extensionBackground)
}
.background(Color.clear)
.padding(20)
}
public var body: some View {
@ -136,6 +106,12 @@ public struct EditLabelsSheet: View {
.navigationTitle("Set Labels")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: Button(action: {
if let linkedItem = viewModel.linkedItem, let linkedItemId = linkedItem.id {
labelsViewModel.saveItemLabelChanges(
itemID: linkedItemId,
dataService: viewModel.services.dataService
)
}
dismiss()
}, label: {
Text("Done").bold()

View file

@ -326,8 +326,8 @@ import Views
func addLabel(dataService: DataService, item: LinkedItem, label: String, color: String) {
if let label = getOrCreateLabel(dataService: dataService, named: "Pinned", color: color) {
let existingLabels = item.labels?.allObjects.compactMap { ($0 as? LinkedItemLabel)?.unwrappedID } ?? []
dataService.updateItemLabels(itemID: item.unwrappedID, labelIDs: existingLabels + [label.unwrappedID])
let existingLabels = item.labels?.allObjects.compactMap { $0 as? LinkedItemLabel } ?? []
dataService.setItemLabels(itemID: item.unwrappedID, labels: InternalLinkedItemLabel.make(Set(existingLabels + [label]) as NSSet))
item.update(inContext: dataService.viewContext)
updateFeatureFilter(context: dataService.viewContext, filter: FeaturedItemFilter(rawValue: featureFilter))
@ -335,10 +335,10 @@ import Views
}
func removeLabel(dataService: DataService, item: LinkedItem, named: String) {
let labelIds = item.labels?
let labels = item.labels?
.filter { ($0 as? LinkedItemLabel)?.name != named }
.compactMap { ($0 as? LinkedItemLabel)?.unwrappedID } ?? []
dataService.updateItemLabels(itemID: item.unwrappedID, labelIDs: labelIds)
.compactMap { $0 as? LinkedItemLabel } ?? []
dataService.setItemLabels(itemID: item.unwrappedID, labels: InternalLinkedItemLabel.make(Set(labels) as NSSet))
item.update(inContext: dataService.viewContext)
}

View file

@ -50,14 +50,13 @@ struct ApplyLabelsView: View {
var innerBody: some View {
VStack {
if !viewModel.labels.isEmpty {
LabelsEntryView(
searchTerm: $viewModel.labelSearchFilter,
viewModel: viewModel
)
.padding(.vertical, 8)
.padding(.horizontal, 16)
}
LabelsEntryView(
searchTerm: $viewModel.labelSearchFilter,
viewModel: viewModel
)
.padding(.horizontal, 10)
.padding(.vertical, 20)
if viewModel.labelSearchFilter.count >= 63 {
Text("The maximum length of a label is 64 chars.").foregroundColor(Color.red).font(.footnote)
}
@ -96,11 +95,13 @@ struct ApplyLabelsView: View {
createLabelButton
}
}
.listStyle(PlainListStyle())
.listStyle(.plain)
.background(Color.extensionBackground)
Spacer()
}
.navigationTitle(mode.navTitle)
.background(Color.extensionBackground)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
.toolbar {

View file

@ -116,7 +116,7 @@ import SwiftUI
}
func saveItemLabelChanges(itemID: String, dataService: DataService) {
dataService.updateItemLabels(itemID: itemID, labelIDs: selectedLabels.map(\.unwrappedID))
dataService.setItemLabels(itemID: itemID, labels: InternalLinkedItemLabel.make(Set(selectedLabels) as NSSet))
}
func saveHighlightLabelChanges(highlightID: String, dataService: DataService) {

View file

@ -3,8 +3,8 @@ import Foundation
import Models
import SwiftGraphQL
extension DataService {
public func updateItemLabels(itemID: String, labelIDs: [String]) {
public extension DataService {
func setItemLabels(itemID: String, labels: [InternalLinkedItemLabel]) {
backgroundContext.perform { [weak self] in
guard let self = self else { return }
guard let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.backgroundContext) else { return }
@ -13,21 +13,19 @@ extension DataService {
linkedItem.removeFromLabels(existingLabels)
}
for labelID in labelIDs {
if let labelObject = LinkedItemLabel.lookup(byID: labelID, inContext: self.backgroundContext) {
linkedItem.addToLabels(labelObject)
}
for label in labels {
linkedItem.addToLabels(label.asManagedObject(inContext: self.backgroundContext))
}
linkedItem.update(inContext: self.backgroundContext)
try? self.backgroundContext.save()
// Send update to server
self.syncLabelUpdates(itemID: itemID, labelIDs: labelIDs)
self.syncLabelUpdates(itemID: itemID, labels: labels)
}
}
func syncLabelUpdates(itemID: String, labelIDs: [String]) {
internal func syncLabelUpdates(itemID: String, labels: [InternalLinkedItemLabel]) {
enum MutationResult {
case saved(feedItem: [InternalLinkedItemLabel])
case error(errorCode: Enums.SetLabelsErrorCode)
@ -40,10 +38,18 @@ extension DataService {
)
}
let labelInputs = labels.compactMap { label in
InputObjects.CreateLabelInput(
color: OptionalArgument(label.color),
description: OptionalArgument(label.labelDescription),
name: label.name
)
}
let mutation = Selection.Mutation {
try $0.setLabels(
input: InputObjects.SetLabelsInput(
labelIds: OptionalArgument(labelIDs),
labels: OptionalArgument(labelInputs),
pageId: itemID
),
selection: selection