mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Disable Local Push Notifications + Start Handling invite notifications (#882)
* disabled local pushes and fixed the issue with the image rendering * disabled local notifications for now and started implementing invite notificatons * disabled local notifications for now and started implementing invite notificatons * removed code that should not have changed * removed useless comment * refactored name for category * fixed a test * fix * fix for missing try await * pr comments
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
{
|
{
|
||||||
"identity" : "compound-ios",
|
"identity" : "compound-ios",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/vector-im/compound-ios",
|
"location" : "https://github.com/vector-im/compound-ios.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "b4f34edce9003ba1fde31500312f317b728f4ee3"
|
"revision" : "b4f34edce9003ba1fde31500312f317b728f4ee3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -507,15 +507,14 @@ extension AppCoordinator: NotificationManagerDelegate {
|
|||||||
func notificationTapped(_ service: NotificationManagerProtocol, content: UNNotificationContent) async {
|
func notificationTapped(_ service: NotificationManagerProtocol, content: UNNotificationContent) async {
|
||||||
MXLog.info("[AppCoordinator] tappedNotification")
|
MXLog.info("[AppCoordinator] tappedNotification")
|
||||||
|
|
||||||
// We store the room identifier into the thread identifier
|
guard let roomID = content.roomID,
|
||||||
guard !content.threadIdentifier.isEmpty,
|
|
||||||
content.receiverID != nil else {
|
content.receiverID != nil else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle here the account switching when available
|
// Handle here the account switching when available
|
||||||
|
|
||||||
handleAppRoute(.room(roomID: content.threadIdentifier))
|
handleAppRoute(.room(roomID: roomID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleInlineReply(_ service: NotificationManagerProtocol, content: UNNotificationContent, replyText: String) async {
|
func handleInlineReply(_ service: NotificationManagerProtocol, content: UNNotificationContent, replyText: String) async {
|
||||||
|
|||||||
@@ -155,6 +155,8 @@ final class AppSettings {
|
|||||||
@UserPreference(key: UserDefaultsKeys.enableInAppNotifications, defaultValue: true, storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.enableInAppNotifications, defaultValue: true, storageType: .userDefaults(store))
|
||||||
var enableInAppNotifications
|
var enableInAppNotifications
|
||||||
|
|
||||||
|
let enableLocalPushNotifications = false
|
||||||
|
|
||||||
/// Tag describing which set of device specific rules a pusher executes.
|
/// Tag describing which set of device specific rules a pusher executes.
|
||||||
@UserPreference(key: UserDefaultsKeys.pusherProfileTag, storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.pusherProfileTag, storageType: .userDefaults(store))
|
||||||
var pusherProfileTag: String?
|
var pusherProfileTag: String?
|
||||||
|
|||||||
@@ -125,9 +125,9 @@ extension UNMutableNotificationContent {
|
|||||||
iconType: NotificationIconType) async throws -> UNMutableNotificationContent {
|
iconType: NotificationIconType) async throws -> UNMutableNotificationContent {
|
||||||
var image: INImage?
|
var image: INImage?
|
||||||
if let mediaSource = iconType.mediaSource {
|
if let mediaSource = iconType.mediaSource {
|
||||||
switch await mediaProvider?.loadFileFromSource(mediaSource) {
|
switch await mediaProvider?.loadImageDataFromSource(mediaSource) {
|
||||||
case .success(let mediaFile):
|
case .success(let data):
|
||||||
image = try INImage(imageData: Data(contentsOf: mediaFile.url))
|
image = INImage(imageData: data)
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
MXLog.error("Couldn't add sender icon: \(error)")
|
MXLog.error("Couldn't add sender icon: \(error)")
|
||||||
case .none:
|
case .none:
|
||||||
|
|||||||
@@ -110,8 +110,10 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
client.setDelegate(delegate: delegate)
|
client.setDelegate(delegate: delegate)
|
||||||
|
|
||||||
// Set up sync listener for generating local notifications.
|
// Set up sync listener for generating local notifications.
|
||||||
await Task.dispatch(on: clientQueue) {
|
if ServiceLocator.shared.settings.enableLocalPushNotifications {
|
||||||
client.setNotificationDelegate(notificationDelegate: delegate)
|
await Task.dispatch(on: clientQueue) {
|
||||||
|
client.setNotificationDelegate(notificationDelegate: delegate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configureSlidingSync()
|
configureSlidingSync()
|
||||||
@@ -326,7 +328,9 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
buildAndConfigureVisibleRoomsSlidingSyncList()
|
buildAndConfigureVisibleRoomsSlidingSyncList()
|
||||||
buildAndConfigureAllRoomsSlidingSyncList()
|
buildAndConfigureAllRoomsSlidingSyncList()
|
||||||
buildAndConfigureInvitesSlidingSyncList()
|
buildAndConfigureInvitesSlidingSyncList()
|
||||||
buildAndConfigureNotificationsSlidingSyncList()
|
if ServiceLocator.shared.settings.enableLocalPushNotifications {
|
||||||
|
buildAndConfigureNotificationsSlidingSyncList()
|
||||||
|
}
|
||||||
|
|
||||||
guard let visibleRoomsListBuilder else {
|
guard let visibleRoomsListBuilder else {
|
||||||
MXLog.error("Visible rooms sliding sync view unavailable")
|
MXLog.error("Visible rooms sliding sync view unavailable")
|
||||||
@@ -537,11 +541,13 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
MXLog.error("Invites sliding sync view unavailable")
|
MXLog.error("Invites sliding sync view unavailable")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let notificationsListBuilder {
|
if ServiceLocator.shared.settings.enableLocalPushNotifications {
|
||||||
MXLog.info("Registering notifications view")
|
if let notificationsListBuilder {
|
||||||
_ = slidingSync?.addList(listBuilder: notificationsListBuilder)
|
MXLog.info("Registering notifications view")
|
||||||
} else {
|
_ = slidingSync?.addList(listBuilder: notificationsListBuilder)
|
||||||
MXLog.error("Notifications sliding sync view unavailable")
|
} else {
|
||||||
|
MXLog.error("Notifications sliding sync view unavailable")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,14 +34,19 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
|
|||||||
weak var delegate: NotificationManagerDelegate?
|
weak var delegate: NotificationManagerDelegate?
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
let replyAction = UNTextInputNotificationAction(identifier: NotificationConstants.Action.inlineReply,
|
// Not implemented yet
|
||||||
title: L10n.actionQuickReply,
|
// let replyAction = UNTextInputNotificationAction(identifier: NotificationConstants.Action.inlineReply,
|
||||||
options: [])
|
// title: L10n.actionQuickReply,
|
||||||
let replyCategory = UNNotificationCategory(identifier: NotificationConstants.Category.reply,
|
// options: [])
|
||||||
actions: [replyAction],
|
let messageCategory = UNNotificationCategory(identifier: NotificationConstants.Category.message,
|
||||||
intentIdentifiers: [],
|
actions: [],
|
||||||
options: [])
|
intentIdentifiers: [],
|
||||||
notificationCenter.setNotificationCategories([replyCategory])
|
options: [])
|
||||||
|
let inviteCategory = UNNotificationCategory(identifier: NotificationConstants.Category.invite,
|
||||||
|
actions: [],
|
||||||
|
intentIdentifiers: [],
|
||||||
|
options: [])
|
||||||
|
notificationCenter.setNotificationCategories([messageCategory, inviteCategory])
|
||||||
notificationCenter.delegate = self
|
notificationCenter.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ enum NotificationConstants {
|
|||||||
|
|
||||||
enum Category {
|
enum Category {
|
||||||
static let discard = "discard"
|
static let discard = "discard"
|
||||||
static let reply = "reply"
|
static let message = "message"
|
||||||
|
static let invite = "invite"
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
|
|||||||
@@ -33,13 +33,15 @@ protocol NotificationItemProxyProtocol {
|
|||||||
|
|
||||||
var roomDisplayName: String { get }
|
var roomDisplayName: String { get }
|
||||||
|
|
||||||
|
var roomCanonicalAlias: String? { get }
|
||||||
|
|
||||||
var roomAvatarMediaSource: MediaSourceProxy? { get }
|
var roomAvatarMediaSource: MediaSourceProxy? { get }
|
||||||
|
|
||||||
var isNoisy: Bool { get }
|
var isNoisy: Bool { get }
|
||||||
|
|
||||||
var isDirect: Bool { get }
|
var isDirect: Bool { get }
|
||||||
|
|
||||||
var isEncrypted: Bool { get }
|
var isEncrypted: Bool? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationItemProxyProtocol {
|
extension NotificationItemProxyProtocol {
|
||||||
@@ -73,6 +75,10 @@ struct NotificationItemProxy: NotificationItemProxyProtocol {
|
|||||||
notificationItem.roomDisplayName
|
notificationItem.roomDisplayName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var roomCanonicalAlias: String? {
|
||||||
|
notificationItem.roomCanonicalAlias
|
||||||
|
}
|
||||||
|
|
||||||
var isNoisy: Bool {
|
var isNoisy: Bool {
|
||||||
notificationItem.isNoisy
|
notificationItem.isNoisy
|
||||||
}
|
}
|
||||||
@@ -81,8 +87,8 @@ struct NotificationItemProxy: NotificationItemProxyProtocol {
|
|||||||
notificationItem.isDirect
|
notificationItem.isDirect
|
||||||
}
|
}
|
||||||
|
|
||||||
var isEncrypted: Bool {
|
var isEncrypted: Bool? {
|
||||||
notificationItem.isEncrypted ?? false
|
notificationItem.isEncrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
var senderAvatarMediaSource: MediaSourceProxy? {
|
var senderAvatarMediaSource: MediaSourceProxy? {
|
||||||
@@ -119,13 +125,15 @@ struct EmptyNotificationItemProxy: NotificationItemProxyProtocol {
|
|||||||
|
|
||||||
var roomDisplayName: String { "" }
|
var roomDisplayName: String { "" }
|
||||||
|
|
||||||
|
var roomCanonicalAlias: String? { nil }
|
||||||
|
|
||||||
var roomAvatarURL: String? { nil }
|
var roomAvatarURL: String? { nil }
|
||||||
|
|
||||||
var isNoisy: Bool { false }
|
var isNoisy: Bool { false }
|
||||||
|
|
||||||
var isDirect: Bool { false }
|
var isDirect: Bool { false }
|
||||||
|
|
||||||
var isEncrypted: Bool { false }
|
var isEncrypted: Bool? { nil }
|
||||||
|
|
||||||
var senderAvatarMediaSource: MediaSourceProxy? { nil }
|
var senderAvatarMediaSource: MediaSourceProxy? { nil }
|
||||||
|
|
||||||
@@ -135,6 +143,18 @@ struct EmptyNotificationItemProxy: NotificationItemProxyProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationItemProxyProtocol {
|
extension NotificationItemProxyProtocol {
|
||||||
|
var baseMutableContent: UNMutableNotificationContent {
|
||||||
|
let notification = UNMutableNotificationContent()
|
||||||
|
notification.receiverID = receiverID
|
||||||
|
notification.roomID = roomID
|
||||||
|
notification.eventID = event.eventID
|
||||||
|
notification.notificationID = id
|
||||||
|
notification.sound = isNoisy ? UNNotificationSound(named: UNNotificationSoundName(rawValue: "message.caf")) : nil
|
||||||
|
// So that the UI groups notification that are received for the same room but also for the same user
|
||||||
|
notification.threadIdentifier = "\(receiverID)\(roomID)"
|
||||||
|
return notification
|
||||||
|
}
|
||||||
|
|
||||||
var requiresMediaProvider: Bool {
|
var requiresMediaProvider: Bool {
|
||||||
if senderAvatarMediaSource != nil || roomAvatarMediaSource != nil {
|
if senderAvatarMediaSource != nil || roomAvatarMediaSource != nil {
|
||||||
return true
|
return true
|
||||||
@@ -157,7 +177,6 @@ extension NotificationItemProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// swiftlint: disable cyclomatic_complexity
|
|
||||||
/// Process the receiver item proxy
|
/// Process the receiver item proxy
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - receiverId: identifier of the user that has received the notification
|
/// - receiverId: identifier of the user that has received the notification
|
||||||
@@ -169,27 +188,14 @@ extension NotificationItemProxyProtocol {
|
|||||||
return processEmpty()
|
return processEmpty()
|
||||||
} else {
|
} else {
|
||||||
switch event.type {
|
switch event.type {
|
||||||
case .none, .state:
|
case .none:
|
||||||
return processEmpty()
|
return processEmpty()
|
||||||
|
case let .state(content):
|
||||||
|
return try await processStateEvent(content: content, mediaProvider: mediaProvider)
|
||||||
case let .messageLike(content):
|
case let .messageLike(content):
|
||||||
switch content {
|
switch content {
|
||||||
case .roomMessage(messageType: let messageType):
|
case .roomMessage(messageType: let messageType):
|
||||||
switch messageType {
|
return try await processRoomMessage(messageType: messageType, mediaProvider: mediaProvider)
|
||||||
case .emote(content: let content):
|
|
||||||
return try await processEmote(content: content, mediaProvider: mediaProvider)
|
|
||||||
case .image(content: let content):
|
|
||||||
return try await processImage(content: content, mediaProvider: mediaProvider)
|
|
||||||
case .audio(content: let content):
|
|
||||||
return try await processAudio(content: content, mediaProvider: mediaProvider)
|
|
||||||
case .video(content: let content):
|
|
||||||
return try await processVideo(content: content, mediaProvider: mediaProvider)
|
|
||||||
case .file(content: let content):
|
|
||||||
return try await processFile(content: content, mediaProvider: mediaProvider)
|
|
||||||
case .notice(content: let content):
|
|
||||||
return try await processNotice(content: content, mediaProvider: mediaProvider)
|
|
||||||
case .text(content: let content):
|
|
||||||
return try await processText(content: content, mediaProvider: mediaProvider)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return processEmpty()
|
return processEmpty()
|
||||||
}
|
}
|
||||||
@@ -197,39 +203,75 @@ extension NotificationItemProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// swiftlint: enable cyclomatic_complexity
|
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func processStateEvent(content: StateEventContent, mediaProvider: MediaProviderProtocol?) async throws -> UNMutableNotificationContent {
|
||||||
|
switch content {
|
||||||
|
case let .roomMemberContent(userId, membershipState):
|
||||||
|
switch membershipState {
|
||||||
|
case .invite:
|
||||||
|
if userId == receiverID {
|
||||||
|
return try await processInvited(mediaProvider: mediaProvider)
|
||||||
|
} else {
|
||||||
|
return processEmpty()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return processEmpty()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return processEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processInvited(mediaProvider: MediaProviderProtocol?) async throws -> UNMutableNotificationContent {
|
||||||
|
var notification = baseMutableContent
|
||||||
|
|
||||||
|
notification.categoryIdentifier = NotificationConstants.Category.invite
|
||||||
|
|
||||||
|
// Sadly as of right now we can't get from the NSE context any information for invited rooms, so we will only display the user name and a simple message
|
||||||
|
let iconType = NotificationIconType.sender(mediaSource: senderAvatarMediaSource)
|
||||||
|
notification = try await notification.addSenderIcon(using: mediaProvider,
|
||||||
|
senderID: event.senderID,
|
||||||
|
senderName: senderDisplayName ?? event.senderID,
|
||||||
|
iconType: iconType)
|
||||||
|
notification.body = L10n.screenInvitesInvitedYou("")
|
||||||
|
return notification
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processRoomMessage(messageType: MessageType, mediaProvider: MediaProviderProtocol?) async throws -> UNMutableNotificationContent {
|
||||||
|
switch messageType {
|
||||||
|
case .emote(content: let content):
|
||||||
|
return try await processEmote(content: content, mediaProvider: mediaProvider)
|
||||||
|
case .image(content: let content):
|
||||||
|
return try await processImage(content: content, mediaProvider: mediaProvider)
|
||||||
|
case .audio(content: let content):
|
||||||
|
return try await processAudio(content: content, mediaProvider: mediaProvider)
|
||||||
|
case .video(content: let content):
|
||||||
|
return try await processVideo(content: content, mediaProvider: mediaProvider)
|
||||||
|
case .file(content: let content):
|
||||||
|
return try await processFile(content: content, mediaProvider: mediaProvider)
|
||||||
|
case .notice(content: let content):
|
||||||
|
return try await processNotice(content: content, mediaProvider: mediaProvider)
|
||||||
|
case .text(content: let content):
|
||||||
|
return try await processText(content: content, mediaProvider: mediaProvider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func processEmpty() -> UNMutableNotificationContent {
|
private func processEmpty() -> UNMutableNotificationContent {
|
||||||
let notification = UNMutableNotificationContent()
|
let notification = baseMutableContent
|
||||||
notification.receiverID = receiverID
|
|
||||||
notification.roomID = roomID
|
|
||||||
notification.eventID = event.eventID
|
|
||||||
notification.notificationID = id
|
|
||||||
notification.title = InfoPlistReader(bundle: .app).bundleDisplayName
|
notification.title = InfoPlistReader(bundle: .app).bundleDisplayName
|
||||||
notification.body = L10n.notification
|
notification.body = L10n.notification
|
||||||
notification.threadIdentifier = roomID
|
notification.categoryIdentifier = NotificationConstants.Category.message
|
||||||
notification.categoryIdentifier = NotificationConstants.Category.reply
|
|
||||||
notification.sound = isNoisy ? UNNotificationSound(named: UNNotificationSoundName(rawValue: "message.caf")) : nil
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processCommon(mediaProvider: MediaProviderProtocol?) async throws -> UNMutableNotificationContent {
|
private func processCommon(mediaProvider: MediaProviderProtocol?) async throws -> UNMutableNotificationContent {
|
||||||
var notification = UNMutableNotificationContent()
|
var notification = baseMutableContent
|
||||||
notification.receiverID = receiverID
|
|
||||||
notification.roomID = roomID
|
|
||||||
notification.eventID = event.eventID
|
|
||||||
notification.notificationID = id
|
|
||||||
notification.title = senderDisplayName ?? roomDisplayName
|
notification.title = senderDisplayName ?? roomDisplayName
|
||||||
if notification.title != roomDisplayName {
|
if notification.title != roomDisplayName {
|
||||||
notification.subtitle = roomDisplayName
|
notification.subtitle = roomDisplayName
|
||||||
}
|
}
|
||||||
// We can store the room identifier into the thread identifier since it's used for notifications
|
notification.categoryIdentifier = NotificationConstants.Category.message
|
||||||
// that belong to the same group
|
|
||||||
notification.threadIdentifier = roomID
|
|
||||||
notification.categoryIdentifier = NotificationConstants.Category.reply
|
|
||||||
notification.sound = isNoisy ? UNNotificationSound(named: UNNotificationSoundName(rawValue: "message.caf")) : nil
|
|
||||||
|
|
||||||
let senderName = senderDisplayName ?? roomDisplayName
|
let senderName = senderDisplayName ?? roomDisplayName
|
||||||
let iconType: NotificationIconType
|
let iconType: NotificationIconType
|
||||||
|
|||||||
@@ -101,14 +101,18 @@ final class NotificationManagerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_whenStart_notificationCategoriesAreSet() throws {
|
func test_whenStart_notificationCategoriesAreSet() throws {
|
||||||
let replyAction = UNTextInputNotificationAction(identifier: NotificationConstants.Action.inlineReply,
|
// let replyAction = UNTextInputNotificationAction(identifier: NotificationConstants.Action.inlineReply,
|
||||||
title: L10n.actionQuickReply,
|
// title: L10n.actionQuickReply,
|
||||||
options: [])
|
// options: [])
|
||||||
let replyCategory = UNNotificationCategory(identifier: NotificationConstants.Category.reply,
|
let messageCategory = UNNotificationCategory(identifier: NotificationConstants.Category.message,
|
||||||
actions: [replyAction],
|
actions: [],
|
||||||
intentIdentifiers: [],
|
intentIdentifiers: [],
|
||||||
options: [])
|
options: [])
|
||||||
XCTAssertEqual(notificationCenter.notificationCategoriesValue, [replyCategory])
|
let inviteCategory = UNNotificationCategory(identifier: NotificationConstants.Category.invite,
|
||||||
|
actions: [],
|
||||||
|
intentIdentifiers: [],
|
||||||
|
options: [])
|
||||||
|
XCTAssertEqual(notificationCenter.notificationCategoriesValue, [messageCategory, inviteCategory])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenStart_delegateIsSet() throws {
|
func test_whenStart_delegateIsSet() throws {
|
||||||
|
|||||||
Reference in New Issue
Block a user