// SPDX-FileCopyrightText: 2021 Nheko Contributors // SPDX-FileCopyrightText: 2022 Nheko Contributors // // SPDX-License-Identifier: GPL-3.0-or-later #include "notifications/MacNotificationDelegate.h" #include "notifications/Manager.h" #include "notifications/NotificationManagerProxy.h" #include "ChatPage.h" #import #import #import #include @interface UNNotificationAttachment (UNNotificationAttachmentAdditions) + (UNNotificationAttachment*)createFromImageData:(NSData*)imgData identifier:(NSString*)imageFileIdentifier options: (NSDictionary*)attachmentOptions; @end @implementation UNNotificationAttachment (UNNotificationAttachmentAdditions) + (UNNotificationAttachment*)createFromImageData:(NSData*)imgData identifier:(NSString*)imageFileIdentifier options: (NSDictionary*)attachmentOptions { NSFileManager* fileManager = [NSFileManager defaultManager]; NSString* tmpSubFolderName = [[NSProcessInfo processInfo] globallyUniqueString]; NSURL* tmpSubFolderURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmpSubFolderName] isDirectory:true]; NSError* error = nil; [fileManager createDirectoryAtURL:tmpSubFolderURL withIntermediateDirectories:true attributes:nil error:&error]; if (error) { NSLog(@"%@", [error localizedDescription]); return nil; } NSURL* fileURL = [tmpSubFolderURL URLByAppendingPathComponent:imageFileIdentifier]; [imgData writeToURL:fileURL atomically:true]; UNNotificationAttachment* imageAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:fileURL options:attachmentOptions error:&error]; if (error) { NSLog(@"%@", [error localizedDescription]); return nil; } return imageAttachment; } @end void NotificationsManager::objCxxPostNotification( const QString room_name, const QString room_id, const QString event_id, const QString subtitle, const QString informativeText, const QString bodyImagePath, const bool enableSound) { // Request permissions for alerts (the generic type of notification), sound playback, // and badges (which allows the Nheko app icon to show the little red bubble with unread count). // NOTE: Possible macOS bug... the 'Play sound for notification checkbox' doesn't appear in // the Notifications and Focus settings unless UNAuthorizationOptionBadges is also // specified UNAuthorizationOptions options = UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge; UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; // TODO: Move this somewhere that isn't dependent on receiving a notification // to actually request notification access. [center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError* _Nullable error) { if (!granted) { NSLog(@"No notification access"); if (error) { NSLog(@"%@", [error localizedDescription]); } } }]; UNTextInputNotificationAction* replyAction = [UNTextInputNotificationAction actionWithIdentifier:@"ReplyAction" title:respondStr.toNSString() options:UNNotificationActionOptionNone textInputButtonTitle:sendStr.toNSString() textInputPlaceholder:placeholder.toNSString()]; UNNotificationCategory* replyCategory = [UNNotificationCategory categoryWithIdentifier:@"ReplyCategory" actions:@[ replyAction ] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; UNNotificationCategory* summaryCategory = [UNNotificationCategory categoryWithIdentifier:@"SummaryCategory" actions:@[] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; NSString* title = room_name.toNSString(); NSString* sub = subtitle.toNSString(); NSString* body = informativeText.toNSString(); NSString* threadIdentifier = room_id.toNSString(); NSString* identifier = event_id.toNSString(); NSString* imgUrl = bodyImagePath.toNSString(); NSSet* categories = [NSSet setWithArray:@[ summaryCategory, replyCategory ]]; [center setNotificationCategories:categories]; [center getNotificationSettingsWithCompletionHandler:^( UNNotificationSettings* _Nonnull settings) { if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) { UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init]; content.title = title; content.subtitle = sub; content.body = body; if (enableSound) { content.sound = [UNNotificationSound defaultSound]; } if (!room_id.isEmpty()) { content.threadIdentifier = threadIdentifier; content.categoryIdentifier = @"ReplyCategory"; } else { content.categoryIdentifier = @"SummaryCategory"; } if ([imgUrl length] != 0) { NSURL* imageURL = [NSURL fileURLWithPath:imgUrl]; NSData* img = [NSData dataWithContentsOfURL:imageURL]; NSArray* attachments = [NSMutableArray array]; UNNotificationAttachment* attachment = [UNNotificationAttachment createFromImageData:img identifier:@"attachment_image.jpeg" options:nil]; if (attachment) { attachments = [NSMutableArray arrayWithObjects:attachment, nil]; content.attachments = attachments; } } UNNotificationRequest* notificationRequest = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:nil]; [center addNotificationRequest:notificationRequest withCompletionHandler:^(NSError* _Nullable error) { if (error != nil) { NSLog(@"Unable to Add Notification Request: %@", [error localizedDescription]); } }]; [content autorelease]; } }]; } void NotificationsManager::attachToMacNotifCenter() { UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; std::unique_ptr proxy = std::make_unique(); connect(proxy.get(), &NotificationManagerProxy::notificationReplied, ChatPage::instance(), &ChatPage::sendNotificationReply); MacNotificationDelegate* notifDelegate = [[MacNotificationDelegate alloc] initWithProxy:std::move(proxy)]; center.delegate = notifDelegate; } // unused void NotificationsManager::actionInvoked(uint, QString) { } void NotificationsManager::activationToken(uint, QString) { } void NotificationsManager::notificationReplied(uint, QString) { } void NotificationsManager::notificationClosed(uint, uint) { } void NotificationsManager::removeNotification(const QString&, const QString&) { }