add additional logic

This commit is contained in:
Hailey
2024-08-25 23:47:44 -07:00
parent 3d7025718c
commit af996ea268
+65 -35
View File
@@ -1,5 +1,6 @@
import UserNotifications import UserNotifications
import UIKit import UIKit
import Intents
let APP_GROUP = "group.app.bsky" let APP_GROUP = "group.app.bsky"
typealias ContentHandler = (UNNotificationContent) -> Void typealias ContentHandler = (UNNotificationContent) -> Void
@@ -32,22 +33,23 @@ class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler self.contentHandler = contentHandler
guard let bestAttempt = NSEUtil.createCopy(request.content) else { guard let bestAttempt = NSEUtil.createCopy(request.content) else {
contentHandler(request.content) contentHandler(request.content)
return return
} }
self.bestAttempt = bestAttempt self.bestAttempt = bestAttempt
if isReasonDm() { if isReasonDm() {
} }
if isReasonDm() { if isReasonDm() {
mutateWithChatMessage(bestAttempt) mutateWithChatMessage(bestAttempt)
// contentHandler will be called within mutateWithAvatar's completion block mutateWithAvatar(bestAttempt) {
mutateWithAvatar(bestAttempt) contentHandler(bestAttempt)
}
} else { } else {
mutateWithBadge(bestAttempt) mutateWithBadge(bestAttempt)
contentHandler(bestAttempt) contentHandler(bestAttempt)
@@ -59,12 +61,12 @@ class NotificationService: UNNotificationServiceExtension {
let bestAttempt = self.bestAttempt else { let bestAttempt = self.bestAttempt else {
return return
} }
if isReasonDm(), if isReasonDm(),
let subject = self.getSubject() { let subject = self.getSubject() {
NSEUtil.shared.cancelImageDownload(userId: subject) NSEUtil.shared.cancelImageDownload(userId: subject)
} }
contentHandler(bestAttempt) contentHandler(bestAttempt)
} }
@@ -94,40 +96,68 @@ class NotificationService: UNNotificationServiceExtension {
func mutateWithDmSound(_ content: UNMutableNotificationContent) { func mutateWithDmSound(_ content: UNMutableNotificationContent) {
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "dm.aiff")) content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "dm.aiff"))
} }
func mutateWithAvatar(_ content: UNMutableNotificationContent) { func mutateWithAvatar(_ content: UNMutableNotificationContent, completion: @escaping () -> Void) {
// Here, the server will only give us an avatar uri if there is a // Here, the server will only give us an avatar uri if there is a
// follow relationship. All we need to check is the preference. // follow relationship. All we need to check is the preference.
guard let avatarUrlString = content.userInfo["avatar"] as? String, guard NSEUtil.shared.prefs?.bool(forKey: "showAvatarDms") == true,
NSEUtil.shared.prefs?.bool(forKey: "showAvatarDms") == true, #available (iOSApplicationExtension 15.0, *),
let avatarUrlString = content.userInfo["avatar"] as? String,
let displayName = content.userInfo["displayName"] as? String,
let avatarUrl = URL(string: avatarUrlString), let avatarUrl = URL(string: avatarUrlString),
let subject = self.getSubject() else { let subject = self.getSubject(),
let contentHandler = self.contentHandler else {
completion()
return return
} }
NSEUtil.shared.downloadImageIfNecessary(avatarUrl, userId: subject) { imageUrl in NSEUtil.shared.downloadImageIfNecessary(avatarUrl, userId: subject) { imageUrl in
guard let contentHandler = self.contentHandler else { guard let imageUrl = imageUrl else {
completion()
return return
} }
self.contentHandler?(content)
let handle = INPersonHandle(value: subject,
type: .unknown)
let image = INImage(url: imageUrl)
let sender = INPerson(personHandle: handle,
nameComponents: nil,
displayName: displayName,
image: image,
contactIdentifier: subject,
customIdentifier: nil)
let intent = INSendMessageIntent.init(recipients: nil,
outgoingMessageType: .outgoingMessageText,
content: content.body,
speakableGroupName: nil,
conversationIdentifier: subject,
serviceName: nil,
sender: sender,
attachments: nil)
_ = try? content.updating(from: intent)
completion()
} }
} }
// MARK: Util // MARK: Util
func isReasonDm() -> Bool { func isReasonDm() -> Bool {
guard let reason = self.bestAttempt?.userInfo["reason"] as? String else { guard let reason = self.bestAttempt?.userInfo["reason"] as? String else {
return false return false
} }
return reason == "chat-message" return reason == "chat-message"
} }
func getSubject() -> String? { func getSubject() -> String? {
guard let subject = self.bestAttempt?.userInfo["subject"] as? String else { guard let subject = self.bestAttempt?.userInfo["subject"] as? String else {
return nil return nil
} }
return subject return subject
} }
func addPersonAndAvatar(_ content: UNMutableNotificationContent) {
}
} }
// NSEUtil's purpose is to create a shared instance of `UserDefaults` across // NSEUtil's purpose is to create a shared instance of `UserDefaults` across
@@ -136,41 +166,41 @@ class NotificationService: UNNotificationServiceExtension {
private class NSEUtil { private class NSEUtil {
static let shared = NSEUtil() static let shared = NSEUtil()
var prefs = UserDefaults(suiteName: APP_GROUP) var prefs = UserDefaults(suiteName: APP_GROUP)
var prefsQueue = DispatchQueue(label: "NSEPrefsQueue") var prefsQueue = DispatchQueue(label: "NSEPrefsQueue")
// Store each image download task that // Store each image download task that
private let imageDownloadTasks = NSMapTable<NSString, ImageDownloadTask>( private let imageDownloadTasks = NSMapTable<NSString, ImageDownloadTask>(
keyOptions: NSPointerFunctions.Options.weakMemory, keyOptions: NSPointerFunctions.Options.weakMemory,
valueOptions: NSPointerFunctions.Options.weakMemory valueOptions: NSPointerFunctions.Options.weakMemory
) )
// Creates a mutable copy of the provided content // Creates a mutable copy of the provided content
static func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? { static func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? {
return content.mutableCopy() as? UNMutableNotificationContent return content.mutableCopy() as? UNMutableNotificationContent
} }
private func createFileURL(userId: String) -> URL? { private func createFileURL(userId: String) -> URL? {
let dir = FileManager.default.temporaryDirectory let dir = FileManager.default.temporaryDirectory
return URL(string: "\(dir.absoluteString)\(userId).png") return URL(string: "\(dir.absoluteString)\(userId).png")
} }
private func imageExists(_ url: URL) -> Bool { private func imageExists(_ url: URL) -> Bool {
return FileManager.default.fileExists(atPath: url.absoluteString) return FileManager.default.fileExists(atPath: url.absoluteString)
} }
func downloadImageIfNecessary(_ imageUrl: URL, userId: String, completion: @escaping (URL?) -> Void) { func downloadImageIfNecessary(_ imageUrl: URL, userId: String, completion: @escaping (URL?) -> Void) {
guard let outUrl = self.createFileURL(userId: userId) else { guard let outUrl = self.createFileURL(userId: userId) else {
completion(nil) completion(nil)
return return
} }
if (self.imageExists(outUrl)) { if self.imageExists(outUrl) {
completion(outUrl) completion(outUrl)
return return
} }
let task = ImageDownloadTask(imageUrl: imageUrl) { image in let task = ImageDownloadTask(imageUrl: imageUrl) { image in
guard let image = image, guard let image = image,
let pngData = image.pngData() else { let pngData = image.pngData() else {
@@ -186,7 +216,7 @@ private class NSEUtil {
} }
Self.shared.imageDownloadTasks.setObject(task, forKey: NSString(string: userId)) Self.shared.imageDownloadTasks.setObject(task, forKey: NSString(string: userId))
} }
func cancelImageDownload(userId: String) { func cancelImageDownload(userId: String) {
guard let imageDownloadTask = self.imageDownloadTasks.object(forKey: NSString(string: userId)) else { guard let imageDownloadTask = self.imageDownloadTasks.object(forKey: NSString(string: userId)) else {
return return
@@ -201,12 +231,12 @@ private class NSEUtil {
class ImageDownloadTask { class ImageDownloadTask {
private var completionBlocks = [ImageDownloadTaskCompletion]() private var completionBlocks = [ImageDownloadTaskCompletion]()
private var task: URLSessionDataTask? private var task: URLSessionDataTask?
init(imageUrl: URL, completion: @escaping ImageDownloadTaskCompletion) { init(imageUrl: URL, completion: @escaping ImageDownloadTaskCompletion) {
self.completionBlocks.append(completion) self.completionBlocks.append(completion)
self.createTask(imageUrl) self.createTask(imageUrl)
} }
private func createTask(_ imageUrl: URL) { private func createTask(_ imageUrl: URL) {
let task = URLSession.shared.dataTask(with: imageUrl) { data, _, _ in let task = URLSession.shared.dataTask(with: imageUrl) { data, _, _ in
guard let data = data, guard let data = data,
@@ -218,17 +248,17 @@ class ImageDownloadTask {
} }
task.resume() task.resume()
} }
private func callEachCompletionBlock(_ image: UIImage?) { private func callEachCompletionBlock(_ image: UIImage?) {
completionBlocks.forEach { completion in completionBlocks.forEach { completion in
completion(image) completion(image)
} }
} }
func addCompletionBlock(_ completion: @escaping ImageDownloadTaskCompletion) { func addCompletionBlock(_ completion: @escaping ImageDownloadTaskCompletion) {
self.completionBlocks.append(completion) self.completionBlocks.append(completion)
} }
func cancel() { func cancel() {
self.task?.cancel() self.task?.cancel()
self.callEachCompletionBlock(nil) self.callEachCompletionBlock(nil)