mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-17 15:09:31 +02:00
MESSENGER-3580 Several bugfixes to notification times
This commit is contained in:
@@ -242,7 +242,7 @@ class RiotSharedSettings: NSObject {
|
||||
let toHour = calender.component(.hour, from: weekday.endTime)
|
||||
let toMinute = calender.component(.minute, from: weekday.endTime)
|
||||
|
||||
let entry: [String: Any] = ["is_enabled": weekday.isEnabled, "from_our": fromHour, "from_minute": fromMinute, "to_hour": toHour, "to_minute": toMinute]
|
||||
let entry: [String: Any] = ["is_enabled": weekday.isEnabled, "from_hour": fromHour, "from_minute": fromMinute, "to_hour": toHour, "to_minute": toMinute]
|
||||
entries.append(entry)
|
||||
}
|
||||
|
||||
|
||||
@@ -580,6 +580,15 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||
|
||||
// bwi: could be a system alert wait for diEnter background
|
||||
self.isApplicationActiveFromSystemAlert = YES;
|
||||
|
||||
// bwi: make sure that the shared user defaults have the current notification times settings before the nse is called
|
||||
MXSession *session = [AppDelegate theDelegate].mxSessions.firstObject;
|
||||
if(session) {
|
||||
RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
|
||||
[sharedSettings fetchNotificationTimes];
|
||||
[NotificationTimes storeToSharedUserDefaults];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application
|
||||
|
||||
@@ -121,14 +121,6 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
return
|
||||
}
|
||||
|
||||
// check if the event arrived outside the user defined working time for this room
|
||||
guard !isRoomMuted(roomID: roomId) else {
|
||||
// it's not a Matrix notification, do not change the content
|
||||
MXLog.debug("[NotificationService] didReceiveRequest: This is not a Matrix notification.")
|
||||
contentHandler(UNNotificationContent())
|
||||
return
|
||||
}
|
||||
|
||||
// save this content as fallback content
|
||||
guard let content = request.content.mutableCopy() as? UNMutableNotificationContent else {
|
||||
return
|
||||
@@ -146,10 +138,12 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
// setup user account
|
||||
setup(withRoomId: roomId, eventId: eventId) {
|
||||
// preprocess the payload, will attempt to fetch room display name
|
||||
self.preprocessPayload(forEventId: eventId, roomId: roomId)
|
||||
// fetch the event first
|
||||
self.fetchAndProcessEvent(withEventId: eventId, roomId: roomId)
|
||||
do {
|
||||
try self.preprocessPayload(forEventId: eventId, roomId: roomId) // fetch displayname and check against notification times
|
||||
self.fetchAndProcessEvent(withEventId: eventId, roomId: roomId) // decrypt and parse
|
||||
} catch {
|
||||
contentHandler(UNNotificationContent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,20 +186,60 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
}
|
||||
|
||||
private func isGlobalSettingsNotificationTimesToggleOn() -> Bool {
|
||||
let sharedUserDefaults = MXKAppSettings.standard().sharedUserDefaults
|
||||
|
||||
guard let data = sharedUserDefaults?.data(forKey: NotificationTimes.sharedUserDefaultsKey) else {
|
||||
return false
|
||||
}
|
||||
guard let notificationTimes = try? JSONDecoder().decode(NotificationTimes.self, from: data) else {
|
||||
return false
|
||||
}
|
||||
|
||||
return notificationTimes.isEnabled
|
||||
}
|
||||
|
||||
private func isRoomMuted(roomID: String) -> Bool {
|
||||
private func isRoomSettingsNotificationTimesToggleOn(roomId: String, isDirect: Bool) -> Bool {
|
||||
let sharedUserDefaults = MXKAppSettings.standard().sharedUserDefaults
|
||||
|
||||
guard let data = sharedUserDefaults?.data(forKey: NotificationTimes.sharedUserDefaultsKey) else {
|
||||
return false
|
||||
}
|
||||
guard let notificationTimes = try? JSONDecoder().decode(NotificationTimes.self, from: data) else {
|
||||
return false
|
||||
}
|
||||
|
||||
if isDirect {
|
||||
return notificationTimes.rooms[roomId] == true
|
||||
} else {
|
||||
return notificationTimes.rooms[roomId] != false
|
||||
}
|
||||
}
|
||||
|
||||
private func notificationTimeCheckPassed(roomId: String, isDirect: Bool) -> Bool {
|
||||
guard isGlobalSettingsNotificationTimesToggleOn() else {
|
||||
return true
|
||||
}
|
||||
guard isRoomSettingsNotificationTimesToggleOn(roomId: roomId, isDirect: isDirect) else {
|
||||
return true
|
||||
}
|
||||
return isNotificationTimeEnabled(for: Date())
|
||||
}
|
||||
|
||||
private func isNotificationTimeEnabled(for date: Date) -> Bool {
|
||||
let sharedUserDefaults = MXKAppSettings.standard().sharedUserDefaults
|
||||
|
||||
if #available(iOSApplicationExtension 14.0, *) {
|
||||
guard let data = sharedUserDefaults?.data(forKey: NotificationTimes.sharedUserDefaultsKey), let notificationTimes = try? JSONDecoder().decode(NotificationTimes.self, from: data) else {
|
||||
return false
|
||||
guard let data = sharedUserDefaults?.data(forKey: NotificationTimes.sharedUserDefaultsKey) else {
|
||||
return true
|
||||
}
|
||||
guard let notificationTimes = try? JSONDecoder().decode(NotificationTimes.self, from: data) else {
|
||||
return true
|
||||
}
|
||||
|
||||
let now = Date()
|
||||
|
||||
let calendar = Calendar.current
|
||||
let weekday: NotificationTimesWeekday
|
||||
switch calendar.component(.weekday, from: now) {
|
||||
switch calendar.component(.weekday, from: date) {
|
||||
case 1: // sunday
|
||||
weekday = notificationTimes.weekday(index: 6)
|
||||
case 2: // monday
|
||||
@@ -222,12 +256,18 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
weekday = notificationTimes.weekday(index: 5)
|
||||
default:
|
||||
MXLog.error("[NotificationService] isRoomMuted failed because of an invalid day component.")
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
return notificationTimes.isEnabled && weekday.isEnabled && weekday.startTime <= now && weekday.endTime >= now
|
||||
let nowComponents = calendar.dateComponents([.hour, .minute], from: date)
|
||||
if let hour = nowComponents.hour, let minute = nowComponents.minute {
|
||||
let checkTime = Date.from(hour: hour, minute: minute)
|
||||
return notificationTimes.isEnabled && weekday.isEnabled && weekday.startTime <= checkTime && weekday.endTime >= checkTime
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,22 +342,49 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
return true
|
||||
}
|
||||
|
||||
enum NSEError: Error, LocalizedError {
|
||||
case isProtectionSet
|
||||
case roomSummaryNotAvailable
|
||||
case mutedBecauseOfNotificationTimes
|
||||
case displayNameNotAvailable
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .isProtectionSet:
|
||||
return "isProtectionSet"
|
||||
case .roomSummaryNotAvailable:
|
||||
return "roomSummaryNotAvailable"
|
||||
case .mutedBecauseOfNotificationTimes:
|
||||
return "isProtectionSet"
|
||||
case .displayNameNotAvailable:
|
||||
return "isProtectionSet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to preprocess payload and attach room display name to the best attempt content
|
||||
/// - Parameters:
|
||||
/// - eventId: Event identifier to mutate best attempt content
|
||||
/// - roomId: Room identifier to fetch display name
|
||||
private func preprocessPayload(forEventId eventId: String, roomId: String) {
|
||||
if localAuthenticationService.isProtectionSet {
|
||||
MXLog.debug("[NotificationService] preprocessPayload: Do not preprocess because app protection is set")
|
||||
return
|
||||
private func preprocessPayload(forEventId eventId: String, roomId: String) throws {
|
||||
// If a room summary is available, use the displayname for the best attempt title.
|
||||
guard let roomSummary = NotificationService.backgroundSyncService.roomSummary(forRoomId: roomId) else {
|
||||
throw NSEError.roomSummaryNotAvailable
|
||||
}
|
||||
|
||||
// If a room summary is available, use the displayname for the best attempt title.
|
||||
guard let roomSummary = NotificationService.backgroundSyncService.roomSummary(forRoomId: roomId) else { return }
|
||||
guard let roomDisplayName = roomSummary.displayName else { return }
|
||||
bestAttemptContents[eventId]?.title = roomDisplayName
|
||||
guard notificationTimeCheckPassed(roomId: roomId, isDirect: roomSummary.isDirect) else {
|
||||
throw NSEError.mutedBecauseOfNotificationTimes
|
||||
}
|
||||
|
||||
// At this stage we don't know the message type, so leave the body as set in didReceive.
|
||||
if localAuthenticationService.isProtectionSet {
|
||||
MXLog.debug("[NotificationService] preprocessPayload: Do not preprocess because app protection is set")
|
||||
guard let roomDisplayName = roomSummary.displayName else {
|
||||
throw NSEError.displayNameNotAvailable
|
||||
}
|
||||
|
||||
bestAttemptContents[eventId]?.title = roomDisplayName
|
||||
// At this stage we don't know the message type, so leave the body as set in didReceive.
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchAndProcessEvent(withEventId eventId: String, roomId: String) {
|
||||
|
||||
@@ -19,13 +19,8 @@ import Foundation
|
||||
|
||||
extension Date {
|
||||
static func from(hour: Int, minute: Int) -> Date {
|
||||
let gregorianCalendar = NSCalendar(calendarIdentifier: .gregorian)!
|
||||
|
||||
var dateComponents = DateComponents()
|
||||
dateComponents.hour = hour
|
||||
dateComponents.minute = minute
|
||||
|
||||
let date = gregorianCalendar.date(from: dateComponents)!
|
||||
let calendar = NSCalendar(calendarIdentifier: .gregorian)!
|
||||
let date = calendar.date(bySettingHour: hour, minute: minute, second: 0, of: Date(timeIntervalSinceReferenceDate: 0))!
|
||||
return date
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,5 +92,12 @@ import Combine
|
||||
func disableForRoom(roomID: String) {
|
||||
rooms[roomID] = false
|
||||
}
|
||||
|
||||
static func storeToSharedUserDefaults() {
|
||||
if let data = try? JSONEncoder().encode(NotificationTimes.shared) {
|
||||
let sharedUserDefaults = MXKAppSettings.standard().sharedUserDefaults
|
||||
sharedUserDefaults?.set(data, forKey: NotificationTimes.sharedUserDefaultsKey)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import SwiftUI
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct NotificationTimesView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@State var isOn: Bool
|
||||
@AppStorage("NotificationTimes_selectedDay") var selectedDay: Int = 0
|
||||
@State var weekdays: [NotificationTimesWeekday]
|
||||
@@ -43,14 +44,14 @@ struct NotificationTimesView: View {
|
||||
if let session = session {
|
||||
RiotSharedSettings(session: session).fetchNotificationTimes()
|
||||
}
|
||||
isOn = NotificationTimes.shared.isEnabled
|
||||
weekdays = [NotificationTimes.shared.monday.copy(),
|
||||
NotificationTimes.shared.tuesday.copy(),
|
||||
NotificationTimes.shared.wednesday.copy(),
|
||||
NotificationTimes.shared.thursday.copy(),
|
||||
NotificationTimes.shared.friday.copy(),
|
||||
NotificationTimes.shared.saturday.copy(),
|
||||
NotificationTimes.shared.sunday.copy()]
|
||||
self._isOn = State(initialValue: NotificationTimes.shared.isEnabled)
|
||||
self._weekdays = State(initialValue: [NotificationTimes.shared.monday.copy(),
|
||||
NotificationTimes.shared.tuesday.copy(),
|
||||
NotificationTimes.shared.wednesday.copy(),
|
||||
NotificationTimes.shared.thursday.copy(),
|
||||
NotificationTimes.shared.friday.copy(),
|
||||
NotificationTimes.shared.saturday.copy(),
|
||||
NotificationTimes.shared.sunday.copy()])
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -82,22 +83,28 @@ struct NotificationTimesView: View {
|
||||
.disabled(!hasChanges)
|
||||
}
|
||||
}
|
||||
.onChange(of: isOn) { _ in
|
||||
// hasChanges = true
|
||||
}
|
||||
}
|
||||
|
||||
private func onDoneButton() {
|
||||
hasChanges = false
|
||||
|
||||
NotificationTimes.shared.isEnabled = isOn
|
||||
for i in 0...6 {
|
||||
NotificationTimes.shared.weekday(index: i).startTime = weekdays[i].startTime
|
||||
NotificationTimes.shared.weekday(index: i).endTime = weekdays[i].endTime
|
||||
NotificationTimes.shared.weekday(index: i).isEnabled = weekdays[i].isEnabled
|
||||
}
|
||||
transferChanges(to: NotificationTimes.shared)
|
||||
storeToMatrixAccountData()
|
||||
NotificationTimes.storeToSharedUserDefaults()
|
||||
|
||||
// save the changes to matrix account data
|
||||
presentationMode.wrappedValue.dismiss() // dismiss the screen
|
||||
}
|
||||
|
||||
private func transferChanges(to notificationTimes: NotificationTimes) {
|
||||
notificationTimes.isEnabled = isOn
|
||||
for i in 0...6 {
|
||||
notificationTimes.weekday(index: i).startTime = weekdays[i].startTime
|
||||
notificationTimes.weekday(index: i).endTime = weekdays[i].endTime
|
||||
notificationTimes.weekday(index: i).isEnabled = weekdays[i].isEnabled
|
||||
}
|
||||
}
|
||||
|
||||
private func storeToMatrixAccountData() {
|
||||
if let session = session {
|
||||
_ = RiotSharedSettings(session: session).storeNotificationTimes {
|
||||
print("NotificationTimes successuflly stored to account data")
|
||||
@@ -108,12 +115,6 @@ struct NotificationTimesView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make the new NotificationTimes changes available to the NSE extension
|
||||
if let data = try? JSONEncoder().encode(NotificationTimes.shared) {
|
||||
let sharedUserDefaults = MXKAppSettings.standard().sharedUserDefaults
|
||||
sharedUserDefaults?.set(data, forKey: NotificationTimes.sharedUserDefaultsKey)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class NotificationTimesWeekday: ObservableObject, Identifiable, Codable {
|
||||
let fromMinute = try container.decode(Int.self, forKey: .fromMinute)
|
||||
let toMinute = try container.decode(Int.self, forKey: .toMinute)
|
||||
|
||||
isEnabled = try container.decode(Bool.self, forKey: .toMinute)
|
||||
isEnabled = try container.decode(Bool.self, forKey: .isEnabled)
|
||||
|
||||
id = 0
|
||||
name = ""
|
||||
|
||||
@@ -124,7 +124,7 @@ extension RoomRollsAndRightsPickerViewController {
|
||||
let cell = UITableViewCell(style: .default, reuseIdentifier: "rolls_and_rights_picker_cells")
|
||||
cell.tintColor = ThemeService.shared().theme.tintColor // only for the accessory view
|
||||
cell.backgroundColor = theme.backgroundColor
|
||||
cell.textLabel?.textColor = theme.textPrimaryColor
|
||||
cell.textLabel?.textColor = BWIBuildSettings.shared.useNewBumColors ? theme.tintColor : theme.textPrimaryColor
|
||||
|
||||
let powerLevel = self.powerLevel(for: selectedViewStateProperty)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user