Merge pull request #3338 from omnivore-app/fix/ios-non-following

Improve the iOS filters when following tab is disabled
This commit is contained in:
Jackson Harper
2024-01-09 14:35:57 +08:00
committed by GitHub
8 changed files with 64 additions and 84 deletions

View File

@ -164,10 +164,13 @@ import Views
var subPredicates = [NSPredicate]()
let folderPredicate = NSPredicate(
format: "%K == %@", #keyPath(Models.LibraryItem.folder), filterState.folder
)
subPredicates.append(folderPredicate)
// TODO: FOLLOWING MIGRATION: invert this once the following migration has completed
if !UserDefaults.standard.bool(forKey: "LibraryTabView::hideFollowingTab") {
let folderPredicate = NSPredicate(
format: "%K == %@", #keyPath(Models.LibraryItem.folder), filterState.folder
)
subPredicates.append(folderPredicate)
}
if let predicate = filterState.appliedFilter?.predicate {
subPredicates.append(predicate)
@ -255,7 +258,11 @@ import Views
}.joined(separator: ","))
}
query.append(" use:folders")
// TODO: FOLLOWING MIGRATION: invert this once the following migration has completed
if !UserDefaults.standard.bool(forKey: "LibraryTabView::hideFollowingTab") {
query.append(" use:folders")
}
print("QUERY: `\(query)`")
return query

View File

