From 661c37deda7d8aac04f4e72cdccad2fdf7fcb2fc Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Sun, 16 Feb 2025 13:10:21 +0100 Subject: [PATCH 1/7] Remove ViewModifier from loop --- Views/BuildingBlocks/ArticleActions.swift | 36 +++++++++++++++++++++++ Views/BuildingBlocks/CopyPasteMenu.swift | 35 ++++++++++++++++++++++ Views/Library/Library.swift | 36 ++--------------------- Views/Library/ZimFilesNew.swift | 27 ++++++++++++++--- 4 files changed, 97 insertions(+), 37 deletions(-) create mode 100644 Views/BuildingBlocks/ArticleActions.swift create mode 100644 Views/BuildingBlocks/CopyPasteMenu.swift diff --git a/Views/BuildingBlocks/ArticleActions.swift b/Views/BuildingBlocks/ArticleActions.swift new file mode 100644 index 00000000..e220cb99 --- /dev/null +++ b/Views/BuildingBlocks/ArticleActions.swift @@ -0,0 +1,36 @@ +// This file is part of Kiwix for iOS & macOS. +// +// Kiwix is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// any later version. +// +// Kiwix is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Kiwix; If not, see https://www.gnu.org/licenses/. + +import SwiftUI + +struct ArticleActions: View { + + let zimFileID: UUID + + var body: some View { + AsyncButton { + guard let url = await ZimFileService.shared.getMainPageURL(zimFileID: zimFileID) else { return } + NotificationCenter.openURL(url, inNewTab: true) + } label: { + Label(LocalString.library_zim_file_context_main_page_label, systemImage: "house") + } + AsyncButton { + guard let url = await ZimFileService.shared.getRandomPageURL(zimFileID: zimFileID) else { return } + NotificationCenter.openURL(url, inNewTab: true) + } label: { + Label(LocalString.library_zim_file_context_random_label, systemImage: "die.face.5") + } + } +} diff --git a/Views/BuildingBlocks/CopyPasteMenu.swift b/Views/BuildingBlocks/CopyPasteMenu.swift new file mode 100644 index 00000000..fe5cc061 --- /dev/null +++ b/Views/BuildingBlocks/CopyPasteMenu.swift @@ -0,0 +1,35 @@ +// This file is part of Kiwix for iOS & macOS. +// +// Kiwix is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// any later version. +// +// Kiwix is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Kiwix; If not, see https://www.gnu.org/licenses/. + +import SwiftUI +import UniformTypeIdentifiers + +struct CopyPasteMenu: View { + + let downloadURL: URL + + var body: some View { + Button { + #if os(macOS) + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(downloadURL.absoluteString, forType: .string) + #elseif os(iOS) + UIPasteboard.general.setValue(downloadURL.absoluteString, forPasteboardType: UTType.url.identifier) + #endif + } label: { + Label(LocalString.library_zim_file_context_copy_url, systemImage: "doc.on.doc") + } + } +} diff --git a/Views/Library/Library.swift b/Views/Library/Library.swift index 15098b8a..ee72c969 100644 --- a/Views/Library/Library.swift +++ b/Views/Library/Library.swift @@ -141,40 +141,10 @@ struct LibraryZimFileContext: ViewModifier { #endif }.contextMenu { if zimFile.fileURLBookmark != nil, !zimFile.isMissing { - Section { articleActions } + Section { ArticleActions(zimFileID: zimFile.fileID) } } - Section { supplementaryActions } - } - } - - @ViewBuilder - var articleActions: some View { - AsyncButton { - guard let url = await ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } - NotificationCenter.openURL(url, inNewTab: true) - } label: { - Label(LocalString.library_zim_file_context_main_page_label, systemImage: "house") - } - AsyncButton { - guard let url = await ZimFileService.shared.getRandomPageURL(zimFileID: zimFile.fileID) else { return } - NotificationCenter.openURL(url, inNewTab: true) - } label: { - Label(LocalString.library_zim_file_context_random_label, systemImage: "die.face.5") - } - } - - @ViewBuilder - var supplementaryActions: some View { - if let downloadURL = zimFile.downloadURL { - Button { - #if os(macOS) - NSPasteboard.general.clearContents() - NSPasteboard.general.setString(downloadURL.absoluteString, forType: .string) - #elseif os(iOS) - UIPasteboard.general.setValue(downloadURL.absoluteString, forPasteboardType: UTType.url.identifier) - #endif - } label: { - Label(LocalString.library_zim_file_context_copy_url, systemImage: "doc.on.doc") + if let downloadURL = zimFile.downloadURL { + Section { CopyPasteMenu(downloadURL: downloadURL) } } } } diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index 276fc68b..a64a62c7 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -14,7 +14,6 @@ // along with Kiwix; If not, see https://www.gnu.org/licenses/. import SwiftUI - import Defaults /// A grid of zim files that are newly available. @@ -42,9 +41,29 @@ struct ZimFilesNew: View { alignment: .leading, spacing: 12 ) { - ForEach(zimFiles.filter { filterPredicate.evaluate(with: $0) }) { zimFile in - ZimFileCell(zimFile, prominent: .name) - .modifier(LibraryZimFileContext(zimFile: zimFile, dismiss: dismiss)) + ForEach(zimFiles.filter { filterPredicate.evaluate(with: $0) }, id: \.fileID) { zimFile in + Group { + #if os(macOS) + Button { + viewModel.selectedZimFile = zimFile + } label: { + ZimFileCell(zimFile, prominent: .name) + }.buttonStyle(.plain) + #elseif os(iOS) + NavigationLink { + ZimFileDetail(zimFile: zimFile, dismissParent: dismiss) + } label: { + ZimFileCell(zimFile, prominent: .name) + } + #endif + }.contextMenu { + if zimFile.fileURLBookmark != nil, !zimFile.isMissing { + Section { ArticleActions(zimFileID: zimFile.fileID) } + } + if let downloadURL = zimFile.downloadURL { + Section { CopyPasteMenu(downloadURL: downloadURL) } + } + } } } .modifier(GridCommon()) From 780522cc01af044fce8180bd4396f90333a463f0 Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Sun, 16 Feb 2025 15:15:37 +0100 Subject: [PATCH 2/7] Use a nested view instead of viewModifier --- Views/Library/Library.swift | 33 ++++++++++++++++---------- Views/Library/ZimFilesCategories.swift | 18 +++++++++----- Views/Library/ZimFilesDownloads.swift | 9 +++---- Views/Library/ZimFilesNew.swift | 26 ++++---------------- Views/Library/ZimFilesOpened.swift | 6 +++-- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Views/Library/Library.swift b/Views/Library/Library.swift index ee72c969..6f124024 100644 --- a/Views/Library/Library.swift +++ b/Views/Library/Library.swift @@ -112,33 +112,39 @@ struct LibraryZimFileDetailSidePanel: ViewModifier { /// On macOS, converts the modified view to a Button that modifies the currently selected zim file /// On iOS, converts the modified view to a NavigationLink that goes to the zim file detail. -struct LibraryZimFileContext: ViewModifier { +struct LibraryZimFileContext: View { @EnvironmentObject private var viewModel: LibraryViewModel - @EnvironmentObject private var navigation: NavigationViewModel - - let zimFile: ZimFile - let dismiss: (() -> Void)? // iOS only - - init(zimFile: ZimFile, dismiss: (() -> Void)?) { + + private let content: Content + private let zimFile: ZimFile + /// iOS only + private let dismiss: (() -> Void)? + + init( + @ViewBuilder content: () -> Content, + zimFile: ZimFile, + dismiss: (() -> Void)? = nil + ) { + self.content = content() self.zimFile = zimFile self.dismiss = dismiss } - - func body(content: Content) -> some View { + + var body: some View { Group { - #if os(macOS) +#if os(macOS) Button { - viewModel.selectedZimFile = zimFile + viewModel.selectedZimFile = ZimFile } label: { content }.buttonStyle(.plain) - #elseif os(iOS) +#elseif os(iOS) NavigationLink { ZimFileDetail(zimFile: zimFile, dismissParent: dismiss) } label: { content } - #endif +#endif }.contextMenu { if zimFile.fileURLBookmark != nil, !zimFile.isMissing { Section { ArticleActions(zimFileID: zimFile.fileID) } @@ -148,4 +154,5 @@ struct LibraryZimFileContext: ViewModifier { } } } + } diff --git a/Views/Library/ZimFilesCategories.swift b/Views/Library/ZimFilesCategories.swift index 25d716bd..4479fe6c 100644 --- a/Views/Library/ZimFilesCategories.swift +++ b/Views/Library/ZimFilesCategories.swift @@ -139,14 +139,18 @@ private struct CategoryGrid: View { ForEach(sections) { section in if sections.count <= 1 { ForEach(section) { zimFile in - ZimFileCell(zimFile, prominent: .size) - .modifier(LibraryZimFileContext(zimFile: zimFile, dismiss: dismiss)) + LibraryZimFileContext( + content: { ZimFileCell(zimFile, prominent: .size) }, + zimFile: zimFile, + dismiss: dismiss) } } else { Section { ForEach(section) { zimFile in - ZimFileCell(zimFile, prominent: .size) - .modifier(LibraryZimFileContext(zimFile: zimFile, dismiss: dismiss)) + LibraryZimFileContext( + content: { ZimFileCell(zimFile, prominent: .size) }, + zimFile: zimFile, + dismiss: dismiss) } } header: { SectionHeader( @@ -244,8 +248,10 @@ private struct CategoryList: View { } } else { List(zimFiles, id: \.self, selection: $viewModel.selectedZimFile) { zimFile in - ZimFileRow(zimFile) - .modifier(LibraryZimFileContext(zimFile: zimFile, dismiss: dismiss)) + LibraryZimFileContext( + content: { ZimFileRow(zimFile) }, + zimFile: zimFile, + dismiss: dismiss) } #if os(macOS) .listStyle(.inset) diff --git a/Views/Library/ZimFilesDownloads.swift b/Views/Library/ZimFilesDownloads.swift index 0e28a270..906cad83 100644 --- a/Views/Library/ZimFilesDownloads.swift +++ b/Views/Library/ZimFilesDownloads.swift @@ -35,10 +35,11 @@ struct ZimFilesDownloads: View { alignment: .leading, spacing: 12 ) { - ForEach(downloadTasks) { downloadTask in - if let zimFile = downloadTask.zimFile { - DownloadTaskCell(zimFile).modifier(LibraryZimFileContext(zimFile: zimFile, dismiss: dismiss)) - } + ForEach(downloadTasks.compactMap(\.zimFile)) { zimFile in + LibraryZimFileContext( + content: { DownloadTaskCell(zimFile) }, + zimFile: zimFile, + dismiss: dismiss) } } .modifier(GridCommon()) diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index a64a62c7..d63f5b56 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -42,28 +42,12 @@ struct ZimFilesNew: View { spacing: 12 ) { ForEach(zimFiles.filter { filterPredicate.evaluate(with: $0) }, id: \.fileID) { zimFile in - Group { - #if os(macOS) - Button { - viewModel.selectedZimFile = zimFile - } label: { + LibraryZimFileContext( + content: { ZimFileCell(zimFile, prominent: .name) - }.buttonStyle(.plain) - #elseif os(iOS) - NavigationLink { - ZimFileDetail(zimFile: zimFile, dismissParent: dismiss) - } label: { - ZimFileCell(zimFile, prominent: .name) - } - #endif - }.contextMenu { - if zimFile.fileURLBookmark != nil, !zimFile.isMissing { - Section { ArticleActions(zimFileID: zimFile.fileID) } - } - if let downloadURL = zimFile.downloadURL { - Section { CopyPasteMenu(downloadURL: downloadURL) } - } - } + }, + zimFile: zimFile, + dismiss: dismiss) } } .modifier(GridCommon()) diff --git a/Views/Library/ZimFilesOpened.swift b/Views/Library/ZimFilesOpened.swift index c9812333..51c7c06c 100644 --- a/Views/Library/ZimFilesOpened.swift +++ b/Views/Library/ZimFilesOpened.swift @@ -35,8 +35,10 @@ struct ZimFilesOpened: View { spacing: 12 ) { ForEach(zimFiles) { zimFile in - ZimFileCell(zimFile, prominent: .name).modifier(LibraryZimFileContext(zimFile: zimFile, - dismiss: self.dismiss)) + LibraryZimFileContext( + content: { ZimFileCell(zimFile, prominent: .name) }, + zimFile: zimFile, + dismiss: dismiss) } } .modifier(GridCommon(edges: .all)) From 4a77b6c20274352aedd8c2e3efbfe2f298ef1113 Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Sun, 16 Feb 2025 15:16:37 +0100 Subject: [PATCH 3/7] Fix typo --- Views/Library/Library.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Views/Library/Library.swift b/Views/Library/Library.swift index 6f124024..99e52acb 100644 --- a/Views/Library/Library.swift +++ b/Views/Library/Library.swift @@ -134,7 +134,7 @@ struct LibraryZimFileContext: View { Group { #if os(macOS) Button { - viewModel.selectedZimFile = ZimFile + viewModel.selectedZimFile = zimFile } label: { content }.buttonStyle(.plain) From 64cfd0eff5688dcb59c7a47ad542bcf5fac98887 Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Sun, 16 Feb 2025 23:18:28 +0100 Subject: [PATCH 4/7] Use a viewModel for ZimFilesNew --- Views/Library/ZimFilesNew.swift | 145 ++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 36 deletions(-) diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index d63f5b56..1abbdf09 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -16,23 +16,88 @@ import SwiftUI import Defaults + +final class ZimFilesNewViewModel: ObservableObject { + + @Published private(set) var zimFiles: [ZimFile] = [] + + private var languageCodes = Set() + private var searchText: String = "" + + private let sortDescriptors = [ + NSSortDescriptor(keyPath: \ZimFile.created, ascending: false), + NSSortDescriptor(keyPath: \ZimFile.name, ascending: true), + NSSortDescriptor(keyPath: \ZimFile.size, ascending: false) + ] + + func update(languageCodes: Set) { + guard languageCodes != self.languageCodes else { return } + self.languageCodes = languageCodes + Task { + await update() + } + } + + func update(searchText: String) { + guard searchText != self.searchText else { return } + self.searchText = searchText + Task { + await update() + } + } + + func update() async { + let searchText = self.searchText + let languageCodes = self.languageCodes + let newZimFiles: [ZimFile] = await withCheckedContinuation { continuation in + Database.shared.performBackgroundTask { context in + let predicate: NSPredicate = Self.buildPredicate( + searchText: searchText, + languageCodes: languageCodes + ) + if let results = try? context.fetch( + ZimFile.fetchRequest( + predicate: predicate, + sortDescriptors: self.sortDescriptors + ) + ) { + continuation.resume(returning: results) + } else { + continuation.resume(returning: []) + } + } + } + await MainActor.run { self.zimFiles = newZimFiles } + } + + private static func buildPredicate(searchText: String, languageCodes: Set) -> NSPredicate { + var predicates = [ + NSPredicate(format: "languageCode IN %@", languageCodes), + NSPredicate(format: "requiresServiceWorkers == false") + ] + if let aMonthAgo = Calendar.current.date(byAdding: .month, value: -3, to: Date()) { + predicates.append(NSPredicate(format: "created > %@", aMonthAgo as CVarArg)) + } + if !searchText.isEmpty { + predicates.append( + NSCompoundPredicate(orPredicateWithSubpredicates: [ + NSPredicate(format: "name CONTAINS[cd] %@", searchText), + NSPredicate(format: "fileDescription CONTAINS[cd] %@", searchText) + ]) + ) + } + return NSCompoundPredicate(andPredicateWithSubpredicates: predicates) + } + +} + /// A grid of zim files that are newly available. struct ZimFilesNew: View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass - @EnvironmentObject var viewModel: LibraryViewModel + @EnvironmentObject var library: LibraryViewModel @Default(.libraryLanguageCodes) private var languageCodes - @FetchRequest( - sortDescriptors: [ - NSSortDescriptor(keyPath: \ZimFile.created, ascending: false), - NSSortDescriptor(keyPath: \ZimFile.name, ascending: true), - NSSortDescriptor(keyPath: \ZimFile.size, ascending: false) - ], - animation: .easeInOut - ) private var zimFiles: FetchedResults + @StateObject private var viewModel = ZimFilesNewViewModel() @State private var searchText = "" - private var filterPredicate: NSPredicate { - ZimFilesNew.buildPredicate(searchText: searchText) - } let dismiss: (() -> Void)? // iOS only var body: some View { @@ -41,7 +106,7 @@ struct ZimFilesNew: View { alignment: .leading, spacing: 12 ) { - ForEach(zimFiles.filter { filterPredicate.evaluate(with: $0) }, id: \.fileID) { zimFile in + ForEach(viewModel.zimFiles, id: \.fileID) { zimFile in LibraryZimFileContext( content: { ZimFileCell(zimFile, prominent: .name) @@ -55,11 +120,19 @@ struct ZimFilesNew: View { .navigationTitle(NavigationItem.new.name) .searchable(text: $searchText) .onAppear { - viewModel.start(isUserInitiated: false) + viewModel.update(searchText: searchText) + viewModel.update(languageCodes: languageCodes) + library.start(isUserInitiated: false) + } + .onChange(of: searchText) { newSearchText in + viewModel.update(searchText: newSearchText) + } + .onChange(of: languageCodes) { newLanguageCodes in + viewModel.update(languageCodes: newLanguageCodes) } .overlay { - if zimFiles.isEmpty { - switch viewModel.state { + if viewModel.zimFiles.isEmpty { + switch library.state { case .inProgress: Message(text: LocalString.zim_file_catalog_fetching_message) case .error: @@ -83,14 +156,14 @@ struct ZimFilesNew: View { } #endif ToolbarItem { - if viewModel.state == .inProgress { + if library.state == .inProgress { ProgressView() #if os(macOS) .scaleEffect(0.5) #endif } else { Button { - viewModel.start(isUserInitiated: true) + library.start(isUserInitiated: true) } label: { Label(LocalString.zim_file_new_button_refresh, systemImage: "arrow.triangle.2.circlepath.circle") @@ -100,24 +173,24 @@ struct ZimFilesNew: View { } } - private static func buildPredicate(searchText: String) -> NSPredicate { - var predicates = [ - NSPredicate(format: "languageCode IN %@", Defaults[.libraryLanguageCodes]), - NSPredicate(format: "requiresServiceWorkers == false") - ] - if let aMonthAgo = Calendar.current.date(byAdding: .month, value: -3, to: Date()) { - predicates.append(NSPredicate(format: "created > %@", aMonthAgo as CVarArg)) - } - if !searchText.isEmpty { - predicates.append( - NSCompoundPredicate(orPredicateWithSubpredicates: [ - NSPredicate(format: "name CONTAINS[cd] %@", searchText), - NSPredicate(format: "fileDescription CONTAINS[cd] %@", searchText) - ]) - ) - } - return NSCompoundPredicate(andPredicateWithSubpredicates: predicates) - } +// private static func buildPredicate(searchText: String) -> NSPredicate { +// var predicates = [ +// NSPredicate(format: "languageCode IN %@", Defaults[.libraryLanguageCodes]), +// NSPredicate(format: "requiresServiceWorkers == false") +// ] +// if let aMonthAgo = Calendar.current.date(byAdding: .month, value: -3, to: Date()) { +// predicates.append(NSPredicate(format: "created > %@", aMonthAgo as CVarArg)) +// } +// if !searchText.isEmpty { +// predicates.append( +// NSCompoundPredicate(orPredicateWithSubpredicates: [ +// NSPredicate(format: "name CONTAINS[cd] %@", searchText), +// NSPredicate(format: "fileDescription CONTAINS[cd] %@", searchText) +// ]) +// ) +// } +// return NSCompoundPredicate(andPredicateWithSubpredicates: predicates) +// } } @available(macOS 13.0, iOS 16.0, *) From 46e56b1372b6bfd3f2fe7fb450e35d21cc87bd95 Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Sun, 16 Feb 2025 23:48:17 +0100 Subject: [PATCH 5/7] Fixlint --- Views/Library/ZimFilesNew.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index 1abbdf09..431b2760 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -16,7 +16,6 @@ import SwiftUI import Defaults - final class ZimFilesNewViewModel: ObservableObject { @Published private(set) var zimFiles: [ZimFile] = [] From b87ad35e8d78904e2400f3cad4d1d82f4e046cbe Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Sun, 16 Feb 2025 23:51:53 +0100 Subject: [PATCH 6/7] Cleanup --- Views/Library/ZimFilesNew.swift | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index 431b2760..633dfea2 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -16,7 +16,7 @@ import SwiftUI import Defaults -final class ZimFilesNewViewModel: ObservableObject { +private final class ViewModel: ObservableObject { @Published private(set) var zimFiles: [ZimFile] = [] @@ -95,7 +95,7 @@ struct ZimFilesNew: View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass @EnvironmentObject var library: LibraryViewModel @Default(.libraryLanguageCodes) private var languageCodes - @StateObject private var viewModel = ZimFilesNewViewModel() + @StateObject private var viewModel = ViewModel() @State private var searchText = "" let dismiss: (() -> Void)? // iOS only @@ -171,25 +171,6 @@ struct ZimFilesNew: View { } } } - -// private static func buildPredicate(searchText: String) -> NSPredicate { -// var predicates = [ -// NSPredicate(format: "languageCode IN %@", Defaults[.libraryLanguageCodes]), -// NSPredicate(format: "requiresServiceWorkers == false") -// ] -// if let aMonthAgo = Calendar.current.date(byAdding: .month, value: -3, to: Date()) { -// predicates.append(NSPredicate(format: "created > %@", aMonthAgo as CVarArg)) -// } -// if !searchText.isEmpty { -// predicates.append( -// NSCompoundPredicate(orPredicateWithSubpredicates: [ -// NSPredicate(format: "name CONTAINS[cd] %@", searchText), -// NSPredicate(format: "fileDescription CONTAINS[cd] %@", searchText) -// ]) -// ) -// } -// return NSCompoundPredicate(andPredicateWithSubpredicates: predicates) -// } } @available(macOS 13.0, iOS 16.0, *) From 97412d7baed3cef05559177dad38b34357c84284 Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Tue, 18 Feb 2025 20:48:31 +0100 Subject: [PATCH 7/7] Add animations back --- Views/Library/ZimFilesNew.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index 633dfea2..5b7baf0d 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -66,7 +66,11 @@ private final class ViewModel: ObservableObject { } } } - await MainActor.run { self.zimFiles = newZimFiles } + await MainActor.run { + withAnimation(.easeInOut) { + self.zimFiles = newZimFiles + } + } } private static func buildPredicate(searchText: String, languageCodes: Set) -> NSPredicate { @@ -112,6 +116,7 @@ struct ZimFilesNew: View { }, zimFile: zimFile, dismiss: dismiss) + .transition(AnyTransition.opacity) } } .modifier(GridCommon())