mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:06:00 +00:00
Notifications (#275)
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class MockNotificationManager: NotificationManagerProtocol {
|
||||
// MARK: NotificationManagerProtocol
|
||||
|
||||
var isAvailable: Bool {
|
||||
false
|
||||
}
|
||||
|
||||
weak var delegate: NotificationManagerDelegate?
|
||||
|
||||
func start() {
|
||||
delegate?.authorizationStatusUpdated(self, granted: false)
|
||||
}
|
||||
|
||||
func register(with deviceToken: Data) { }
|
||||
|
||||
func registrationFailed(with error: Error) { }
|
||||
|
||||
func showLocalNotification(with title: String, subtitle: String?) { }
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
|
||||
class NotificationManager: NSObject, NotificationManagerProtocol {
|
||||
private let notificationCenter = UNUserNotificationCenter.current()
|
||||
private let clientProxy: ClientProxyProtocol
|
||||
|
||||
init(clientProxy: ClientProxyProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: NotificationManagerProtocol
|
||||
|
||||
weak var delegate: NotificationManagerDelegate?
|
||||
|
||||
var isAvailable: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func start() {
|
||||
let replyAction = UNTextInputNotificationAction(identifier: NotificationConstants.Action.inlineReply,
|
||||
title: ElementL10n.actionQuickReply,
|
||||
options: [])
|
||||
let replyCategory = UNNotificationCategory(identifier: NotificationConstants.Category.reply,
|
||||
actions: [replyAction],
|
||||
intentIdentifiers: [],
|
||||
options: [])
|
||||
notificationCenter.setNotificationCategories([replyCategory])
|
||||
notificationCenter.delegate = self
|
||||
Task {
|
||||
do {
|
||||
let granted = try await notificationCenter.requestAuthorization(options: [.alert, .sound, .badge])
|
||||
MXLog.debug("[NotificationManager] permission granted: \(granted)")
|
||||
|
||||
await MainActor.run {
|
||||
delegate?.authorizationStatusUpdated(self, granted: granted)
|
||||
}
|
||||
} catch {
|
||||
MXLog.debug("[NotificationManager] request authorization failed: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func register(with deviceToken: Data) {
|
||||
setPusher(with: deviceToken, clientProxy: clientProxy)
|
||||
}
|
||||
|
||||
func registrationFailed(with error: Error) { }
|
||||
|
||||
func showLocalNotification(with title: String, subtitle: String?) {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = title
|
||||
if let subtitle {
|
||||
content.subtitle = subtitle
|
||||
}
|
||||
let request = UNNotificationRequest(identifier: ProcessInfo.processInfo.globallyUniqueString,
|
||||
content: content,
|
||||
trigger: nil)
|
||||
Task {
|
||||
do {
|
||||
try await notificationCenter.add(request)
|
||||
MXLog.debug("[NotificationManager] show local notification succeeded")
|
||||
} catch {
|
||||
MXLog.debug("[NotificationManager] show local notification failed: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setPusher(with deviceToken: Data, clientProxy: ClientProxyProtocol) {
|
||||
Task {
|
||||
do {
|
||||
try await clientProxy.setPusher(pushkey: deviceToken.base64EncodedString(),
|
||||
kind: .http,
|
||||
appId: BuildSettings.pusherAppId,
|
||||
appDisplayName: "\(InfoPlistReader.target.bundleDisplayName) (iOS)",
|
||||
deviceDisplayName: UIDevice.current.name,
|
||||
profileTag: pusherProfileTag(),
|
||||
lang: Bundle.preferredLanguages.first ?? "en",
|
||||
url: BuildSettings.pushGatewayBaseURL.absoluteString,
|
||||
format: .eventIdOnly,
|
||||
defaultPayload: [
|
||||
"aps": [
|
||||
"mutable-content": 1,
|
||||
"alert": [
|
||||
"loc-key": "Notification",
|
||||
"loc-args": []
|
||||
]
|
||||
]
|
||||
])
|
||||
MXLog.debug("[NotificationManager] set pusher succeeded")
|
||||
} catch {
|
||||
MXLog.debug("[NotificationManager] set pusher failed: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func pusherProfileTag() -> String {
|
||||
if let currentTag = ElementSettings.shared.pusherProfileTag {
|
||||
return currentTag
|
||||
}
|
||||
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
let newTag = (0..<16).map { _ in
|
||||
let offset = Int.random(in: 0..<chars.count)
|
||||
return String(chars[chars.index(chars.startIndex, offsetBy: offset)])
|
||||
}.joined()
|
||||
|
||||
ElementSettings.shared.pusherProfileTag = newTag
|
||||
return newTag
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UNUserNotificationCenterDelegate
|
||||
|
||||
extension NotificationManager: UNUserNotificationCenterDelegate {
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
||||
willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
|
||||
guard ElementSettings.shared.enableInAppNotifications else {
|
||||
return []
|
||||
}
|
||||
guard let delegate else {
|
||||
return [.badge, .sound, .list, .banner]
|
||||
}
|
||||
|
||||
guard delegate.shouldDisplayInAppNotification(self, content: notification.request.content) else {
|
||||
return []
|
||||
}
|
||||
|
||||
return [.badge, .sound, .list, .banner]
|
||||
}
|
||||
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
||||
didReceive response: UNNotificationResponse) async {
|
||||
switch response.actionIdentifier {
|
||||
case NotificationConstants.Action.inlineReply:
|
||||
guard let response = response as? UNTextInputNotificationResponse else {
|
||||
return
|
||||
}
|
||||
await delegate?.handleInlineReply(self,
|
||||
content: response.notification.request.content,
|
||||
replyText: response.userText)
|
||||
case UNNotificationDefaultActionIdentifier:
|
||||
await delegate?.notificationTapped(self,
|
||||
content: response.notification.request.content)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UserNotifications
|
||||
|
||||
protocol NotificationManagerDelegate: AnyObject {
|
||||
func authorizationStatusUpdated(_ service: NotificationManagerProtocol,
|
||||
granted: Bool)
|
||||
func shouldDisplayInAppNotification(_ service: NotificationManagerProtocol,
|
||||
content: UNNotificationContent) -> Bool
|
||||
func notificationTapped(_ service: NotificationManagerProtocol,
|
||||
content: UNNotificationContent) async
|
||||
func handleInlineReply(_ service: NotificationManagerProtocol,
|
||||
content: UNNotificationContent,
|
||||
replyText: String) async
|
||||
}
|
||||
|
||||
// MARK: - NotificationManagerProtocol
|
||||
|
||||
protocol NotificationManagerProtocol {
|
||||
var isAvailable: Bool { get }
|
||||
var delegate: NotificationManagerDelegate? { get set }
|
||||
|
||||
func start()
|
||||
func register(with deviceToken: Data)
|
||||
func registrationFailed(with error: Error)
|
||||
func showLocalNotification(with title: String, subtitle: String?)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum NotificationConstants {
|
||||
enum UserInfoKey {
|
||||
static let roomIdentifier = "room_id"
|
||||
static let eventIdentifier = "event_id"
|
||||
static let unreadCount = "unread_count"
|
||||
}
|
||||
|
||||
enum Category {
|
||||
static let discard = "discard"
|
||||
static let reply = "reply"
|
||||
}
|
||||
|
||||
enum Action {
|
||||
static let inlineReply = "inline-reply"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class MockNotificationServiceProxy: NotificationServiceProxyProtocol {
|
||||
func notificationItem(roomId: String, eventId: String) async throws -> NotificationItemProxy? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
struct NotificationItemProxy {
|
||||
// let notificationItem: NotificationItem
|
||||
//
|
||||
// init(notificationItem: NotificationItem) {
|
||||
// self.notificationItem = notificationItem
|
||||
// }
|
||||
//
|
||||
// var timelineItemProxy: TimelineItemProxy {
|
||||
// .init(item: notificationItem.item)
|
||||
// }
|
||||
//
|
||||
// var title: String {
|
||||
// notificationItem.title
|
||||
// }
|
||||
//
|
||||
// var subtitle: String? {
|
||||
// notificationItem.subtitle
|
||||
// }
|
||||
//
|
||||
// var isNoisy: Bool {
|
||||
// notificationItem.isNoisy
|
||||
// }
|
||||
//
|
||||
// var avatarUrl: String? {
|
||||
// notificationItem.avatarUrl
|
||||
// }
|
||||
//
|
||||
// var avatarMediaSource: MediaSourceProxy? {
|
||||
// guard let avatarUrl else {
|
||||
// return nil
|
||||
// }
|
||||
// return .init(urlString: avatarUrl)
|
||||
// }
|
||||
|
||||
var title: String {
|
||||
"Title"
|
||||
}
|
||||
|
||||
var subtitle: String? {
|
||||
nil
|
||||
}
|
||||
|
||||
var isNoisy: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var avatarUrl: String? {
|
||||
nil
|
||||
}
|
||||
|
||||
var avatarMediaSource: MediaSourceProxy? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
class NotificationServiceProxy: NotificationServiceProxyProtocol {
|
||||
// private let service: NotificationServiceProtocol
|
||||
|
||||
init(basePath: String,
|
||||
userId: String) {
|
||||
// service = NotificationService(basePath: basePath, userId: userId)
|
||||
}
|
||||
|
||||
func notificationItem(roomId: String, eventId: String) async throws -> NotificationItemProxy? {
|
||||
nil
|
||||
// try await Task.dispatch(on: .global()) {
|
||||
// guard let item = try self.service.getNotificationItem(roomId: roomId, eventId: eventId) else {
|
||||
// return nil
|
||||
// }
|
||||
// return .init(notificationItem: item)
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol NotificationServiceProxyProtocol {
|
||||
func notificationItem(roomId: String, eventId: String) async throws -> NotificationItemProxy?
|
||||
}
|
||||
Reference in New Issue
Block a user