diff --git a/ios/circolapp/circolapp.xcodeproj/project.pbxproj b/ios/circolapp/circolapp.xcodeproj/project.pbxproj index ca67e18..10febec 100644 --- a/ios/circolapp/circolapp.xcodeproj/project.pbxproj +++ b/ios/circolapp/circolapp.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 954E68352574E3890034EBA8 /* iOSServerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954E68342574E3890034EBA8 /* iOSServerApi.swift */; }; 954E683D2574ED9E0034EBA8 /* UserDefaultsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954E683C2574ED9E0034EBA8 /* UserDefaultsExtensions.swift */; }; 9553DB09257173ED006AE167 /* iOSRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9553DB08257173ED006AE167 /* iOSRepository.swift */; }; + 955B7A0E257D098C0091B1F9 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955B7A0D257D098C0091B1F9 /* SearchBar.swift */; }; + 955B7A10257D10A70091B1F9 /* ViewControllerResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955B7A0F257D10A70091B1F9 /* ViewControllerResolver.swift */; }; 95906F48257508660060F5D0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 95906F47257506360060F5D0 /* Assets.xcassets */; }; 95906F4D257510370060F5D0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 95906F4C257510370060F5D0 /* GoogleService-Info.plist */; }; 95BC3BF82572BCF900F24400 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BC3BF72572BCF900F24400 /* AttachmentView.swift */; }; @@ -49,6 +51,8 @@ 954E68342574E3890034EBA8 /* iOSServerApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSServerApi.swift; sourceTree = ""; }; 954E683C2574ED9E0034EBA8 /* UserDefaultsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtensions.swift; sourceTree = ""; }; 9553DB08257173ED006AE167 /* iOSRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSRepository.swift; sourceTree = ""; }; + 955B7A0D257D098C0091B1F9 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = ""; }; + 955B7A0F257D10A70091B1F9 /* ViewControllerResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerResolver.swift; sourceTree = ""; }; 95906F47257506360060F5D0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 95906F4C257510370060F5D0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 959B531E25764726009AD895 /* circolapp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = circolapp.entitlements; sourceTree = ""; }; @@ -100,6 +104,8 @@ 95CA31BF255C28C300AC095B /* CircularView.swift */, 95BC3BF72572BCF900F24400 /* AttachmentView.swift */, 9512D3C0257AB4F60023C3A1 /* NewReminderView.swift */, + 955B7A0D257D098C0091B1F9 /* SearchBar.swift */, + 955B7A0F257D10A70091B1F9 /* ViewControllerResolver.swift */, ); path = View; sourceTree = ""; @@ -278,11 +284,13 @@ 95CA31C0255C28C300AC095B /* CircularView.swift in Sources */, 952DEDDF2576F8DC001DF85D /* SceneDelegate.swift in Sources */, 95CA31B2255C1EDE00AC095B /* ContentView.swift in Sources */, + 955B7A10257D10A70091B1F9 /* ViewControllerResolver.swift in Sources */, 954E68352574E3890034EBA8 /* iOSServerApi.swift in Sources */, 95CA31B0255C1EDE00AC095B /* AppDelegate.swift in Sources */, 95BC3BF82572BCF900F24400 /* AttachmentView.swift in Sources */, 9512D3C1257AB4F60023C3A1 /* NewReminderView.swift in Sources */, 954E683D2574ED9E0034EBA8 /* UserDefaultsExtensions.swift in Sources */, + 955B7A0E257D098C0091B1F9 /* SearchBar.swift in Sources */, 95C46A51255D3A34007A75E5 /* CircularViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/circolapp/circolapp/CircularViewModel.swift b/ios/circolapp/circolapp/CircularViewModel.swift index 5097251..f699dd9 100644 --- a/ios/circolapp/circolapp/CircularViewModel.swift +++ b/ios/circolapp/circolapp/CircularViewModel.swift @@ -25,6 +25,7 @@ class CircularViewModel: ObservableObject { private var circularWatcher: Ktor_ioCloseable? = nil private var userDefaultsObserver: NSKeyValueObservation? = nil private var schoolID = 0 + private var category: Category = .all private let repository: CircularRepository private let key = "school" @@ -58,6 +59,7 @@ class CircularViewModel: ObservableObject { } func startObservingCirculars() { + category = .all stopObserving() circularWatcher = repository.circularDao.getCFlowCirculars(school: Int32(schoolID)).watch { circulars in self.circulars = circulars as! Array; @@ -65,13 +67,15 @@ class CircularViewModel: ObservableObject { } func startObservingFavourites() { + category = .favourites stopObserving() circularWatcher = repository.circularDao.getFavouritesC(school: Int32(schoolID)).watch { circulars in self.circulars = circulars as! Array; } } - func startObservingAlarms() { + func startObservingReminders() { + category = .reminders stopObserving() circularWatcher = repository.circularDao.getCFlowReminders(school: Int32(schoolID)).watch { circulars in self.circulars = circulars as! Array; @@ -81,4 +85,28 @@ class CircularViewModel: ObservableObject { func stopObserving() { circularWatcher?.close() } + + func search(query: String) { + let wrappedQuery = "%" + query + "%" + + stopObserving() + switch category { + case .all: + circularWatcher = repository.circularDao.searchCircularsC(query: wrappedQuery, school: Int32(schoolID)).watch { circulars in + self.circulars = circulars as! Array; + } + case .favourites: + circularWatcher = repository.circularDao.searchFavouritesC(query: wrappedQuery, school: Int32(schoolID)).watch { circulars in + self.circulars = circulars as! Array; + } + case .reminders: + circularWatcher = repository.circularDao.searchRemindersC(query: wrappedQuery, school: Int32(schoolID)).watch { circulars in + self.circulars = circulars as! Array; + } + } + } +} + +enum Category { + case all, favourites, reminders } diff --git a/ios/circolapp/circolapp/View/ContentView.swift b/ios/circolapp/circolapp/View/ContentView.swift index dfe4c4f..0af4a39 100644 --- a/ios/circolapp/circolapp/View/ContentView.swift +++ b/ios/circolapp/circolapp/View/ContentView.swift @@ -22,6 +22,7 @@ import Shared struct ContentView: View { @ObservedObject var circularViewModel = CircularViewModel(repository: iOSRepository.getCircularRepository()) + @ObservedObject var searchBar: SearchBar = SearchBar(placeholder: "Search circulars") var body: some View { NavigationView { @@ -29,6 +30,10 @@ struct ContentView: View { CircularView(circular: circular) } .navigationBarTitle(Text("Circulars"), displayMode: .large) + .addSearchBar(self.searchBar) + .onReceive(searchBar.$text) {query in + self.circularViewModel.search(query: query) + } .onAppear { self.circularViewModel.startObservingCirculars() } @@ -52,7 +57,7 @@ struct ContentView: View { } Button(action: { - self.circularViewModel.startObservingAlarms() + self.circularViewModel.startObservingReminders() }) { Image(systemName: "alarm") } diff --git a/ios/circolapp/circolapp/View/SearchBar.swift b/ios/circolapp/circolapp/View/SearchBar.swift new file mode 100644 index 0000000..e66de81 --- /dev/null +++ b/ios/circolapp/circolapp/View/SearchBar.swift @@ -0,0 +1,48 @@ +import UIKit +import SwiftUI + +class SearchBar: NSObject, ObservableObject { + + @Published var text: String = "" + let searchController: UISearchController = UISearchController(searchResultsController: nil) + + init(placeholder: String) { + super.init() + self.searchController.obscuresBackgroundDuringPresentation = false + self.searchController.searchResultsUpdater = self + + self.searchController.searchBar.placeholder = placeholder + } +} + +extension SearchBar: UISearchResultsUpdating { + + func updateSearchResults(for searchController: UISearchController) { + + // Publish search bar text changes. + if let searchBarText = searchController.searchBar.text { + self.text = searchBarText + } + } +} + +struct SearchBarModifier: ViewModifier { + + let searchBar: SearchBar + + func body(content: Content) -> some View { + content + .overlay( + ViewControllerResolver { viewController in + viewController.navigationItem.searchController = self.searchBar.searchController + } + .frame(width: 0, height: 0) + ) + } +} + +extension View { + func addSearchBar(_ searchBar: SearchBar) -> some View { + return self.modifier(SearchBarModifier(searchBar: searchBar)) + } +} diff --git a/ios/circolapp/circolapp/View/ViewControllerResolver.swift b/ios/circolapp/circolapp/View/ViewControllerResolver.swift new file mode 100644 index 0000000..664e0c1 --- /dev/null +++ b/ios/circolapp/circolapp/View/ViewControllerResolver.swift @@ -0,0 +1,39 @@ +import Foundation +import SwiftUI + +final class ViewControllerResolver: UIViewControllerRepresentable { + + let onResolve: (UIViewController) -> Void + + init(onResolve: @escaping (UIViewController) -> Void) { + self.onResolve = onResolve + } + + func makeUIViewController(context: Context) -> ParentResolverViewController { + ParentResolverViewController(onResolve: onResolve) + } + + func updateUIViewController(_ uiViewController: ParentResolverViewController, context: Context) { } +} + +class ParentResolverViewController: UIViewController { + + let onResolve: (UIViewController) -> Void + + init(onResolve: @escaping (UIViewController) -> Void) { + self.onResolve = onResolve + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("Use init(onResolve:) to instantiate ParentResolverViewController.") + } + + override func didMove(toParent parent: UIViewController?) { + super.didMove(toParent: parent) + + if let parent = parent { + onResolve(parent) + } + } +}