mirror of
https://github.com/Matte23/circolapp.git
synced 2025-12-06 07:29:10 +00:00
Add Spotlight support for iOS
This commit is contained in:
@@ -65,7 +65,7 @@ class CircularLetterViewModel internal constructor(
|
||||
viewModelScope.launch {
|
||||
isNotUpdating = false
|
||||
|
||||
if (!circularRepository.updateCirculars(false).second) {
|
||||
if (circularRepository.updateCirculars(false).second == -1) {
|
||||
showMessage.postValue(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class PollWork(appContext: Context, workerParams: WorkerParameters) :
|
||||
val circularRepository = AndroidCircularRepository.getInstance(applicationContext)
|
||||
|
||||
val result = circularRepository.updateCirculars()
|
||||
if (!result.second)
|
||||
if (result.second == -1)
|
||||
return@coroutineScope Result.retry()
|
||||
|
||||
val newCirculars = result.first
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
95CA31B7255C1EE000AC095B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 95CA31B6255C1EE000AC095B /* Preview Assets.xcassets */; };
|
||||
95CA31C0255C28C300AC095B /* CircularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CA31BF255C28C300AC095B /* CircularView.swift */; };
|
||||
95DB71AB258A1C1500A78033 /* CarteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95DB71AA258A1C1500A78033 /* CarteView.swift */; };
|
||||
95F6666E2590D712006DE74F /* URLUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F6666D2590D712006DE74F /* URLUtils.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -86,6 +87,7 @@
|
||||
95CA31B8255C1EE000AC095B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
95CA31BF255C28C300AC095B /* CircularView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularView.swift; sourceTree = "<group>"; };
|
||||
95DB71AA258A1C1500A78033 /* CarteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarteView.swift; sourceTree = "<group>"; };
|
||||
95F6666D2590D712006DE74F /* URLUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLUtils.swift; sourceTree = "<group>"; };
|
||||
976621FBDDCA894FD23FBA8B /* Pods-circolapp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-circolapp.release.xcconfig"; path = "Target Support Files/Pods-circolapp/Pods-circolapp.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -193,6 +195,7 @@
|
||||
95906F4C257510370060F5D0 /* GoogleService-Info.plist */,
|
||||
95906F47257506360060F5D0 /* Assets.xcassets */,
|
||||
95CA31B5255C1EE000AC095B /* Preview Content */,
|
||||
95F6666D2590D712006DE74F /* URLUtils.swift */,
|
||||
);
|
||||
path = circolapp;
|
||||
sourceTree = "<group>";
|
||||
@@ -382,6 +385,7 @@
|
||||
955B7A0E257D098C0091B1F9 /* SearchBar.swift in Sources */,
|
||||
9554BDB8257E498F00D8925B /* OnboardingView.swift in Sources */,
|
||||
950C1788258E5BD300B2DBFE /* PhoneView.swift in Sources */,
|
||||
95F6666E2590D712006DE74F /* URLUtils.swift in Sources */,
|
||||
95C46A51255D3A34007A75E5 /* CircularViewModel.swift in Sources */,
|
||||
95B4CE142588BC890090D5E8 /* SettingsView.swift in Sources */,
|
||||
);
|
||||
|
||||
@@ -71,14 +71,8 @@ extension AppDelegate : UNUserNotificationCenterDelegate {
|
||||
}
|
||||
|
||||
// Handle new circular notification
|
||||
iOSRepository.getCircularRepository().updateCirculars(returnNewCirculars: false, completionHandler:
|
||||
{ result, error in
|
||||
if let errorReal = error {
|
||||
print(errorReal.localizedDescription)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
let repository = iOSRepository.getCircularRepository()
|
||||
iOSRepository.updateCirculars(circularRepository: repository)
|
||||
|
||||
// UI is updated automatically
|
||||
completionHandler([])
|
||||
@@ -96,12 +90,8 @@ extension AppDelegate : UNUserNotificationCenterDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
iOSRepository.getCircularRepository().updateCirculars(returnNewCirculars: false, completionHandler:
|
||||
{ result, error in
|
||||
if let errorReal = error {
|
||||
print(errorReal.localizedDescription)
|
||||
}
|
||||
})
|
||||
let repository = iOSRepository.getCircularRepository()
|
||||
iOSRepository.updateCirculars(circularRepository: repository)
|
||||
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import CoreSpotlight
|
||||
import MobileCoreServices
|
||||
import Shared
|
||||
|
||||
class iOSRepository {
|
||||
@@ -30,4 +32,64 @@ class iOSRepository {
|
||||
let serverAPI = iOSServerApi.instance.serverAPI
|
||||
return CircularRepository(circularDao: getCircularDao(), serverAPI: serverAPI)
|
||||
}
|
||||
|
||||
public static func updateCirculars(circularRepository: CircularRepository) {
|
||||
circularRepository.updateCirculars(returnNewCirculars: true, completionHandler:
|
||||
{ result, error in
|
||||
if let errorReal = error {
|
||||
print(errorReal.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
// Database was resetted, remove all circulars from spotlight
|
||||
if result?.second == 1 {
|
||||
deleteAllFromSpotlight(reindex: false, serverID: -1)
|
||||
}
|
||||
|
||||
// Index circulars
|
||||
for circular in result!.first as! Array<Circular> {
|
||||
indexToSpotlight(circular: circular)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public static func indexAllToSpotlight(serverID: Int) {
|
||||
let circulars = getCircularDao().getCirculars(school: Int32(serverID))
|
||||
|
||||
for circular in circulars {
|
||||
indexToSpotlight(circular: circular)
|
||||
}
|
||||
}
|
||||
|
||||
public static func indexToSpotlight(circular: Circular) {
|
||||
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String)
|
||||
attributeSet.title = "Circular number \(circular.id)"
|
||||
attributeSet.contentDescription = circular.name
|
||||
attributeSet.identifier = "\(circular.id)"
|
||||
|
||||
let item = CSSearchableItem(uniqueIdentifier: "\(circular.id)", domainIdentifier: "net.underdesk.circolapp", attributeSet: attributeSet)
|
||||
item.expirationDate = Date.distantFuture
|
||||
|
||||
CSSearchableIndex.default().indexSearchableItems([item]) { error in
|
||||
if let error = error {
|
||||
print("Indexing error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func deleteAllFromSpotlight(reindex: Bool, serverID: Int) {
|
||||
CSSearchableIndex.default().deleteAllSearchableItems(completionHandler: {
|
||||
error in
|
||||
if let errorReal = error {
|
||||
print(errorReal.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
if reindex {
|
||||
DispatchQueue.main.async {
|
||||
indexAllToSpotlight(serverID: serverID)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,15 @@ class iOSServerApi {
|
||||
}
|
||||
|
||||
func changeServer(serverID: Int) {
|
||||
// Change server provider
|
||||
let serverID = UserDefaults.standard.integer(forKey: schoolKey)
|
||||
let server = serverCompanion.getServer(serverID: Int32(serverID))
|
||||
serverAPI = ServerAPI(serverName: server)
|
||||
|
||||
// Reset spotlight indexed items
|
||||
iOSRepository.deleteAllFromSpotlight(reindex: true, serverID: serverID)
|
||||
|
||||
// Change FCM topic
|
||||
let nullableOldTopic = UserDefaults.standard.string(forKey: topicKey)
|
||||
let newTopic = serverCompanion.getServerTopic(serverID: Int32(serverID))
|
||||
if (nullableOldTopic == nil || nullableOldTopic != newTopic) {
|
||||
@@ -63,11 +68,6 @@ class iOSServerApi {
|
||||
UserDefaults.standard.set(newTopic, forKey: topicKey)
|
||||
}
|
||||
|
||||
CircularRepository(circularDao: iOSRepository.getCircularDao(), serverAPI: serverAPI).updateCirculars(returnNewCirculars: false, completionHandler:
|
||||
{ result, error in
|
||||
if let errorReal = error {
|
||||
print(errorReal.localizedDescription)
|
||||
}
|
||||
})
|
||||
iOSRepository.updateCirculars(circularRepository: CircularRepository(circularDao: iOSRepository.getCircularDao(), serverAPI: serverAPI))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreSpotlight
|
||||
import SwiftUI
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
@@ -27,6 +28,15 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
self.window = window
|
||||
window.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
if let userActivity = connectionOptions.userActivities.first {
|
||||
handleUserActivity(userActivity: userActivity)
|
||||
}
|
||||
}
|
||||
|
||||
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
||||
// Called when the user open an entry from Spotlight
|
||||
handleUserActivity(userActivity: userActivity)
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
@@ -52,7 +62,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
let schoolID = UserDefaults.standard.integer(forKey: "school")
|
||||
let reminders = iOSRepository.getCircularDao().getReminders(school: Int32(schoolID))
|
||||
|
||||
|
||||
// Remove reminder flag if notification was delivered
|
||||
UNUserNotificationCenter.current().getPendingNotificationRequests { notifications in
|
||||
// This function has to be called from the main thread because it is where database is accessible
|
||||
@@ -76,4 +86,18 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
|
||||
func handleUserActivity(userActivity: NSUserActivity) {
|
||||
if userActivity.activityType == CSSearchableItemActionType {
|
||||
if let uniqueIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
|
||||
guard let circularID = Int64(uniqueIdentifier) else { return }
|
||||
let schoolID = UserDefaults.standard.integer(forKey: "school")
|
||||
|
||||
let circularDao = iOSRepository.getCircularDao()
|
||||
let circular = circularDao.getCircular(id: circularID, school: Int32(schoolID))
|
||||
|
||||
URLUtils.openUrl(url: circular.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
ios/circolapp/circolapp/URLUtils.swift
Normal file
27
ios/circolapp/circolapp/URLUtils.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Circolapp
|
||||
* Copyright (C) 2019-2020 Matteo Schiff
|
||||
*
|
||||
* This program 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
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class URLUtils {
|
||||
static func openUrl(url: String) {
|
||||
guard let safeUrl = URL(string: url.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)!) else { return }
|
||||
UIApplication.shared.open(safeUrl)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import Shared
|
||||
|
||||
struct AttachmentView: View {
|
||||
@@ -33,8 +32,7 @@ struct AttachmentView: View {
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
guard let url = URL(string: attachmentUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)!) else { return }
|
||||
UIApplication.shared.open(url)
|
||||
URLUtils.openUrl(url: attachmentUrl)
|
||||
}) {
|
||||
Image(systemName: "envelope.open.fill")
|
||||
.resizable()
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import Shared
|
||||
|
||||
struct CircularView: View {
|
||||
@@ -49,8 +48,7 @@ struct CircularView: View {
|
||||
if showDetail {
|
||||
HStack {
|
||||
Button(action: {
|
||||
guard let url = URL(string: circular.url.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)!) else { return }
|
||||
UIApplication.shared.open(url)
|
||||
URLUtils.openUrl(url: circular.url)
|
||||
}) {
|
||||
Image(systemName: "envelope.open.fill")
|
||||
.resizable()
|
||||
|
||||
@@ -33,8 +33,8 @@ class CircularViewModel: ObservableObject {
|
||||
self.repository = repository
|
||||
|
||||
schoolID = UserDefaults.standard.integer(forKey: key)
|
||||
updateCirculars()
|
||||
|
||||
iOSRepository.updateCirculars(circularRepository: repository)
|
||||
|
||||
userDefaultsObserver = UserDefaults.standard.observe(\.school, options: [.initial, .new], changeHandler: { (defaults, change) in
|
||||
self.schoolID = change.newValue ?? 0
|
||||
|
||||
@@ -48,15 +48,6 @@ class CircularViewModel: ObservableObject {
|
||||
userDefaultsObserver?.invalidate()
|
||||
}
|
||||
|
||||
func updateCirculars() {
|
||||
self.repository.updateCirculars(returnNewCirculars: false, completionHandler:
|
||||
{ result, error in
|
||||
if let errorReal = error {
|
||||
print(errorReal.localizedDescription)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func startObservingCirculars() {
|
||||
stopObserving()
|
||||
circularWatcher = repository.circularDao.getCFlowCirculars(school: Int32(schoolID)).watch { circulars in
|
||||
|
||||
@@ -6,12 +6,13 @@ class CircularRepository(
|
||||
val circularDao: CircularDao,
|
||||
private val serverAPI: ServerAPI
|
||||
) {
|
||||
suspend fun updateCirculars(returnNewCirculars: Boolean = true): Pair<List<Circular>, Boolean> {
|
||||
suspend fun updateCirculars(returnNewCirculars: Boolean = true): Pair<List<Circular>, Int> {
|
||||
var onlyNewCirculars = listOf<Circular>()
|
||||
|
||||
var errorCode = 0
|
||||
val result = serverAPI.getCircularsFromServer()
|
||||
if (result.second == ServerAPI.Companion.Result.ERROR)
|
||||
return Pair(emptyList(), false)
|
||||
return Pair(emptyList(), -1)
|
||||
|
||||
val oldCirculars = circularDao.getCirculars(serverAPI.serverID())
|
||||
val newCirculars = result.first
|
||||
@@ -19,6 +20,7 @@ class CircularRepository(
|
||||
if (newCirculars.size != oldCirculars.size) {
|
||||
if (newCirculars.size < oldCirculars.size) {
|
||||
circularDao.deleteAll()
|
||||
errorCode = 1
|
||||
}
|
||||
|
||||
if (returnNewCirculars) {
|
||||
@@ -31,6 +33,6 @@ class CircularRepository(
|
||||
|
||||
circularDao.insertAll(newCirculars)
|
||||
}
|
||||
return Pair(onlyNewCirculars, true)
|
||||
return Pair(onlyNewCirculars, errorCode)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user