@ -19,10 +19,10 @@ struct FiltersHeader: View {
viewModel.searchTerm = ""
}.frame(maxWidth: reader.size.width * 0.66)
} else {
// if UIDevice.isIPhone {
let hideFollowingTab = UserDefaults.standard.bool(forKey: "LibraryTabView::hideFollowingTab")
Menu(
content: {
ForEach(viewModel.filters.filter { $0.folder == viewModel.currentFolder }) { filter in
ForEach(viewModel.filters.filter { hideFollowingTab || $0.folder == viewModel.currentFolder }) { filter in
Button(filter.name, action: {
viewModel.appliedFilter = filter
})
@ -188,9 +188,10 @@ struct AnimatingCellHeight: AnimatableModifier {
@State var showAddLinkView = false
@State var isListScrolled = false
@State var listTitle = ""
@State var isEditMode: EditMode = .inactive
@State var showExpandedAudioPlayer = false
@Binding var isEditMode: EditMode
@EnvironmentObject var dataService: DataService
@EnvironmentObject var audioController: AudioController
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@ -200,8 +201,9 @@ struct AnimatingCellHeight: AnimatableModifier {
@ObservedObject var viewModel: HomeFeedViewModel
@State private var selection = Set<String>()
init(viewModel: HomeFeedViewModel) {
init(viewModel: HomeFeedViewModel, isEditMode: Binding<EditMode>) {
_viewModel = ObservedObject(wrappedValue: viewModel)
_isEditMode = isEditMode
}
func loadItems(isRefresh: Bool) {

View File

@ -167,10 +167,9 @@ enum LoadingBarStyle {
let availableFolders = folderConfigs.keys
let appliedFilterName = UserDefaults.standard.string(forKey: filterKey)
filters = newFilters
filters = (defaultFilters + newFilters)
.filter { availableFolders.contains($0.folder) }
.sorted(by: { $0.position < $1.position })
+ defaultFilters
if let newFilter = filters.first(where: { $0.name.lowercased() == appliedFilterName }), newFilter.id != appliedFilter?.id {
appliedFilter = newFilter

View File

@ -1,34 +0,0 @@
import SwiftUI
import Utils
import Views
@MainActor
struct HomeView: View {
@State private var viewModel: HomeFeedViewModel
init(viewModel: HomeFeedViewModel) {
self.viewModel = viewModel
}
var body: some View {
#if os(iOS)
HomeFeedContainerView(viewModel: viewModel)
#elseif os(macOS)
HomeFeedView(viewModel: viewModel)
.frame(minWidth: 320)
.toolbar {
ToolbarItem {
Button(
action: {
NSApp.keyWindow?.firstResponder?.tryToPerform(
#selector(NSSplitViewController.toggleSidebar(_:)), with: nil
)
},
label: { Label(LocalText.navigationSelectSidebarToggle, systemImage: "sidebar.left") }
)
}
}
#endif
}
}

View File

@ -6,6 +6,7 @@ import SwiftUI
@MainActor
public struct LibrarySplitView: View {
@EnvironmentObject var dataService: DataService
@State var isEditMode: EditMode = .inactive
@StateObject private var viewModel = HomeFeedViewModel(
filterKey: "lastSelected",
@ -37,7 +38,7 @@ public struct LibrarySplitView: View {
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("")
HomeFeedContainerView(viewModel: viewModel)
HomeFeedContainerView(viewModel: viewModel, isEditMode: $isEditMode)
.navigationViewStyle(.stack)
.navigationBarTitleDisplayMode(.inline)
}

View File

@ -21,6 +21,8 @@ struct LibraryTabView: View {
@AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false
@AppStorage(UserDefaultKey.lastSelectedTabItem.rawValue) var selectedTab = "inbox"
@State var isEditMode: EditMode = .inactive
@State var showExpandedAudioPlayer = false
private let syncManager = LibrarySyncManager()
@ -74,14 +76,14 @@ struct LibraryTabView: View {
TabView(selection: $selectedTab) {
if !hideFollowingTab {
NavigationView {
HomeFeedContainerView(viewModel: followingViewModel)
HomeFeedContainerView(viewModel: followingViewModel, isEditMode: $isEditMode)
.navigationBarTitleDisplayMode(.inline)
.navigationViewStyle(.stack)
}.tag("following")
}
NavigationView {
HomeFeedContainerView(viewModel: inboxViewModel)
HomeFeedContainerView(viewModel: inboxViewModel, isEditMode: $isEditMode)
.navigationBarTitleDisplayMode(.inline)
.navigationViewStyle(.stack)
}.tag("inbox")
@ -101,8 +103,10 @@ struct LibraryTabView: View {
.frame(height: 1)
.frame(maxWidth: .infinity)
}
CustomTabBar(selectedTab: $selectedTab, hideFollowingTab: hideFollowingTab)
.padding(0)
if isEditMode != .active {
CustomTabBar(selectedTab: $selectedTab, hideFollowingTab: hideFollowingTab)
.padding(0)
}
}
.fullScreenCover(isPresented: $showExpandedAudioPlayer) {
ExpandedAudioPlayer(

View File

@ -84,6 +84,11 @@ struct FiltersView: View {
.onReceive(NotificationCenter.default.publisher(for: Notification.Name("ScrollToTop"))) { _ in
dismiss()
}
.onChange(of: viewModel.hideFollowingTab) { _ in
UserDefaults.standard.setValue(nil, forKey: "lastSelected")
UserDefaults.standard.setValue(nil, forKey: "lastSelectedFilter-inbox")
UserDefaults.standard.setValue(nil, forKey: "lastSelectedFilter-following")
}
}
private var innerBody: some View {

View File

@ -22,7 +22,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "",
visible: true,
position: -1,
position: 11,
defaultFilter: true
)
}
@ -34,7 +34,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "in:trash",
visible: true,
position: -1,
position: 12,
defaultFilter: true
)
}
@ -46,7 +46,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "in:inbox is:unread",
visible: true,
position: -1,
position: 10,
defaultFilter: true
)
}
@ -58,7 +58,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "following",
filter: "",
visible: true,
position: -1,
position: 11,
defaultFilter: true
)
}
@ -70,7 +70,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "following",
filter: "in:trash",
visible: true,
position: -1,
position: 12,
defaultFilter: true
)
}
@ -82,7 +82,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "in:inbox is:unread",
visible: true,
position: -1,
position: 10,
defaultFilter: true
)
}
@ -95,7 +95,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "",
visible: true,
position: 0,
position: 10,
defaultFilter: true
),
InternalFilter(
@ -104,7 +104,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "",
visible: true,
position: 1,
position: 11,
defaultFilter: true
),
InternalFilter(
@ -113,7 +113,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "",
visible: true,
position: 2,
position: 12,
defaultFilter: true
),
InternalFilter(
@ -122,7 +122,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "",
visible: true,
position: 3,
position: 13,
defaultFilter: true
),
InternalFilter(
@ -131,7 +131,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "is:archived",
visible: true,
position: 4,
position: 14,
defaultFilter: true
),
InternalFilter(
@ -140,7 +140,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "type:file",
visible: true,
position: 5,
position: 15,
defaultFilter: true
),
InternalFilter(
@ -149,7 +149,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "has:highlights",
visible: true,
position: 6,
position: 16,
defaultFilter: true
),
InternalFilter(
@ -158,7 +158,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "inbox",
filter: "in:all",
visible: true,
position: 7,
position: 17,
defaultFilter: true
)
]
@ -172,7 +172,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "following",
filter: "in:following",
visible: true,
position: 1,
position: 10,
defaultFilter: true
),
InternalFilter(
@ -181,7 +181,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "following",
filter: "in:following label:RSS",
visible: true,
position: 2,
position: 12,
defaultFilter: true
),
InternalFilter(
@ -190,7 +190,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
folder: "following",
filter: "in:following label:Newsletter",
visible: true,
position: 3,
position: 13,
defaultFilter: true
)
]
@ -209,9 +209,6 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
}
public var predicate: NSPredicate? {
let folderPredicate = NSPredicate(
format: "%K == %@", #keyPath(Models.LibraryItem.folder), folder
)
let undeletedPredicate = NSPredicate(
format: "%K != %i AND %K != \"DELETED\"",
#keyPath(Models.LibraryItem.serverSyncStatus), Int64(ServerSyncStatus.needsDeletion.rawValue),
@ -226,16 +223,16 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
let feedLabelPredicate = NSPredicate(
format: "SUBQUERY(labels, $label, $label.name == \"RSS\").@count > 0"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, notInArchivePredicate, undeletedPredicate, feedLabelPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [notInArchivePredicate, undeletedPredicate, feedLabelPredicate])
case "Following":
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, notInArchivePredicate, undeletedPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [notInArchivePredicate, undeletedPredicate])
case "Inbox":
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate, notInArchivePredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, notInArchivePredicate])
case "Unread":
let isUnread = NSPredicate(
format: "readAt == nil"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate, notInArchivePredicate, isUnread])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, notInArchivePredicate, isUnread])
case "Non-Feed Items":
// non-archived or deleted items without the Newsletter label
let nonNewsletterLabelPredicate = NSPredicate(
@ -245,7 +242,7 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
format: "NOT SUBQUERY(labels, $label, $label.name == \"RSS\") .@count > 0"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [
folderPredicate, undeletedPredicate, notInArchivePredicate, nonNewsletterLabelPredicate, nonRSSPredicate
undeletedPredicate, notInArchivePredicate, nonNewsletterLabelPredicate, nonRSSPredicate
])
case "Downloaded":
// include pdf only
@ -259,51 +256,50 @@ public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
format: "localPDF.length > 0"
)
let downloadedPDF = NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, isPDFPredicate, localPDFURL])
return NSCompoundPredicate(orPredicateWithSubpredicates: [folderPredicate, hasHTMLContent, downloadedPDF])
return NSCompoundPredicate(orPredicateWithSubpredicates: [hasHTMLContent, downloadedPDF])
case "Newsletters":
// non-archived or deleted items with the Newsletter label
let newsletterLabelPredicate = NSPredicate(
format: "SUBQUERY(labels, $label, $label.name == \"Newsletter\").@count > 0"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate, notInArchivePredicate, newsletterLabelPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, notInArchivePredicate, newsletterLabelPredicate])
case "Feeds":
let feedLabelPredicate = NSPredicate(
format: "SUBQUERY(labels, $label, $label.name == \"RSS\").@count > 0"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate, notInArchivePredicate, feedLabelPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, notInArchivePredicate, feedLabelPredicate])
case "Recommended":
// non-archived or deleted items with the Newsletter label
let recommendedPredicate = NSPredicate(
format: "recommendations.@count > 0"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate, notInArchivePredicate, recommendedPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, notInArchivePredicate, recommendedPredicate])
case "All":
// include everything undeleted
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate])
case "Archived":
let inArchivePredicate = NSPredicate(
format: "%K == %@", #keyPath(Models.LibraryItem.isArchived), Int(truncating: true) as NSNumber
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate, inArchivePredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, inArchivePredicate])
case "Deleted":
let deletedPredicate = NSPredicate(
format: "%K == %i OR %K == \"DELETED\"",
#keyPath(Models.LibraryItem.serverSyncStatus), Int64(ServerSyncStatus.needsDeletion.rawValue),
#keyPath(Models.LibraryItem.state)
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, deletedPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [deletedPredicate])
case "Files":
// include pdf only
let isPDFPredicate = NSPredicate(
format: "%K == %@", #keyPath(Models.LibraryItem.contentReader), "PDF"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [folderPredicate, undeletedPredicate, isPDFPredicate])
return NSCompoundPredicate(andPredicateWithSubpredicates: [undeletedPredicate, isPDFPredicate])
case "Highlights":
let hasHighlightsPredicate = NSPredicate(
format: "highlights.@count > 0"
)
return NSCompoundPredicate(andPredicateWithSubpredicates: [
folderPredicate,
undeletedPredicate,
hasHighlightsPredicate
])