From 5f1332e2d77f188adaf235ae223a6f3c6b02477b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 1 Dec 2023 12:02:34 +0800 Subject: [PATCH] Fix filters to navigation header --- .../App/Views/Home/FetcherFilterState.swift | 9 +- .../App/Views/Home/HomeFeedViewIOS.swift | 196 +++++++++++------- .../App/Views/Home/HomeFeedViewModel.swift | 2 +- .../App/Views/Profile/ProfileView.swift | 22 +- .../InternalModels/InternalFilter.swift | 9 - .../Sources/Views/Colors/Colors.swift | 1 + 6 files changed, 146 insertions(+), 93 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/FetcherFilterState.swift b/apple/OmnivoreKit/Sources/App/Views/Home/FetcherFilterState.swift index 4ed322200..277be132a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/FetcherFilterState.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/FetcherFilterState.swift @@ -11,10 +11,15 @@ class FetcherFilterState: ObservableObject { @Published var searchTerm = "" @Published var selectedLabels = [LinkedItemLabel]() @Published var negatedLabels = [LinkedItemLabel]() - - @Published var appliedFilter: InternalFilter? @Published var appliedSort = LinkedItemSort.newest.rawValue + @Published var appliedFilter: InternalFilter? { + didSet { + let filterKey = UserDefaults.standard.string(forKey: "lastSelected-\(folder)-filter") ?? folder + UserDefaults.standard.setValue(appliedFilter?.name, forKey: filterKey) + } + } + init(folder: String) { self.folder = folder } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index bfcdd01bb..252817b29 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -40,10 +40,14 @@ struct AnimatingCellHeight: AnimatableModifier { @AppStorage(UserDefaultKey.homeFeedlayoutPreference.rawValue) var prefersListLayout = true @AppStorage(UserDefaultKey.openAIPrimerDisplayed.rawValue) var openAIPrimerDisplayed = false - @ObservedObject var viewModel: HomeFeedViewModel + @StateObject var viewModel: HomeFeedViewModel @State private var selection = Set() + init(viewModel: HomeFeedViewModel) { + _viewModel = StateObject(wrappedValue: viewModel) + } + func loadItems(isRefresh: Bool) { Task { await viewModel.loadItems(dataService: dataService, isRefresh: isRefresh) } } @@ -54,8 +58,9 @@ struct AnimatingCellHeight: AnimatableModifier { viewModel.fetcher.items.count > 0 && viewModel.filterState.searchTerm.isEmpty && viewModel.filterState.selectedLabels.isEmpty && - viewModel.filterState.negatedLabels.isEmpty && - viewModel.filterState.appliedFilter?.name == "inbox" + viewModel.filterState.negatedLabels.isEmpty + /* && + viewModel.filterState.appliedFilter?.name == "inbox" */ } var body: some View { @@ -66,11 +71,15 @@ struct AnimatingCellHeight: AnimatableModifier { isEditMode: $isEditMode, selection: $selection, viewModel: viewModel, + filterState: viewModel.filterState, showFeatureCards: showFeatureCards ) .refreshable { loadItems(isRefresh: true) } + .onChange(of: viewModel.filterState.appliedFilter?.id) { _ in + loadItems(isRefresh: true) + } .onChange(of: viewModel.filterState.searchTerm) { _ in // Maybe we should debounce this, but // it feels like it works ok without @@ -168,14 +177,15 @@ struct AnimatingCellHeight: AnimatableModifier { let showDate = isListScrolled && !listTitle.isEmpty if let title = viewModel.filterState.appliedFilter?.name { Text(title) - .font(Font.system(size: showDate ? 10 : 18, weight: .semibold)) + .font(Font.system(size: showDate ? 10 : 32, weight: .semibold)) if showDate, prefersListLayout, isListScrolled || !showFeatureCards { Text(listTitle) .font(Font.system(size: 15, weight: .regular)) .foregroundColor(Color.appGrayText) } } - }.frame(maxWidth: .infinity, alignment: .leading) + } + .frame(maxWidth: .infinity, alignment: .bottomLeading) } ToolbarItem(placement: .barTrailing) { Button("", action: {}) @@ -263,6 +273,7 @@ struct AnimatingCellHeight: AnimatableModifier { @Binding var isEditMode: EditMode @Binding var selection: Set @ObservedObject var viewModel: HomeFeedViewModel + @ObservedObject var filterState: FetcherFilterState let showFeatureCards: Bool @@ -292,6 +303,7 @@ struct AnimatingCellHeight: AnimatableModifier { isEditMode: $isEditMode, selection: $selection, viewModel: viewModel, + filterState: filterState, showFeatureCards: showFeatureCards ) } else { @@ -302,11 +314,11 @@ struct AnimatingCellHeight: AnimatableModifier { } }.sheet(isPresented: $viewModel.showLabelsSheet) { FilterByLabelsView( - initiallySelected: viewModel.filterState.selectedLabels, - initiallyNegated: viewModel.filterState.negatedLabels + initiallySelected: filterState.selectedLabels, + initiallyNegated: filterState.negatedLabels ) { - viewModel.filterState.selectedLabels = $0 - viewModel.filterState.negatedLabels = $1 + filterState.selectedLabels = $0 + filterState.negatedLabels = $1 } } .popup(isPresented: $viewModel.showSnackbar) { @@ -347,9 +359,29 @@ struct AnimatingCellHeight: AnimatableModifier { @Binding var selection: Set @ObservedObject var viewModel: HomeFeedViewModel + @ObservedObject var filterState: FetcherFilterState let showFeatureCards: Bool + init(listTitle: Binding, + isListScrolled: Binding, + prefersListLayout: Binding, + isEditMode: Binding, + selection: Binding>, + viewModel: HomeFeedViewModel, + filterState: FetcherFilterState, + showFeatureCards: Bool) + { + self._listTitle = listTitle + self._isListScrolled = isListScrolled + self._prefersListLayout = prefersListLayout + self._isEditMode = isEditMode + self._selection = selection + self.viewModel = viewModel + self.filterState = filterState + self.showFeatureCards = showFeatureCards + } + var filtersHeader: some View { GeometryReader { reader in ScrollView(.horizontal, showsIndicators: false) { @@ -362,7 +394,9 @@ struct AnimatingCellHeight: AnimatableModifier { Menu( content: { ForEach(viewModel.filters) { filter in - Button(filter.name, action: { viewModel.filterState.appliedFilter = filter }) + Button(filter.name, action: { + viewModel.filterState.appliedFilter = filter + }) } }, label: { @@ -399,10 +433,19 @@ struct AnimatingCellHeight: AnimatableModifier { } Spacer() } - .padding(0) } - .listRowSeparator(.hidden) } + .padding(.top, 0) + .padding(.bottom, 10) + .padding(.leading, 15) + .listRowSpacing(0) + .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) + .frame(maxWidth: .infinity, minHeight: 38) + .background(Color.systemBackground) + .overlay(Rectangle() + .padding(.leading, 15) + .frame(width: nil, height: 0.5, alignment: .bottom) + .foregroundColor(isListScrolled ? Color(hex: "#3D3D3D") : Color.systemBackground), alignment: .bottom) .dynamicTypeSize(.small ... .accessibility1) } @@ -515,6 +558,7 @@ struct AnimatingCellHeight: AnimatableModifier { @State var topItem: Models.LibraryItem? func setTopItem(_ item: Models.LibraryItem) { + print("setting top item: ", item) if let date = item.savedAt, let daysAgo = Calendar.current.dateComponents([.day], from: date, to: Date()).day { if daysAgo < 1 { let formatter = DateFormatter() @@ -560,79 +604,80 @@ struct AnimatingCellHeight: AnimatableModifier { } List(selection: $selection) { - filtersHeader - .listRowSeparator(.hidden, edges: .all) - .listRowInsets(.init(top: 0, leading: horizontalInset, bottom: 0, trailing: horizontalInset)) + Section(content: { + if let appliedFilter = viewModel.filterState.appliedFilter, + networkMonitor.status == .disconnected, + !appliedFilter.allowLocalFetch + { + HStack { + Text("This search requires an internet connection.") + .padding() + .foregroundColor(Color.white) + .frame(maxWidth: .infinity, alignment: .center) + } + .background(Color.blue) + .frame(maxWidth: .infinity, alignment: .center) + .listRowSeparator(.hidden, edges: .all) + .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) + } else { + if showFeatureCards { + featureCard + .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) + .listRowSeparator(.hidden, edges: .all) + .modifier(AnimatingCellHeight(height: 190 + 13)) + .onDisappear { + withAnimation { + isListScrolled = true + } + } + .onAppear { + withAnimation { + isListScrolled = false + } + } + } - if let appliedFilter = viewModel.filterState.appliedFilter, - networkMonitor.status == .disconnected, - !appliedFilter.allowLocalFetch - { - HStack { - Text("This search requires an internet connection.") - .padding() - .foregroundColor(Color.white) - .frame(maxWidth: .infinity, alignment: .center) - } - .background(Color.blue) - .frame(maxWidth: .infinity, alignment: .center) - .listRowSeparator(.hidden, edges: .all) - .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) - } else { - if showFeatureCards { - featureCard - .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) - .listRowSeparator(.hidden, edges: .all) - .modifier(AnimatingCellHeight(height: 190 + 13)) - .onDisappear { - withAnimation { - isListScrolled = true + ForEach(Array(viewModel.fetcher.items.enumerated()), id: \.1.unwrappedID) { _, item in + FeedCardNavigationLink( + item: item, + isInMultiSelectMode: viewModel.isInMultiSelectMode, + viewModel: viewModel + ) + .background(GeometryReader { geometry in + Color.clear + .preference(key: ScrollOffsetPreferenceKey.self, value: geometry.frame(in: .named("scroll")).origin) + }) + .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in + print("ScrollOffsetPreferenceKey.self", value, item) + if value.y < 100, value.y > 0 { + if item.savedAt != nil, topItem != item { + setTopItem(item) + } } } - .onAppear { - withAnimation { - isListScrolled = false + .listRowSeparatorTint(Color.thBorderColor) + .listRowInsets(.init(top: 0, leading: horizontalInset, bottom: 10, trailing: horizontalInset)) + .contextMenu { + menuItems(for: item) + } + .swipeActions(edge: .leading, allowsFullSwipe: true) { + ForEach(viewModel.listConfig.leadingSwipeActions, id: \.self) { action in + swipeActionButton(action: action, item: item) } } - } - - ForEach(Array(viewModel.fetcher.items.enumerated()), id: \.1.unwrappedID) { _, item in - FeedCardNavigationLink( - item: item, - isInMultiSelectMode: viewModel.isInMultiSelectMode, - viewModel: viewModel - ) - .background(GeometryReader { geometry in - Color.clear - .preference(key: ScrollOffsetPreferenceKey.self, value: geometry.frame(in: .named("scroll")).origin) - }) - .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in - if value.y < 100, value.y > 0 { - if item.savedAt != nil, topItem != item { - setTopItem(item) + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + ForEach(viewModel.listConfig.trailingSwipeActions, id: \.self) { action in + swipeActionButton(action: action, item: item) } } } - .listRowSeparatorTint(Color.thBorderColor) - .listRowInsets(.init(top: 0, leading: horizontalInset, bottom: 10, trailing: horizontalInset)) - .contextMenu { - menuItems(for: item) - } - .swipeActions(edge: .leading, allowsFullSwipe: true) { - ForEach(viewModel.listConfig.leadingSwipeActions, id: \.self) { action in - swipeActionButton(action: action, item: item) - } - } - .swipeActions(edge: .trailing, allowsFullSwipe: true) { - ForEach(viewModel.listConfig.trailingSwipeActions, id: \.self) { action in - swipeActionButton(action: action, item: item) - } - } } - } + }, header: { + filtersHeader + }) } .padding(0) - .listStyle(PlainListStyle()) + .listStyle(.plain) .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) .coordinateSpace(name: "scroll") } @@ -642,6 +687,9 @@ struct AnimatingCellHeight: AnimatableModifier { viewModel.hideFeatureSection = true } Button(LocalText.cancelGeneric, role: .cancel) { self.showHideFeatureAlert = false } + }.introspectNavigationController { nav in + nav.navigationBar.shadowImage = UIImage() + nav.navigationBar.setBackgroundImage(UIImage(), for: .default) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index a036cbfa0..2ca444e94 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -37,7 +37,7 @@ import Views @Published var filters = [InternalFilter]() - @ObservedObject var filterState: FetcherFilterState + @Published var filterState: FetcherFilterState @AppStorage(UserDefaultKey.hideFeatureSection.rawValue) var hideFeatureSection = false @AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift index a51e29f24..8ec1dc4f9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift @@ -69,8 +69,11 @@ struct ProfileView: View { Form { innerBody } - .navigationTitle(LocalText.genericProfile) - .navigationBarTitleDisplayMode(.inline) +// .navigationTitle("LocalText.genericProfile") +// .navigationBarTitleDisplayMode(.) + .toolbar { + toolbarItems + } #elseif os(macOS) List { innerBody @@ -80,11 +83,16 @@ struct ProfileView: View { #endif } - var dismissButton: some View { - Button( - action: { dismiss() }, - label: { Text(LocalText.genericClose) } - ) + var toolbarItems: some ToolbarContent { + Group { + ToolbarItem(placement: .barLeading) { + VStack(alignment: .leading) { + Text(LocalText.genericProfile) + .font(Font.system(size: 32, weight: .semibold)) + } + .frame(maxWidth: .infinity, alignment: .bottomLeading) + } + } } private var accountSection: some View { diff --git a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalFilter.swift b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalFilter.swift index e103e690f..ab0e54da8 100644 --- a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalFilter.swift +++ b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalFilter.swift @@ -116,15 +116,6 @@ public struct InternalFilter: Encodable, Identifiable, Hashable { [ InternalFilter( id: "following", - name: "Following", - folder: "following", - filter: "in:following", - visible: true, - position: 0, - defaultFilter: true - ), - InternalFilter( - id: "rss", name: "RSS", folder: "following", filter: "in:following label:RSS", diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift index 214444c79..af29d25bb 100644 --- a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift +++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift @@ -43,6 +43,7 @@ public extension Color { static var themeDisabledBG: Color { Color("_themeDisabledBG", bundle: .module) } static var themeSolidBackground: Color { Color("_themeSolidBackground", bundle: .module) } static var thBorderColor: Color { Color("thBorderColor", bundle: .module) } + static var thLibrarySeparator: Color { Color("thLibrarySeparator", bundle: .module) } static var thFeatureSeparator: Color { Color("featureSeparator", bundle: .module) }