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:
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user