Hang search variables off view model to make them easier to observe
This commit is contained in:
@ -5,22 +5,13 @@ import SwiftUI
|
||||
import Utils
|
||||
|
||||
@MainActor
|
||||
class FetcherFilterState: ObservableObject {
|
||||
struct FetcherFilterState {
|
||||
let folder: String
|
||||
|
||||
@Published var searchTerm = ""
|
||||
@Published var selectedLabels = [LinkedItemLabel]()
|
||||
@Published var negatedLabels = [LinkedItemLabel]()
|
||||
@Published var appliedSort = LinkedItemSort.newest.rawValue
|
||||
let searchTerm: String
|
||||
let selectedLabels: [LinkedItemLabel]
|
||||
let negatedLabels: [LinkedItemLabel]
|
||||
let appliedSort: String
|
||||
|
||||
@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
|
||||
}
|
||||
let appliedFilter: InternalFilter?
|
||||
}
|
||||
|
||||
@ -41,12 +41,11 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
@AppStorage(UserDefaultKey.homeFeedlayoutPreference.rawValue) var prefersListLayout = true
|
||||
@AppStorage(UserDefaultKey.openAIPrimerDisplayed.rawValue) var openAIPrimerDisplayed = false
|
||||
|
||||
@StateObject var viewModel: HomeFeedViewModel
|
||||
|
||||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
@State private var selection = Set<String>()
|
||||
|
||||
init(viewModel: HomeFeedViewModel) {
|
||||
_viewModel = StateObject(wrappedValue: viewModel)
|
||||
_viewModel = ObservedObject(wrappedValue: viewModel)
|
||||
}
|
||||
|
||||
func loadItems(isRefresh: Bool) {
|
||||
@ -57,11 +56,10 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
viewModel.listConfig.hasFeatureCards &&
|
||||
!viewModel.hideFeatureSection &&
|
||||
viewModel.fetcher.items.count > 0 &&
|
||||
viewModel.filterState.searchTerm.isEmpty &&
|
||||
viewModel.filterState.selectedLabels.isEmpty &&
|
||||
viewModel.filterState.negatedLabels.isEmpty
|
||||
/* &&
|
||||
viewModel.filterState.appliedFilter?.name == "inbox" */
|
||||
viewModel.searchTerm.isEmpty &&
|
||||
viewModel.selectedLabels.isEmpty &&
|
||||
viewModel.negatedLabels.isEmpty &&
|
||||
viewModel.appliedFilter?.name == "inbox"
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@ -72,35 +70,31 @@ 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.presentWebContainer) { _ in
|
||||
if !viewModel.presentWebContainer {
|
||||
viewModel.linkRequest = nil
|
||||
}
|
||||
}
|
||||
.onChange(of: viewModel.filterState.searchTerm) { _ in
|
||||
.onChange(of: viewModel.searchTerm) { _ in
|
||||
// Maybe we should debounce this, but
|
||||
// it feels like it works ok without
|
||||
loadItems(isRefresh: true)
|
||||
}
|
||||
.onChange(of: viewModel.filterState.selectedLabels) { _ in
|
||||
.onChange(of: viewModel.selectedLabels) { _ in
|
||||
loadItems(isRefresh: true)
|
||||
}
|
||||
.onChange(of: viewModel.filterState.negatedLabels) { _ in
|
||||
.onChange(of: viewModel.negatedLabels) { _ in
|
||||
loadItems(isRefresh: true)
|
||||
}
|
||||
.onChange(of: viewModel.filterState.appliedFilter) { _ in
|
||||
.onChange(of: viewModel.appliedFilter) { _ in
|
||||
loadItems(isRefresh: true)
|
||||
}
|
||||
.onChange(of: viewModel.filterState.appliedSort) { _ in
|
||||
.onChange(of: viewModel.appliedSort) { _ in
|
||||
loadItems(isRefresh: true)
|
||||
}
|
||||
.sheet(item: $viewModel.itemUnderLabelEdit) { item in
|
||||
@ -140,10 +134,10 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
if let deepLink = DeepLink.make(from: url) {
|
||||
switch deepLink {
|
||||
case let .search(query):
|
||||
viewModel.filterState.searchTerm = query
|
||||
viewModel.searchTerm = query
|
||||
case let .savedSearch(named):
|
||||
if let filter = viewModel.findFilter(dataService, named: named) {
|
||||
viewModel.filterState.appliedFilter = filter
|
||||
viewModel.appliedFilter = filter
|
||||
}
|
||||
case let .webAppLinkRequest(requestID):
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
@ -172,7 +166,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
if viewModel.fetcher.items.isEmpty {
|
||||
loadItems(isRefresh: false)
|
||||
}
|
||||
await viewModel.loadFilters(dataService: dataService, filterState: viewModel.filterState)
|
||||
await viewModel.loadFilters(dataService: dataService)
|
||||
}
|
||||
.environment(\.editMode, self.$isEditMode)
|
||||
}
|
||||
@ -182,9 +176,9 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
ToolbarItem(placement: .barLeading) {
|
||||
VStack(alignment: .leading) {
|
||||
let showDate = isListScrolled && !listTitle.isEmpty
|
||||
if let title = viewModel.filterState.appliedFilter?.name {
|
||||
if let title = viewModel.appliedFilter?.name {
|
||||
Text(title)
|
||||
.font(Font.system(size: showDate ? 10 : 28, weight: .semibold))
|
||||
.font(Font.system(size: showDate ? 10 : 24, weight: .semibold))
|
||||
if showDate, prefersListLayout, isListScrolled || !showFeatureCards {
|
||||
Text(listTitle)
|
||||
.font(Font.system(size: 15, weight: .regular))
|
||||
@ -280,7 +274,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
@Binding var isEditMode: EditMode
|
||||
@Binding var selection: Set<String>
|
||||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
@ObservedObject var filterState: FetcherFilterState
|
||||
|
||||
let showFeatureCards: Bool
|
||||
|
||||
@ -311,7 +304,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
isEditMode: $isEditMode,
|
||||
selection: $selection,
|
||||
viewModel: viewModel,
|
||||
filterState: filterState,
|
||||
showFeatureCards: showFeatureCards
|
||||
)
|
||||
} else {
|
||||
@ -322,11 +314,11 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
}
|
||||
}.sheet(isPresented: $viewModel.showLabelsSheet) {
|
||||
FilterByLabelsView(
|
||||
initiallySelected: filterState.selectedLabels,
|
||||
initiallyNegated: filterState.negatedLabels
|
||||
initiallySelected: viewModel.selectedLabels,
|
||||
initiallyNegated: viewModel.negatedLabels
|
||||
) {
|
||||
filterState.selectedLabels = $0
|
||||
filterState.negatedLabels = $1
|
||||
viewModel.selectedLabels = $0
|
||||
viewModel.negatedLabels = $1
|
||||
}
|
||||
}
|
||||
.popup(isPresented: $viewModel.showSnackbar) {
|
||||
@ -367,7 +359,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
|
||||
@Binding var selection: Set<String>
|
||||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
@ObservedObject var filterState: FetcherFilterState
|
||||
|
||||
let showFeatureCards: Bool
|
||||
|
||||
@ -375,45 +366,43 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
@State var topItem: Models.LibraryItem?
|
||||
@ObservedObject var networkMonitor = NetworkMonitor()
|
||||
|
||||
init(listTitle: Binding<String>,
|
||||
isListScrolled: Binding<Bool>,
|
||||
prefersListLayout: Binding<Bool>,
|
||||
isEditMode: Binding<EditMode>,
|
||||
selection: Binding<Set<String>>,
|
||||
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
|
||||
}
|
||||
// init(listTitle: Binding<String>,
|
||||
// isListScrolled: Binding<Bool>,
|
||||
// prefersListLayout: Binding<Bool>,
|
||||
// isEditMode: Binding<EditMode>,
|
||||
// selection: Binding<Set<String>>,
|
||||
// viewModel: HomeFeedViewModel,
|
||||
// showFeatureCards: Bool)
|
||||
// {
|
||||
// self._listTitle = listTitle
|
||||
// self._isListScrolled = isListScrolled
|
||||
// self._prefersListLayout = prefersListLayout
|
||||
// self._isEditMode = isEditMode
|
||||
// self._selection = selection
|
||||
// self.viewModel = viewModel
|
||||
// self.showFeatureCards = showFeatureCards
|
||||
// }
|
||||
|
||||
var filtersHeader: some View {
|
||||
GeometryReader { reader in
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack {
|
||||
if viewModel.filterState.searchTerm.count > 0 {
|
||||
TextChipButton.makeSearchFilterButton(title: viewModel.filterState.searchTerm) {
|
||||
viewModel.filterState.searchTerm = ""
|
||||
if viewModel.searchTerm.count > 0 {
|
||||
TextChipButton.makeSearchFilterButton(title: viewModel.searchTerm) {
|
||||
viewModel.searchTerm = ""
|
||||
}.frame(maxWidth: reader.size.width * 0.66)
|
||||
} else {
|
||||
Menu(
|
||||
content: {
|
||||
ForEach(viewModel.filters) { filter in
|
||||
Button(filter.name, action: {
|
||||
viewModel.filterState.appliedFilter = filter
|
||||
viewModel.appliedFilter = filter
|
||||
})
|
||||
}
|
||||
},
|
||||
label: {
|
||||
TextChipButton.makeMenuButton(
|
||||
title: viewModel.filterState.appliedFilter?.name ?? "-",
|
||||
title: viewModel.appliedFilter?.name ?? "-",
|
||||
color: .systemGray6
|
||||
)
|
||||
}
|
||||
@ -422,25 +411,25 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
Menu(
|
||||
content: {
|
||||
ForEach(LinkedItemSort.allCases, id: \.self) { sort in
|
||||
Button(sort.displayName, action: { viewModel.filterState.appliedSort = sort.rawValue })
|
||||
Button(sort.displayName, action: { viewModel.appliedSort = sort.rawValue })
|
||||
}
|
||||
},
|
||||
label: {
|
||||
TextChipButton.makeMenuButton(
|
||||
title: LinkedItemSort(rawValue: viewModel.filterState.appliedSort)?.displayName ?? "Sort",
|
||||
title: LinkedItemSort(rawValue: viewModel.appliedSort)?.displayName ?? "Sort",
|
||||
color: .systemGray6
|
||||
)
|
||||
}
|
||||
)
|
||||
TextChipButton.makeAddLabelButton(color: .systemGray6, onTap: { viewModel.showLabelsSheet = true })
|
||||
ForEach(viewModel.filterState.selectedLabels, id: \.self) { label in
|
||||
ForEach(viewModel.selectedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: false) {
|
||||
viewModel.filterState.selectedLabels.removeAll { $0.id == label.id }
|
||||
viewModel.selectedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
ForEach(viewModel.filterState.negatedLabels, id: \.self) { label in
|
||||
ForEach(viewModel.negatedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: true) {
|
||||
viewModel.filterState.negatedLabels.removeAll { $0.id == label.id }
|
||||
viewModel.negatedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
@ -614,7 +603,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
List(selection: $selection) {
|
||||
Section(content: {
|
||||
EmptyView().id("TOP")
|
||||
if let appliedFilter = viewModel.filterState.appliedFilter,
|
||||
if let appliedFilter = viewModel.appliedFilter,
|
||||
networkMonitor.status == .disconnected,
|
||||
!appliedFilter.allowLocalFetch
|
||||
{
|
||||
@ -795,20 +784,20 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
GeometryReader { reader in
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack {
|
||||
if viewModel.filterState.searchTerm.count > 0 {
|
||||
TextChipButton.makeSearchFilterButton(title: viewModel.filterState.searchTerm) {
|
||||
viewModel.filterState.searchTerm = ""
|
||||
if viewModel.searchTerm.count > 0 {
|
||||
TextChipButton.makeSearchFilterButton(title: viewModel.searchTerm) {
|
||||
viewModel.searchTerm = ""
|
||||
}.frame(maxWidth: reader.size.width * 0.66)
|
||||
} else {
|
||||
Menu(
|
||||
content: {
|
||||
ForEach(viewModel.filters, id: \.self) { filter in
|
||||
Button(filter.name, action: { viewModel.filterState.appliedFilter = filter })
|
||||
Button(filter.name, action: { viewModel.appliedFilter = filter })
|
||||
}
|
||||
},
|
||||
label: {
|
||||
TextChipButton.makeMenuButton(
|
||||
title: viewModel.filterState.appliedFilter?.name ?? "-",
|
||||
title: viewModel.appliedFilter?.name ?? "-",
|
||||
color: .systemGray6
|
||||
)
|
||||
}
|
||||
@ -817,25 +806,25 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
Menu(
|
||||
content: {
|
||||
ForEach(LinkedItemSort.allCases, id: \.self) { sort in
|
||||
Button(sort.displayName, action: { viewModel.filterState.appliedSort = sort.rawValue })
|
||||
Button(sort.displayName, action: { viewModel.appliedSort = sort.rawValue })
|
||||
}
|
||||
},
|
||||
label: {
|
||||
TextChipButton.makeMenuButton(
|
||||
title: LinkedItemSort(rawValue: viewModel.filterState.appliedSort)?.displayName ?? "Sort",
|
||||
title: LinkedItemSort(rawValue: viewModel.appliedSort)?.displayName ?? "Sort",
|
||||
color: .systemGray6
|
||||
)
|
||||
}
|
||||
)
|
||||
TextChipButton.makeAddLabelButton(color: .systemGray6, onTap: { viewModel.showLabelsSheet = true })
|
||||
ForEach(viewModel.filterState.selectedLabels, id: \.self) { label in
|
||||
ForEach(viewModel.selectedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: false) {
|
||||
viewModel.filterState.selectedLabels.removeAll { $0.id == label.id }
|
||||
viewModel.selectedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
ForEach(viewModel.filterState.negatedLabels, id: \.self) { label in
|
||||
ForEach(viewModel.negatedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: true) {
|
||||
viewModel.filterState.negatedLabels.removeAll { $0.id == label.id }
|
||||
viewModel.negatedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
|
||||
@ -6,7 +6,9 @@ import Utils
|
||||
import Views
|
||||
|
||||
@MainActor final class HomeFeedViewModel: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
|
||||
var currentDetailViewModel: LinkItemDetailViewModel?
|
||||
let folder: String
|
||||
let fetcher: LibraryItemFetcher
|
||||
let listConfig: LibraryListConfig
|
||||
|
||||
private var fetchedResultsController: NSFetchedResultsController<Models.LibraryItem>?
|
||||
|
||||
@ -31,24 +33,34 @@ import Views
|
||||
@Published var showCommunityModal = false
|
||||
@Published var featureItems = [Models.LibraryItem]()
|
||||
|
||||
@Published var listConfig: LibraryListConfig
|
||||
|
||||
@Published var showSnackbar = false
|
||||
@Published var snackbarOperation: SnackbarOperation?
|
||||
|
||||
@Published var filters = [InternalFilter]()
|
||||
|
||||
@Published var filterState: FetcherFilterState
|
||||
@Published var searchTerm = ""
|
||||
@Published var selectedLabels = [LinkedItemLabel]()
|
||||
@Published var negatedLabels = [LinkedItemLabel]()
|
||||
@Published var appliedSort = LinkedItemSort.newest.rawValue
|
||||
|
||||
@AppStorage(UserDefaultKey.hideFeatureSection.rawValue) var hideFeatureSection = false
|
||||
@AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue
|
||||
|
||||
let fetcher: LibraryItemFetcher
|
||||
@Published var appliedFilter: InternalFilter? {
|
||||
didSet {
|
||||
let filterKey = UserDefaults.standard.string(forKey: "lastSelected-\(folder)-filter") ?? folder
|
||||
UserDefaults.standard.setValue(appliedFilter?.name, forKey: filterKey)
|
||||
}
|
||||
}
|
||||
|
||||
init(fetcher: LibraryItemFetcher, filterState: FetcherFilterState, listConfig: LibraryListConfig) {
|
||||
private var filterState: FetcherFilterState {
|
||||
FetcherFilterState(folder: folder, searchTerm: searchTerm, selectedLabels: selectedLabels, negatedLabels: negatedLabels, appliedSort: appliedSort, appliedFilter: appliedFilter)
|
||||
}
|
||||
|
||||
init(folder: String, fetcher: LibraryItemFetcher, listConfig: LibraryListConfig) {
|
||||
self.folder = folder
|
||||
self.fetcher = fetcher
|
||||
self.listConfig = listConfig
|
||||
self.filterState = filterState
|
||||
super.init()
|
||||
}
|
||||
|
||||
@ -68,10 +80,10 @@ import Views
|
||||
}
|
||||
}
|
||||
|
||||
func loadFilters(dataService: DataService, filterState: FetcherFilterState) async {
|
||||
func loadFilters(dataService: DataService) async {
|
||||
switch filterState.folder {
|
||||
case "following":
|
||||
updateFilters(filterState: filterState, newFilters: InternalFilter.DefaultFollowingFilters)
|
||||
updateFilters(newFilters: InternalFilter.DefaultFollowingFilters)
|
||||
default:
|
||||
var hasLocalResults = false
|
||||
let fetchRequest: NSFetchRequest<Models.Filter> = Filter.fetchRequest()
|
||||
@ -79,15 +91,15 @@ import Views
|
||||
// Load from disk
|
||||
if let results = try? dataService.viewContext.fetch(fetchRequest) {
|
||||
hasLocalResults = true
|
||||
updateFilters(filterState: filterState, newFilters: InternalFilter.make(from: results))
|
||||
updateFilters(newFilters: InternalFilter.make(from: results))
|
||||
}
|
||||
|
||||
let hasResults = hasLocalResults
|
||||
Task.detached {
|
||||
if let downloadedFilters = try? await dataService.filters() {
|
||||
await self.updateFilters(filterState: filterState, newFilters: downloadedFilters)
|
||||
await self.updateFilters(newFilters: downloadedFilters)
|
||||
} else if !hasResults {
|
||||
await self.updateFilters(filterState: filterState, newFilters: InternalFilter.DefaultInboxFilters)
|
||||
await self.updateFilters(newFilters: InternalFilter.DefaultInboxFilters)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,7 +139,7 @@ import Views
|
||||
}
|
||||
}
|
||||
|
||||
func updateFilters(filterState: FetcherFilterState, newFilters: [InternalFilter]) {
|
||||
func updateFilters(newFilters: [InternalFilter]) {
|
||||
let appliedFilterName = UserDefaults.standard.string(forKey: "lastSelected-\(filterState.folder)-filter") ?? filterState.folder
|
||||
|
||||
filters = newFilters
|
||||
@ -135,8 +147,8 @@ import Views
|
||||
.sorted(by: { $0.position < $1.position })
|
||||
+ [InternalFilter.DeletedFilter, InternalFilter.DownloadedFilter]
|
||||
|
||||
if let newFilter = filters.first(where: { $0.name.lowercased() == appliedFilterName }), newFilter.id != filterState.appliedFilter?.id {
|
||||
filterState.appliedFilter = newFilter
|
||||
if let newFilter = filters.first(where: { $0.name.lowercased() == appliedFilterName }), newFilter.id != appliedFilter?.id {
|
||||
appliedFilter = newFilter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,8 +11,8 @@ import SwiftUI
|
||||
|
||||
struct LibraryListView: View {
|
||||
@StateObject private var libraryViewModel = HomeFeedViewModel(
|
||||
folder: "inbox",
|
||||
fetcher: LibraryItemFetcher(),
|
||||
filterState: FetcherFilterState(folder: "inbox"),
|
||||
listConfig: LibraryListConfig(
|
||||
hasFeatureCards: true,
|
||||
leadingSwipeActions: [.pin],
|
||||
|
||||
@ -24,8 +24,8 @@ struct LibraryTabView: View {
|
||||
}
|
||||
|
||||
@StateObject private var followingViewModel = HomeFeedViewModel(
|
||||
folder: "following",
|
||||
fetcher: LibraryItemFetcher(),
|
||||
filterState: FetcherFilterState(folder: "following"),
|
||||
listConfig: LibraryListConfig(
|
||||
hasFeatureCards: false,
|
||||
leadingSwipeActions: [.moveToInbox],
|
||||
@ -35,8 +35,8 @@ struct LibraryTabView: View {
|
||||
)
|
||||
|
||||
@StateObject private var libraryViewModel = HomeFeedViewModel(
|
||||
folder: "inbox",
|
||||
fetcher: LibraryItemFetcher(),
|
||||
filterState: FetcherFilterState(folder: "inbox"),
|
||||
listConfig: LibraryListConfig(
|
||||
hasFeatureCards: true,
|
||||
leadingSwipeActions: [.pin],
|
||||
|
||||
@ -69,8 +69,6 @@ struct ProfileView: View {
|
||||
Form {
|
||||
innerBody
|
||||
}
|
||||
// .navigationTitle("LocalText.genericProfile")
|
||||
// .navigationBarTitleDisplayMode(.)
|
||||
.toolbar {
|
||||
toolbarItems
|
||||
}
|
||||
@ -88,7 +86,7 @@ struct ProfileView: View {
|
||||
ToolbarItem(placement: .barLeading) {
|
||||
VStack(alignment: .leading) {
|
||||
Text(LocalText.genericProfile)
|
||||
.font(Font.system(size: 28, weight: .semibold))
|
||||
.font(Font.system(size: 24, weight: .semibold))
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .bottomLeading)
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import CoreData
|
||||
import Foundation
|
||||
import Models
|
||||
|
||||
public struct InternalFilter: Encodable, Identifiable, Hashable {
|
||||
public struct InternalFilter: Encodable, Identifiable, Hashable, Equatable {
|
||||
public let id: String
|
||||
public let name: String
|
||||
public let folder: String
|
||||
@ -11,6 +11,10 @@ public struct InternalFilter: Encodable, Identifiable, Hashable {
|
||||
public let position: Int
|
||||
public let defaultFilter: Bool
|
||||
|
||||
public static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
|
||||
public static var DownloadedFilter: InternalFilter {
|
||||
InternalFilter(
|
||||
id: "downloaded",
|
||||
|
||||
Reference in New Issue
Block a user