Use the ApplyLabels view when users want to search for labels

This commit is contained in:
Jackson Harper 2023-01-20 18:08:51 +08:00
parent 1d79690f0d
commit 12f40a61b8
7 changed files with 84 additions and 143 deletions

View file

@ -4,9 +4,9 @@ import SwiftUI
import Utils
import Views
// swiftlint:disable:next type_body_length
public struct ShareExtensionView: View {
let extensionContext: NSExtensionContext?
@EnvironmentObject var dataService: DataService
@StateObject var labelsViewModel = LabelsViewModel()
@StateObject private var viewModel = ShareExtensionViewModel()
@ -14,7 +14,7 @@ public struct ShareExtensionView: View {
@State var hideUntilReminded = false
@State var previousLabels: [LinkedItemLabel]?
@State var messageText: String?
@State var searchTerm: String = ""
@State var showSearchLabels = false
@State var viewState = ViewState.mainView
@State var showHighlightInstructionAlert = false
@ -129,7 +129,7 @@ public struct ShareExtensionView: View {
.font(.appFootnote)
.foregroundColor(.appGrayText)
.frame(maxWidth: .infinity, alignment: .leading)
} else {}
}
}
.frame(maxWidth: .infinity, maxHeight: 60)
.padding()
@ -140,6 +140,28 @@ public struct ShareExtensionView: View {
}
}
var searchButton: some View {
HStack {
Image(systemName: "magnifyingglass")
.resizable()
.frame(width: 15, height: 15)
.foregroundColor(.appGrayText)
.padding(.leading, 10)
Text("Search labels")
.font(Font.system(size: 15))
.foregroundColor(.appGrayText)
Spacer()
}
.frame(height: 36)
.frame(maxWidth: .infinity)
.background(
Color.appButtonBackground
.cornerRadius(8)
)
}
var labelsSection: some View {
HStack {
if viewState != .editingLabels {
@ -174,32 +196,24 @@ public struct ShareExtensionView: View {
Image(systemName: "chevron.right")
.font(.appCallout)
} else {
VStack {
ScrollView {
SearchBar(searchTerm: $searchTerm)
VStack(spacing: 15) {
searchButton
.onTapGesture { showSearchLabels = true }
VStack {
LabelsMasonaryView(labels: labelsViewModel.labels,
selectedLabels: labelsViewModel.selectedLabels,
onLabelTap: onLabelTap)
}.background(Color.appButtonBackground)
.cornerRadius(8)
Button(
action: { labelsViewModel.showCreateLabelModal = true },
label: {
HStack {
Spacer()
Image(systemName: "plus")
Text("Create label")
Spacer()
}
}
).buttonStyle(RoundedRectButtonStyle(color: .blue, textColor: .white))
Spacer()
}
.background(Color.appButtonBackground)
.cornerRadius(8)
}
}
}
.padding(16)
.padding(viewState == .editingLabels ? 0 : 16)
.background(viewState == .editingLabels ? Color.clear : Color.appButtonBackground)
.frame(maxWidth: .infinity, maxHeight: viewState == .editingLabels ? .infinity : 60)
.background(Color.appButtonBackground)
.cornerRadius(8)
}
@ -492,89 +506,17 @@ public struct ShareExtensionView: View {
isPresented: $showHighlightInstructionAlert) {
Button("Ok", role: .cancel) { showHighlightInstructionAlert = false }
}
.environmentObject(viewModel.services.dataService)
.task {
await labelsViewModel.loadLabelsFromStore(dataService: viewModel.services.dataService)
}
}
}
struct ApplyLabelsListView: View {
@EnvironmentObject var dataService: DataService
@StateObject var viewModel = LabelsViewModel()
let linkedItem: LinkedItem?
func isSelected(_ label: LinkedItemLabel) -> Bool {
viewModel.selectedLabels.contains(where: { $0.id == label.id })
}
var body: some View {
List {
Section(
content: {
ForEach(viewModel.labels.applySearchFilter(viewModel.labelSearchFilter), id: \.self) { label in
Button(
action: {
if isSelected(label) {
viewModel.selectedLabels.removeAll(where: { $0.id == label.id })
} else {
viewModel.selectedLabels.append(label)
}
if let linkedItem = linkedItem {
viewModel.saveItemLabelChanges(itemID: linkedItem.unwrappedID, dataService: dataService)
}
},
label: {
HStack {
TextChip(feedItemLabel: label)
Spacer()
if isSelected(label) {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.checkmarkBlue)
} else {
Image(systemName: "circle")
.foregroundColor(.appGraySolid)
}
}
.contentShape(Rectangle())
}
)
#if os(iOS)
.listRowSeparator(.hidden)
#endif
.buttonStyle(PlainButtonStyle())
}
},
header: {
Text(LocalText.labelsGeneric)
.font(.appFootnote)
.foregroundColor(.appGrayText)
}
)
#if os(iOS)
.listRowSeparator(.hidden)
#endif
Button(
action: { viewModel.showCreateLabelModal = true },
label: {
HStack {
Image(systemName: "plus.circle.fill").foregroundColor(.green)
Text(LocalText.createLabelMessage).foregroundColor(.appGrayTextContrast)
Spacer()
}
}
)
.disabled(viewModel.isLoading)
}
.listStyle(PlainListStyle())
.padding(.vertical, 0)
.task {
await viewModel.loadLabelsFromStore(dataService: dataService)
}
.sheet(isPresented: $viewModel.showCreateLabelModal) {
CreateLabelView(viewModel: viewModel)
}
.sheet(isPresented: $showSearchLabels) {
ApplyLabelsView(mode: .list(self.labelsViewModel.selectedLabels), isSearchFocused: true) { labels in
self.labelsViewModel.selectedLabels = labels
if let itemID = self.viewModel.linkedItem?.unwrappedID {
self.labelsViewModel.saveItemLabelChanges(itemID: itemID, dataService: self.viewModel.services.dataService)
}
}
}
.environmentObject(viewModel.services.dataService)
}
}

