|
|
@ -1,57 +1,96 @@ |
|
|
|
|
|
|
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors |
|
|
|
|
|
|
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors |
|
|
|
|
|
|
|
// |
|
|
|
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "notifications/NotificationManagerProxy.h" |
|
|
|
|
|
|
|
#include "notifications/MacNotificationDelegate.h" |
|
|
|
#include "notifications/Manager.h" |
|
|
|
#include "notifications/Manager.h" |
|
|
|
|
|
|
|
|
|
|
|
#import <Foundation/Foundation.h> |
|
|
|
#include "ChatPage.h" |
|
|
|
|
|
|
|
|
|
|
|
#import <AppKit/NSImage.h> |
|
|
|
#import <AppKit/NSImage.h> |
|
|
|
|
|
|
|
#import <Foundation/Foundation.h> |
|
|
|
#import <UserNotifications/UserNotifications.h> |
|
|
|
#import <UserNotifications/UserNotifications.h> |
|
|
|
|
|
|
|
|
|
|
|
#include <QtMac> |
|
|
|
|
|
|
|
#include <QImage> |
|
|
|
#include <QImage> |
|
|
|
|
|
|
|
#include <QtMac> |
|
|
|
|
|
|
|
|
|
|
|
@interface UNNotificationAttachment (UNNotificationAttachmentAdditions) |
|
|
|
@interface UNNotificationAttachment (UNNotificationAttachmentAdditions) |
|
|
|
+ (UNNotificationAttachment *) createFromImageData:(NSData*)imgData identifier:(NSString *)imageFileIdentifier options:(NSDictionary*)attachmentOptions; |
|
|
|
+ (UNNotificationAttachment*)createFromImageData:(NSData*)imgData |
|
|
|
|
|
|
|
identifier:(NSString*)imageFileIdentifier |
|
|
|
|
|
|
|
options: |
|
|
|
|
|
|
|
(NSDictionary*)attachmentOptions; |
|
|
|
@end |
|
|
|
@end |
|
|
|
|
|
|
|
|
|
|
|
@implementation UNNotificationAttachment (UNNotificationAttachmentAdditions) |
|
|
|
@implementation UNNotificationAttachment (UNNotificationAttachmentAdditions) |
|
|
|
+ (UNNotificationAttachment *) createFromImageData:(NSData*)imgData identifier:(NSString *)imageFileIdentifier options:(NSDictionary*)attachmentOptions { |
|
|
|
+ (UNNotificationAttachment*)createFromImageData:(NSData*)imgData |
|
|
|
|
|
|
|
identifier:(NSString*)imageFileIdentifier |
|
|
|
|
|
|
|
options: |
|
|
|
|
|
|
|
(NSDictionary*)attachmentOptions |
|
|
|
|
|
|
|
{ |
|
|
|
NSFileManager* fileManager = [NSFileManager defaultManager]; |
|
|
|
NSFileManager* fileManager = [NSFileManager defaultManager]; |
|
|
|
NSString *tmpSubFolderName = [[NSProcessInfo processInfo] globallyUniqueString]; |
|
|
|
NSString* tmpSubFolderName = |
|
|
|
NSURL *tmpSubFolderURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmpSubFolderName] isDirectory:true]; |
|
|
|
[[NSProcessInfo processInfo] globallyUniqueString]; |
|
|
|
|
|
|
|
NSURL* tmpSubFolderURL = [NSURL |
|
|
|
|
|
|
|
fileURLWithPath:[NSTemporaryDirectory() |
|
|
|
|
|
|
|
stringByAppendingPathComponent:tmpSubFolderName] |
|
|
|
|
|
|
|
isDirectory:true]; |
|
|
|
NSError* error = nil; |
|
|
|
NSError* error = nil; |
|
|
|
[fileManager createDirectoryAtURL:tmpSubFolderURL withIntermediateDirectories:true attributes:nil error:&error]; |
|
|
|
[fileManager createDirectoryAtURL:tmpSubFolderURL |
|
|
|
|
|
|
|
withIntermediateDirectories:true |
|
|
|
|
|
|
|
attributes:nil |
|
|
|
|
|
|
|
error:&error]; |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
NSLog(@"%@", [error localizedDescription]); |
|
|
|
NSLog(@"%@", [error localizedDescription]); |
|
|
|
return nil; |
|
|
|
return nil; |
|
|
|
} |
|
|
|
} |
|
|
|
NSURL *fileURL = [tmpSubFolderURL URLByAppendingPathComponent:imageFileIdentifier]; |
|
|
|
NSURL* fileURL = |
|
|
|
|
|
|
|
[tmpSubFolderURL URLByAppendingPathComponent:imageFileIdentifier]; |
|
|
|
[imgData writeToURL:fileURL atomically:true]; |
|
|
|
[imgData writeToURL:fileURL atomically:true]; |
|
|
|
UNNotificationAttachment *imageAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:fileURL options:attachmentOptions error:&error]; |
|
|
|
UNNotificationAttachment* imageAttachment = |
|
|
|
|
|
|
|
[UNNotificationAttachment attachmentWithIdentifier:@"" |
|
|
|
|
|
|
|
URL:fileURL |
|
|
|
|
|
|
|
options:attachmentOptions |
|
|
|
|
|
|
|
error:&error]; |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
NSLog(@"%@", [error localizedDescription]); |
|
|
|
NSLog(@"%@", [error localizedDescription]); |
|
|
|
return nil; |
|
|
|
return nil; |
|
|
|
} |
|
|
|
} |
|
|
|
return imageAttachment; |
|
|
|
return imageAttachment; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
@end |
|
|
|
@end |
|
|
|
|
|
|
|
|
|
|
|
NotificationsManager::NotificationsManager(QObject *parent): QObject(parent) |
|
|
|
NotificationsManager::NotificationsManager(QObject* parent) |
|
|
|
|
|
|
|
: QObject(parent) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
void NotificationsManager::objCxxPostNotification( |
|
|
|
NotificationsManager::objCxxPostNotification(const QString &room_name, |
|
|
|
const QString& room_name, |
|
|
|
const QString& room_id, |
|
|
|
const QString& room_id, |
|
|
|
const QString& event_id, |
|
|
|
const QString& event_id, |
|
|
|
const QString& subtitle, |
|
|
|
const QString& subtitle, |
|
|
|
const QString& informativeText, |
|
|
|
const QString& informativeText, |
|
|
|
const QString &bodyImagePath) |
|
|
|
const QString& bodyImagePath, |
|
|
|
|
|
|
|
const QString& respondStr, |
|
|
|
|
|
|
|
const QString& sendStr, |
|
|
|
|
|
|
|
const QString& placeholder) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
// 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; |
|
|
|
UNAuthorizationOptions options = UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge; |
|
|
|
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; |
|
|
|
UNUserNotificationCenter* center = |
|
|
|
|
|
|
|
[UNUserNotificationCenter currentNotificationCenter]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Move this somewhere that isn't dependent on receiving a notification |
|
|
|
|
|
|
|
// to actually request notification access. |
|
|
|
[center requestAuthorizationWithOptions:options |
|
|
|
[center requestAuthorizationWithOptions:options |
|
|
|
completionHandler:^(BOOL granted, NSError * _Nullable error) { |
|
|
|
completionHandler:^(BOOL granted, |
|
|
|
|
|
|
|
NSError* _Nullable error) { |
|
|
|
if (!granted) { |
|
|
|
if (!granted) { |
|
|
|
NSLog(@"No notification access"); |
|
|
|
NSLog(@"No notification access"); |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
@ -60,53 +99,89 @@ NotificationsManager::objCxxPostNotification(const QString &room_name, |
|
|
|
} |
|
|
|
} |
|
|
|
}]; |
|
|
|
}]; |
|
|
|
|
|
|
|
|
|
|
|
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; |
|
|
|
UNTextInputNotificationAction* replyAction = [UNTextInputNotificationAction actionWithIdentifier:@"ReplyAction" |
|
|
|
|
|
|
|
title:respondStr.toNSString() |
|
|
|
content.title = room_name.toNSString(); |
|
|
|
options:UNNotificationActionOptionNone |
|
|
|
content.subtitle = subtitle.toNSString(); |
|
|
|
textInputButtonTitle:sendStr.toNSString() |
|
|
|
content.body = informativeText.toNSString(); |
|
|
|
textInputPlaceholder:placeholder.toNSString()]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UNNotificationCategory* category = [UNNotificationCategory categoryWithIdentifier:@"ReplyCategory" |
|
|
|
|
|
|
|
actions:@[ replyAction ] |
|
|
|
|
|
|
|
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 setWithObject:category]; |
|
|
|
|
|
|
|
[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; |
|
|
|
content.sound = [UNNotificationSound defaultSound]; |
|
|
|
content.sound = [UNNotificationSound defaultSound]; |
|
|
|
content.threadIdentifier = room_id.toNSString(); |
|
|
|
content.threadIdentifier = threadIdentifier; |
|
|
|
|
|
|
|
content.categoryIdentifier = @"ReplyCategory"; |
|
|
|
|
|
|
|
|
|
|
|
if (!bodyImagePath.isEmpty()) { |
|
|
|
if ([imgUrl length] != 0) { |
|
|
|
NSURL *imageURL = [NSURL fileURLWithPath:bodyImagePath.toNSString()]; |
|
|
|
NSURL* imageURL = [NSURL fileURLWithPath:imgUrl]; |
|
|
|
NSData* img = [NSData dataWithContentsOfURL:imageURL]; |
|
|
|
NSData* img = [NSData dataWithContentsOfURL:imageURL]; |
|
|
|
NSArray* attachments = [NSMutableArray array]; |
|
|
|
NSArray* attachments = [NSMutableArray array]; |
|
|
|
UNNotificationAttachment *attachment = [UNNotificationAttachment createFromImageData:img identifier:@"attachment_image.jpeg" options:nil]; |
|
|
|
UNNotificationAttachment* attachment = [UNNotificationAttachment |
|
|
|
|
|
|
|
createFromImageData:img |
|
|
|
|
|
|
|
identifier:@"attachment_image.jpeg" |
|
|
|
|
|
|
|
options:nil]; |
|
|
|
if (attachment) { |
|
|
|
if (attachment) { |
|
|
|
attachments = [NSMutableArray arrayWithObjects:attachment, nil]; |
|
|
|
attachments = [NSMutableArray arrayWithObjects:attachment, nil]; |
|
|
|
content.attachments = attachments; |
|
|
|
content.attachments = attachments; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
UNNotificationRequest *notificationRequest = [UNNotificationRequest requestWithIdentifier:event_id.toNSString() content:content trigger:nil]; |
|
|
|
UNNotificationRequest* notificationRequest = |
|
|
|
|
|
|
|
[UNNotificationRequest requestWithIdentifier:identifier |
|
|
|
|
|
|
|
content:content |
|
|
|
|
|
|
|
trigger:nil]; |
|
|
|
|
|
|
|
|
|
|
|
[center addNotificationRequest:notificationRequest withCompletionHandler:^(NSError * _Nullable error) { |
|
|
|
[center addNotificationRequest:notificationRequest |
|
|
|
|
|
|
|
withCompletionHandler:^(NSError* _Nullable error) { |
|
|
|
if (error != nil) { |
|
|
|
if (error != nil) { |
|
|
|
NSLog(@"Unable to Add Notification Request"); |
|
|
|
NSLog(@"Unable to Add Notification Request: %@", [error localizedDescription]); |
|
|
|
} |
|
|
|
} |
|
|
|
}]; |
|
|
|
}]; |
|
|
|
|
|
|
|
|
|
|
|
[content autorelease]; |
|
|
|
[content autorelease]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}]; |
|
|
|
//unused |
|
|
|
|
|
|
|
void |
|
|
|
|
|
|
|
NotificationsManager::actionInvoked(uint, QString) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
void NotificationsManager::attachToMacNotifCenter() |
|
|
|
NotificationsManager::notificationReplied(uint, QString) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
} |
|
|
|
UNUserNotificationCenter* center = |
|
|
|
|
|
|
|
[UNUserNotificationCenter currentNotificationCenter]; |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
std::unique_ptr<NotificationManagerProxy> proxy = std::make_unique<NotificationManagerProxy>(); |
|
|
|
NotificationsManager::notificationClosed(uint, uint) |
|
|
|
|
|
|
|
{ |
|
|
|
connect(proxy.get(), &NotificationManagerProxy::notificationReplied, ChatPage::instance(), &ChatPage::sendNotificationReply); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MacNotificationDelegate* notifDelegate = [[MacNotificationDelegate alloc] initWithProxy:std::move(proxy)]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
center.delegate = notifDelegate; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
// unused |
|
|
|
NotificationsManager::removeNotification(const QString &, const QString &) |
|
|
|
void NotificationsManager::actionInvoked(uint, QString) { } |
|
|
|
{} |
|
|
|
|
|
|
|
|
|
|
|
void NotificationsManager::notificationReplied(uint, QString) { } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void NotificationsManager::notificationClosed(uint, uint) { } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void NotificationsManager::removeNotification(const QString&, const QString&) { } |
|
|
|