mirror of
https://github.com/Matte23/circolapp.git
synced 2025-12-06 07:29:10 +00:00
Add reminders to iOS
This commit is contained in:
@@ -43,7 +43,7 @@ class RemindersViewModel internal constructor(
|
|||||||
val circulars: LiveData<List<Circular>> =
|
val circulars: LiveData<List<Circular>> =
|
||||||
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
||||||
if (input.first == null || input.first == "") {
|
if (input.first == null || input.first == "") {
|
||||||
circularRepository.circularDao.getReminders(input.second ?: 0).asLiveData()
|
circularRepository.circularDao.getFlowReminders(input.second ?: 0).asLiveData()
|
||||||
} else {
|
} else {
|
||||||
circularRepository.circularDao.searchReminders(
|
circularRepository.circularDao.searchReminders(
|
||||||
"%${input.first}%",
|
"%${input.first}%",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
9512D3C1257AB4F60023C3A1 /* NewReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9512D3C0257AB4F60023C3A1 /* NewReminderView.swift */; };
|
||||||
952DEDDF2576F8DC001DF85D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952DEDDE2576F8DC001DF85D /* SceneDelegate.swift */; };
|
952DEDDF2576F8DC001DF85D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952DEDDE2576F8DC001DF85D /* SceneDelegate.swift */; };
|
||||||
9547205B2573B688005AA401 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 9547205A2573B688005AA401 /* Settings.bundle */; };
|
9547205B2573B688005AA401 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 9547205A2573B688005AA401 /* Settings.bundle */; };
|
||||||
954E68352574E3890034EBA8 /* iOSServerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954E68342574E3890034EBA8 /* iOSServerApi.swift */; };
|
954E68352574E3890034EBA8 /* iOSServerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954E68342574E3890034EBA8 /* iOSServerApi.swift */; };
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
892B265487980F6A344AC2A7 /* Pods-circolapp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-circolapp.debug.xcconfig"; path = "Target Support Files/Pods-circolapp/Pods-circolapp.debug.xcconfig"; sourceTree = "<group>"; };
|
892B265487980F6A344AC2A7 /* Pods-circolapp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-circolapp.debug.xcconfig"; path = "Target Support Files/Pods-circolapp/Pods-circolapp.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
8BEA78E7C5BBEF0119834B33 /* Pods_circolapp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_circolapp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
8BEA78E7C5BBEF0119834B33 /* Pods_circolapp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_circolapp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
9512D3C0257AB4F60023C3A1 /* NewReminderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewReminderView.swift; sourceTree = "<group>"; };
|
||||||
952C5954255C57650018C010 /* shared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = shared.framework; path = "../../shared/build/xcode-frameworks/shared.framework"; sourceTree = "<group>"; };
|
952C5954255C57650018C010 /* shared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = shared.framework; path = "../../shared/build/xcode-frameworks/shared.framework"; sourceTree = "<group>"; };
|
||||||
952DEDDE2576F8DC001DF85D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
952DEDDE2576F8DC001DF85D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
9547205A2573B688005AA401 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
9547205A2573B688005AA401 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||||
@@ -97,6 +99,7 @@
|
|||||||
95CA31B1255C1EDE00AC095B /* ContentView.swift */,
|
95CA31B1255C1EDE00AC095B /* ContentView.swift */,
|
||||||
95CA31BF255C28C300AC095B /* CircularView.swift */,
|
95CA31BF255C28C300AC095B /* CircularView.swift */,
|
||||||
95BC3BF72572BCF900F24400 /* AttachmentView.swift */,
|
95BC3BF72572BCF900F24400 /* AttachmentView.swift */,
|
||||||
|
9512D3C0257AB4F60023C3A1 /* NewReminderView.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -278,6 +281,7 @@
|
|||||||
954E68352574E3890034EBA8 /* iOSServerApi.swift in Sources */,
|
954E68352574E3890034EBA8 /* iOSServerApi.swift in Sources */,
|
||||||
95CA31B0255C1EDE00AC095B /* AppDelegate.swift in Sources */,
|
95CA31B0255C1EDE00AC095B /* AppDelegate.swift in Sources */,
|
||||||
95BC3BF82572BCF900F24400 /* AttachmentView.swift in Sources */,
|
95BC3BF82572BCF900F24400 /* AttachmentView.swift in Sources */,
|
||||||
|
9512D3C1257AB4F60023C3A1 /* NewReminderView.swift in Sources */,
|
||||||
954E683D2574ED9E0034EBA8 /* UserDefaultsExtensions.swift in Sources */,
|
954E683D2574ED9E0034EBA8 /* UserDefaultsExtensions.swift in Sources */,
|
||||||
95C46A51255D3A34007A75E5 /* CircularViewModel.swift in Sources */,
|
95C46A51255D3A34007A75E5 /* CircularViewModel.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
|
|
||||||
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
|
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
|
||||||
UNUserNotificationCenter.current().requestAuthorization(
|
UNUserNotificationCenter.current().requestAuthorization(
|
||||||
options: authOptions,
|
options: authOptions,
|
||||||
completionHandler: {_, _ in })
|
completionHandler: {_, _ in })
|
||||||
|
|
||||||
application.registerForRemoteNotifications()
|
application.registerForRemoteNotifications()
|
||||||
|
|
||||||
@@ -53,30 +53,56 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
|
|
||||||
extension AppDelegate : UNUserNotificationCenterDelegate {
|
extension AppDelegate : UNUserNotificationCenterDelegate {
|
||||||
|
|
||||||
// Receive displayed notifications for iOS 10 devices.
|
// Receive displayed notifications for iOS 10 devices.
|
||||||
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
||||||
willPresent notification: UNNotification,
|
willPresent notification: UNNotification,
|
||||||
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||||
iOSRepository.getCircularRepository().updateCirculars(returnNewCirculars: false, completionHandler:
|
let userInfo = notification.request.content.userInfo
|
||||||
{ result, error in
|
|
||||||
if let errorReal = error {
|
|
||||||
print(errorReal.localizedDescription)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// UI is updated automatically
|
// Handle reminder
|
||||||
}
|
if ((userInfo["reminder"]) != nil) {
|
||||||
|
let circularDao = iOSRepository.getCircularDao()
|
||||||
|
let circular = circularDao.getCircular(id: userInfo["id"] as! Int64, school: userInfo["school"] as! Int32)
|
||||||
|
iOSRepository.getCircularDao().update(id: circular.id, school: circular.school, favourite: circular.favourite, reminder: false, completionHandler: {_,_ in })
|
||||||
|
|
||||||
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
// Show notification
|
||||||
didReceive response: UNNotificationResponse,
|
completionHandler([.alert, .sound, .badge])
|
||||||
withCompletionHandler completionHandler: @escaping () -> Void) {
|
return
|
||||||
iOSRepository.getCircularRepository().updateCirculars(returnNewCirculars: false, completionHandler:
|
}
|
||||||
{ result, error in
|
|
||||||
if let errorReal = error {
|
|
||||||
print(errorReal.localizedDescription)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
completionHandler()
|
// Handle new circular notification
|
||||||
}
|
iOSRepository.getCircularRepository().updateCirculars(returnNewCirculars: false, completionHandler:
|
||||||
|
{ result, error in
|
||||||
|
if let errorReal = error {
|
||||||
|
print(errorReal.localizedDescription)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// UI is updated automatically
|
||||||
|
completionHandler([])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle user action
|
||||||
|
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
||||||
|
didReceive response: UNNotificationResponse,
|
||||||
|
withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||||
|
let userInfo = response.notification.request.content.userInfo
|
||||||
|
|
||||||
|
// Do nothing if it's a reminder
|
||||||
|
if ((userInfo["reminder"]) != nil) {
|
||||||
|
completionHandler()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iOSRepository.getCircularRepository().updateCirculars(returnNewCirculars: false, completionHandler:
|
||||||
|
{ result, error in
|
||||||
|
if let errorReal = error {
|
||||||
|
print(errorReal.localizedDescription)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
completionHandler()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class CircularViewModel: ObservableObject {
|
|||||||
|
|
||||||
func startObservingAlarms() {
|
func startObservingAlarms() {
|
||||||
stopObserving()
|
stopObserving()
|
||||||
circularWatcher = repository.circularDao.getRemindersC(school: Int32(schoolID)).watch { circulars in
|
circularWatcher = repository.circularDao.getCFlowReminders(school: Int32(schoolID)).watch { circulars in
|
||||||
self.circulars = circulars as! Array<Circular>;
|
self.circulars = circulars as! Array<Circular>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,26 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||||
// Called as the scene transitions from the background to the foreground.
|
// Called as the scene transitions from the background to the foreground.
|
||||||
// Use this method to undo the changes made on entering the background.
|
// Use this method to undo the changes made on entering the background.
|
||||||
|
|
||||||
|
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
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
loop:
|
||||||
|
for circular in reminders {
|
||||||
|
for notification in notifications {
|
||||||
|
if (String(circular.id) == notification.identifier) {
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iOSRepository.getCircularDao().update(id: circular.id, school: circular.school, favourite: circular.favourite, reminder: false, completionHandler: {_,_ in })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import UIKit
|
|||||||
import Shared
|
import Shared
|
||||||
|
|
||||||
struct CircularView: View {
|
struct CircularView: View {
|
||||||
|
@State private var creatingReminder: Bool = false
|
||||||
@State private var showDetail = false
|
@State private var showDetail = false
|
||||||
var circular: Circular
|
var circular: Circular
|
||||||
|
|
||||||
@@ -99,7 +100,16 @@ struct CircularView: View {
|
|||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
iOSRepository.getCircularDao().update(id: circular.id, school: circular.school, favourite: circular.favourite, reminder: !circular.reminder, completionHandler: {_,_ in })
|
if circular.reminder {
|
||||||
|
let center = UNUserNotificationCenter.current()
|
||||||
|
center.removePendingNotificationRequests(withIdentifiers: [String(circular.id)])
|
||||||
|
|
||||||
|
iOSRepository.getCircularDao().update(id: circular.id, school: circular.school, favourite: circular.favourite, reminder: false, completionHandler: {_,_ in })
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.creatingReminder = true
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: circular.reminder ? "alarm.fill" : "alarm")
|
Image(systemName: circular.reminder ? "alarm.fill" : "alarm")
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
@@ -107,10 +117,13 @@ struct CircularView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
.sheet(isPresented: self.$creatingReminder) {
|
||||||
|
NewReminderView(circular: circular)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach(0..<circular.attachmentsNames.count, id: \.self) { index in
|
ForEach(0..<circular.attachmentsNames.count, id: \.self) { index in
|
||||||
AttachmentView(attachmentName: circular.attachmentsNames[index] as! String, attachmentUrl: circular.attachmentsUrls[index] as! String)
|
AttachmentView(attachmentName: circular.attachmentsNames[index] as! String, attachmentUrl: circular.attachmentsUrls[index] as! String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
79
ios/circolapp/circolapp/View/NewReminderView.swift
Normal file
79
ios/circolapp/circolapp/View/NewReminderView.swift
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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 SwiftUI
|
||||||
|
import Shared
|
||||||
|
|
||||||
|
struct NewReminderView: View {
|
||||||
|
@State private var reminderDate = Date()
|
||||||
|
@Environment(\.presentationMode) var presentationMode
|
||||||
|
var circular: Circular
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationView {
|
||||||
|
DatePicker("Pick a date for the reminder", selection: $reminderDate, in: Date()...)
|
||||||
|
.datePickerStyle(WheelDatePickerStyle())
|
||||||
|
.labelsHidden()
|
||||||
|
.padding()
|
||||||
|
.navigationBarTitle(Text("New reminder"), displayMode: .inline)
|
||||||
|
.navigationBarItems(leading: Button(action: {
|
||||||
|
self.presentationMode.wrappedValue.dismiss()
|
||||||
|
}) {
|
||||||
|
Text("Cancel")
|
||||||
|
}, trailing: Button(action: {
|
||||||
|
createReminder()
|
||||||
|
self.presentationMode.wrappedValue.dismiss()
|
||||||
|
}) {
|
||||||
|
Text("Done")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createReminder() {
|
||||||
|
iOSRepository.getCircularDao().update(id: circular.id, school: circular.school, favourite: circular.favourite, reminder: true, completionHandler: {_,_ in })
|
||||||
|
|
||||||
|
let center = UNUserNotificationCenter.current()
|
||||||
|
|
||||||
|
let content = UNMutableNotificationContent()
|
||||||
|
content.title = "Circular number " + String(circular.id)
|
||||||
|
content.body = circular.name
|
||||||
|
content.sound = UNNotificationSound.default
|
||||||
|
content.userInfo["reminder"] = true
|
||||||
|
content.userInfo["id"] = circular.id
|
||||||
|
content.userInfo["school"] = circular.school
|
||||||
|
|
||||||
|
let comps = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: reminderDate)
|
||||||
|
let trigger = UNCalendarNotificationTrigger(dateMatching: comps, repeats: false)
|
||||||
|
|
||||||
|
let request = UNNotificationRequest(identifier: String(circular.id), content: content, trigger: trigger)
|
||||||
|
|
||||||
|
center.add(request) { (error) in
|
||||||
|
if error != nil {
|
||||||
|
print("Error \(String(describing: error))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NewReminderView_Previews: PreviewProvider {
|
||||||
|
static var previewCircular = Circular(id: 1, school: 0, name: "This is a circular", url: "http://example.com", date: "19/11/2020", favourite: false, reminder: false, attachmentsNames: [], attachmentsUrls: [])
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
NewReminderView(circular: previewCircular)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -83,9 +83,10 @@ class CircularDao(
|
|||||||
.mapToList()
|
.mapToList()
|
||||||
fun searchFavouritesC(query: String, school: Int) = searchFavourites(query, school).wrap()
|
fun searchFavouritesC(query: String, school: Int) = searchFavourites(query, school).wrap()
|
||||||
|
|
||||||
fun getReminders(school: Int) =
|
fun getReminders(school: Int) = appDatabaseQueries.getReminders(school.toLong(), circularMapper).executeAsList()
|
||||||
|
fun getFlowReminders(school: Int) =
|
||||||
appDatabaseQueries.getReminders(school.toLong(), circularMapper).asFlow().mapToList()
|
appDatabaseQueries.getReminders(school.toLong(), circularMapper).asFlow().mapToList()
|
||||||
fun getRemindersC(school: Int) = getReminders(school).wrap()
|
fun getCFlowReminders(school: Int) = getFlowReminders(school).wrap()
|
||||||
|
|
||||||
fun searchReminders(query: String, school: Int) =
|
fun searchReminders(query: String, school: Int) =
|
||||||
appDatabaseQueries.searchReminders(school.toLong(), query, circularMapper).asFlow()
|
appDatabaseQueries.searchReminders(school.toLong(), query, circularMapper).asFlow()
|
||||||
|
|||||||
Reference in New Issue
Block a user