View file

@ -71,7 +71,7 @@ struct HighlightsListView: View {
}
}
}.sheet(item: $setLabelsHighlight) { highlight in
ApplyLabelsView(mode: .highlight(highlight), onSave: { selectedLabels in
ApplyLabelsView(mode: .highlight(highlight), isSearchFocused: false, onSave: { selectedLabels in
hasHighlightMutations = true
viewModel.setLabelsForHighlight(highlightID: highlight.unwrappedID,

View file

@ -60,7 +60,7 @@ import Views
loadItems(isRefresh: true)
}
.sheet(item: $viewModel.itemUnderLabelEdit) { item in
ApplyLabelsView(mode: .item(item), onSave: nil)
ApplyLabelsView(mode: .item(item), isSearchFocused: false, onSave: nil)
}
.sheet(item: $viewModel.itemUnderTitleEdit) { item in
LinkedItemMetadataEditView(item: item)

View file

@ -12,9 +12,9 @@ struct ApplyLabelsView: View {
var navTitle: String {
switch self {
case .item, .highlight:
return "Assign Labels"
return "Set Labels"
case .list:
return "Apply Label Filters"
return "Set Labels"
}
}
@ -23,24 +23,35 @@ struct ApplyLabelsView: View {
case .item, .highlight:
return "Save"
case .list:
return "Apply"
return "Done"
}
}
}
let mode: Mode
let isSearchFocused: Bool
let onSave: (([LinkedItemLabel]) -> Void)?
@EnvironmentObject var dataService: DataService
@Environment(\.presentationMode) private var presentationMode
@StateObject var viewModel = LabelsViewModel()
enum ViewState {
case mainView
case editingTitle
case editingLabels
case viewingHighlight
}
func isSelected(_ label: LinkedItemLabel) -> Bool {
viewModel.selectedLabels.contains(where: { $0.id == label.id })
}
var innerBody: some View {
List {
Section(header: Spacer(minLength: 0)) {
SearchBar(searchTerm: $viewModel.labelSearchFilter, initialFocus: isSearchFocused)
}
Section {
Button(
action: { viewModel.showCreateLabelModal = true },
@ -80,6 +91,7 @@ struct ApplyLabelsView: View {
}
}
}
.padding(.top, 0)
.navigationTitle(mode.navTitle)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
@ -136,10 +148,7 @@ struct ApplyLabelsView: View {
EmptyView()
} else {
innerBody
.searchable(
text: $viewModel.labelSearchFilter,
placement: .navigationBarDrawer(displayMode: .always)
)
.padding(.top, -20) // This is a hack to give us a bit more room on the page
}
}
#elseif os(macOS)

View file

@ -12,12 +12,8 @@ import Models
import Views
struct LabelsMasonaryView: View {
// var allLabels: [LinkedItemLabel]
// var selectedLabels: [LinkedItemLabel]
var onLabelTap: (LinkedItemLabel, TextChip) -> Void
var iteration = UUID().uuidString
@State private var totalHeight = CGFloat.zero
private var labelItems: [(label: LinkedItemLabel, selected: Bool)]
@ -52,7 +48,8 @@ struct LabelsMasonaryView: View {
return ZStack(alignment: .topLeading) {
ForEach(self.labelItems, id: \.label.self) { label in
self.item(for: label)
.padding([.horizontal, .vertical], 6)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.alignmentGuide(.leading, computeValue: { dim in
if abs(width - dim.width) > geom.size.width {
width = 0

View file

@ -295,7 +295,7 @@ struct WebReaderContainerView: View {
Button(LocalText.cancelGeneric, role: .cancel, action: {})
}
.sheet(isPresented: $showLabelsModal) {
ApplyLabelsView(mode: .item(item), onSave: { _ in showLabelsModal = false })
ApplyLabelsView(mode: .item(item), isSearchFocused: false, onSave: { _ in showLabelsModal = false })
}
.sheet(isPresented: $showTitleEdit) {
LinkedItemMetadataEditView(item: item)
@ -410,7 +410,7 @@ struct WebReaderContainerView: View {
}
.sheet(isPresented: $showHighlightLabelsModal) {
if let highlight = Highlight.lookup(byID: self.annotation, inContext: self.dataService.viewContext) {
ApplyLabelsView(mode: .highlight(highlight)) { selectedLabels in
ApplyLabelsView(mode: .highlight(highlight), isSearchFocused: false) { selectedLabels in
viewModel.setLabelsForHighlight(highlightID: highlight.unwrappedID,
labelIDs: selectedLabels.map(\.unwrappedID),
dataService: dataService)

View file

@ -1,48 +1,38 @@
import SwiftUI
public struct SearchBar: View {
private let horizontalPadding: Double
@Binding var searchTerm: String
@FocusState private var isFocused: Bool
@State private var initialFocus: Bool
public init(
searchTerm: Binding<String>,
horizontalPadding: Double = 10
initialFocus: Bool
) {
self._searchTerm = searchTerm
self.horizontalPadding = horizontalPadding
self.initialFocus = initialFocus
}
public var body: some View {
HStack(spacing: 0) {
TextField("Search", text: $searchTerm)
.padding(7)
.padding(.horizontal, 25)
.background(Color.systemGray6)
.frame(height: 36)
.frame(maxWidth: .infinity)
.background(Color.appButtonBackground)
.cornerRadius(8)
.focused($isFocused)
.padding(.leading, 24)
.overlay(
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 10)
.resizable()
.frame(width: 15, height: 15)
.foregroundColor(.appGrayText)
.padding(.leading, 2)
if self.searchTerm != "" {
Button(
action: {
self.searchTerm = ""
},
label: {
Image(systemName: "multiply.circle.fill")
.foregroundColor(.gray)
.padding(.trailing, 8)
}
)
}
Spacer()
}
)
.padding(.horizontal, horizontalPadding)
if isFocused {
Button(
@ -51,12 +41,15 @@ public struct SearchBar: View {
self.isFocused = false
},
label: {
Text(LocalText.cancelGeneric)
Image(systemName: "multiply.circle.fill")
.foregroundColor(.gray)
}
)
.padding(.trailing, 10)
.padding(.trailing, 0)
.transition(.move(edge: .trailing))
}
}.onAppear {
self.isFocused = initialFocus
}
}
}