Improve label search in the apply views

This commit is contained in:
Jackson Harper 2023-01-23 11:59:37 +08:00
parent 8d452094d6
commit 57289cb0c4
6 changed files with 105 additions and 82 deletions

View file

@ -181,11 +181,24 @@ public struct ShareExtensionView: View {
LabelsMasonaryView(labels: labelsViewModel.labels.applySearchFilter(labelsViewModel.labelSearchFilter),
selectedLabels: labelsViewModel.selectedLabels.applySearchFilter(labelsViewModel.labelSearchFilter),
onLabelTap: onLabelTap)
Spacer()
}
.padding(.bottom, 16)
.background(Color.appButtonBackground)
.cornerRadius(8)
Button(
action: { labelsViewModel.showCreateLabelModal = true },
label: {
HStack {
Image(systemName: "tag").foregroundColor(.blue)
Text(
labelsViewModel.labelSearchFilter.count > 0 ?
"Create: \"\(labelsViewModel.labelSearchFilter)\" label" :
LocalText.createLabelMessage
).foregroundColor(.blue)
.font(Font.system(size: 14))
Spacer()
}
}
)
.buttonStyle(PlainButtonStyle())
.padding(10)
}.background(Color.appButtonBackground)
}
}
}
@ -462,7 +475,7 @@ public struct ShareExtensionView: View {
viewModel.savePage(extensionContext: extensionContext)
}
.sheet(isPresented: $labelsViewModel.showCreateLabelModal) {
CreateLabelView(viewModel: labelsViewModel)
CreateLabelView(viewModel: labelsViewModel, newLabelName: labelsViewModel.labelSearchFilter)
}
.alert("Before saving an article select text in Safari to create a highlight on save.",
isPresented: $showHighlightInstructionAlert) {
@ -470,15 +483,6 @@ public struct ShareExtensionView: View {
}
.task {
await labelsViewModel.loadLabelsFromStore(dataService: viewModel.services.dataService)
}
.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)
}.environmentObject(viewModel.services.dataService)
}
}

View file

@ -48,75 +48,87 @@ struct ApplyLabelsView: View {
}
var innerBody: some View {
List {
Section(header: Spacer(minLength: 0)) {
SearchBar(searchTerm: $viewModel.labelSearchFilter)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.listRowBackground(Color.clear)
}
Section {
Button(
action: { viewModel.showCreateLabelModal = true },
label: {
HStack {
Image(systemName: "plus.circle.fill").foregroundColor(.green)
Text(LocalText.createLabelMessage).foregroundColor(.appGrayTextContrast)
Spacer()
}
}
)
.disabled(viewModel.isLoading)
}
Section {
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)
}
},
label: {
HStack {
TextChip(feedItemLabel: label)
Spacer()
VStack {
SearchBar(searchTerm: $viewModel.labelSearchFilter)
.padding(.vertical, 8)
.padding(.horizontal, 16)
List {
Section {
ForEach(viewModel.labels.applySearchFilter(viewModel.labelSearchFilter), id: \.self) { label in
Button(
action: {
if isSelected(label) {
Image(systemName: "checkmark")
viewModel.selectedLabels.removeAll(where: { $0.id == label.id })
} else {
viewModel.selectedLabels.append(label)
}
},
label: {
HStack {
TextChip(feedItemLabel: label)
Spacer()
if isSelected(label) {
Image(systemName: "checkmark")
}
}
}
}
)
.listRowInsets(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8))
#if os(macOS)
.buttonStyle(PlainButtonStyle())
#endif
)
.padding(.vertical, 5)
#if os(macOS)
.buttonStyle(PlainButtonStyle())
#endif
}
createLabelButton
}
Spacer()
}
.listStyle(PlainListStyle())
.navigationTitle(mode.navTitle)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
cancelButton
}
ToolbarItem(placement: .navigationBarTrailing) {
saveItemChangesButton
}
}
#else
.toolbar {
ToolbarItemGroup {
cancelButton
saveItemChangesButton
}
}
#endif
.sheet(isPresented: $viewModel.showCreateLabelModal) {
CreateLabelView(viewModel: viewModel, newLabelName: viewModel.labelSearchFilter)
}
}
.padding(.top, 0)
.navigationTitle(mode.navTitle)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
cancelButton
}
ToolbarItem(placement: .navigationBarTrailing) {
saveItemChangesButton
}
var createLabelButton: some View {
Button(
action: { viewModel.showCreateLabelModal = true },
label: {
HStack {
Image(systemName: "tag").foregroundColor(.blue)
Text(
viewModel.labelSearchFilter.count > 0 ?
"Create: \"\(viewModel.labelSearchFilter)\" label" :
LocalText.createLabelMessage
).foregroundColor(.blue)
.font(Font.system(size: 14))
Spacer()
}
}
#else
.toolbar {
ToolbarItemGroup {
cancelButton
saveItemChangesButton
}
}
#endif
.sheet(isPresented: $viewModel.showCreateLabelModal) {
CreateLabelView(viewModel: viewModel)
}
)
.buttonStyle(PlainButtonStyle())
.disabled(viewModel.isLoading)
.listRowSeparator(.hidden, edges: .bottom)
.padding(.vertical, 10)
}
var saveItemChangesButton: some View {
@ -151,7 +163,6 @@ struct ApplyLabelsView: View {
EmptyView()
} else {
innerBody
.padding(.top, -20) // This is a hack to give us a bit more room on the page
}
}
#elseif os(macOS)

