diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift index ed0991c9d..97f5593e7 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift @@ -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) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift index b0e5b8577..2a0e9bfe9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift @@ -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, diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 100e1eed5..457a07037 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -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) diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift index 840933f9e..db61b30ad 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift @@ -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) diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsMasonaryView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsMasonaryView.swift index a9d24a4e6..2eedcd1f7 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsMasonaryView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsMasonaryView.swift @@ -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 diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index 649a20a9b..39649a71c 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -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) diff --git a/apple/OmnivoreKit/Sources/Views/SearchBar.swift b/apple/OmnivoreKit/Sources/Views/SearchBar.swift index e66fc9587..c8d97b891 100644 --- a/apple/OmnivoreKit/Sources/Views/SearchBar.swift +++ b/apple/OmnivoreKit/Sources/Views/SearchBar.swift @@ -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, - 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 } } }