View file

@ -38,7 +38,7 @@ struct LabelsView: View {
Button(LocalText.cancelGeneric, role: .cancel) { self.labelToRemove = nil }
}
.sheet(isPresented: $viewModel.showCreateLabelModal) {
CreateLabelView(viewModel: viewModel)
CreateLabelView(viewModel: viewModel, newLabelName: viewModel.labelSearchFilter)
}
.task { await viewModel.loadLabels(dataService: dataService, item: nil) }
}
@ -85,9 +85,14 @@ struct CreateLabelView: View {
@EnvironmentObject var dataService: DataService
@ObservedObject var viewModel: LabelsViewModel
@State private var newLabelName = ""
@State private var newLabelName: String
@State private var newLabelColor = Color.clear
init(viewModel: LabelsViewModel, newLabelName: String = "") {
self.viewModel = viewModel
self.newLabelName = newLabelName
}
var shouldDisableCreateButton: Bool {
viewModel.isLoading || newLabelName.isEmpty || newLabelColor == .clear
}

View file

@ -114,7 +114,7 @@ import Views
if let label = dataService.viewContext.object(with: labelObjectID) as? LinkedItemLabel {
labels.insert(label, at: 0)
unselectedLabels.insert(label, at: 0)
selectedLabels.insert(label, at: 0)
}
isLoading = false

View file

@ -52,5 +52,8 @@ public struct SearchBar: View {
searchTerm = ""
}
}
.onTapGesture {
isFocused = true
}
}
}

View file

@ -171,7 +171,7 @@ Readability.prototype = {
// Readability-readerable.js. Please keep both copies in sync.
articleNegativeLookBehindCandidates: /breadcrumbs|breadcrumb|utils|trilist/i,
articleNegativeLookAheadCandidates: /outstream(.?)_|sub(.?)_|m_|omeda-promo-|in-article-advert|block-ad-.*/i,
unlikelyCandidates: /\bad\b|ai2html|banner|breadcrumbs|breadcrumb|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager(?!ow)|popup|yom-remote|copyright|keywords|outline|infinite-list|beta|recirculation|site-index|hide-for-print|post-end-share-cta|post-end-cta-full|post-footer|post-head|post-tag|li-date|main-navigation|programtic-ads|outstream_article|hfeed|comment-holder|back-to-top|show-up-next|onward-journey|topic-tracker|list-nav|block-ad-entity|adSpecs|gift-article-button|modal-title|in-story-masthead|share-tools|standard-dock|expanded-dock|margins-h|subscribe-dialog|icon|bumped|dvz-social-media-buttons/i,
unlikelyCandidates: /\bad\b|ai2html|banner|breadcrumbs|breadcrumb|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager(?!ow)|popup|yom-remote|copyright|keywords|outline|infinite-list|beta|recirculation|site-index|hide-for-print|post-end-share-cta|post-end-cta-full|post-footer|post-head|post-tag|li-date|main-navigation|programtic-ads|outstream_article|hfeed|comment-holder|back-to-top|show-up-next|onward-journey|topic-tracker|list-nav|block-ad-entity|adSpecs|gift-article-button|modal-title|in-story-masthead|share-tools|standard-dock|expanded-dock|margins-h|subscribe-dialog|icon|bumped|dvz-social-media-buttons|post-toc|mobile-menu|mobile-navbar/i,
// okMaybeItsACandidate: /and|article(?!-breadcrumb)|body|column|content|main|shadow|post-header/i,
get okMaybeItsACandidate() {
return new RegExp(`and|(?<!${this.articleNegativeLookAheadCandidates.source})article(?!-(${this.articleNegativeLookBehindCandidates.source}))|body|column|content|^(?!main-navigation|main-header)main|shadow|post-header|hfeed site|blog-posts hfeed|container-banners|menu-opacity|header-with-anchor-widget`, 'i')