✨ New NSE and hold notification to reply
This commit is contained in:
parent
311420e1f7
commit
7f36c86c55
1
.vscode/settings.json
vendored
Normal file
1
.vscode/settings.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{}
|
@ -45,8 +45,8 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
|
||||
private func processNotification(request: UNNotificationRequest, content: UNMutableNotificationContent) throws {
|
||||
switch content.categoryIdentifier {
|
||||
case "messaging.message", "messaging.callStart":
|
||||
switch content.userInfo["type"] as? String {
|
||||
case "messages.new":
|
||||
try handleMessagingNotification(request: request, content: content)
|
||||
default:
|
||||
try handleDefaultNotification(content: content)
|
||||
@ -54,11 +54,11 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
|
||||
private func handleMessagingNotification(request: UNNotificationRequest, content: UNMutableNotificationContent) throws {
|
||||
guard let metadata = content.userInfo["metadata"] as? [AnyHashable: Any] else {
|
||||
throw ParseNotificationPayloadError.missingMetadata("The notification has no metadata.")
|
||||
guard let meta = content.userInfo["meta"] as? [AnyHashable: Any] else {
|
||||
throw ParseNotificationPayloadError.missingMetadata("The notification has no meta.")
|
||||
}
|
||||
|
||||
guard let pfpIdentifier = metadata["pfp"] as? String else {
|
||||
guard let pfpIdentifier = meta["pfp"] as? String else {
|
||||
throw ParseNotificationPayloadError.missingAvatarUrl("The notification has no pfp.")
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
|
||||
content.categoryIdentifier = replyableMessageCategory.identifier
|
||||
|
||||
let metadataCopy = metadata as? [String: String] ?? [:]
|
||||
let metaCopy = meta as? [String: String] ?? [:]
|
||||
let pfpUrl = getAttachmentUrl(for: pfpIdentifier)
|
||||
|
||||
let targetSize = 512
|
||||
@ -93,17 +93,17 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
print("Unable to get pfp url: \(error)")
|
||||
}
|
||||
|
||||
let handle = INPersonHandle(value: "\(metadataCopy["user_id"] ?? "")", type: .unknown)
|
||||
let handle = INPersonHandle(value: "\(metaCopy["user_id"] ?? "")", type: .unknown)
|
||||
let sender = INPerson(
|
||||
personHandle: handle,
|
||||
nameComponents: PersonNameComponents(nickname: "\(metadataCopy["sender_name"] ?? "")"),
|
||||
nameComponents: PersonNameComponents(nickname: "\(metaCopy["sender_name"] ?? "")"),
|
||||
displayName: content.title,
|
||||
image: image == nil ? nil : INImage(imageData: image!),
|
||||
contactIdentifier: nil,
|
||||
customIdentifier: nil
|
||||
)
|
||||
|
||||
let intent = self.createMessageIntent(with: sender, metadata: metadataCopy, body: content.body)
|
||||
let intent = self.createMessageIntent(with: sender, meta: metaCopy, body: content.body)
|
||||
self.donateInteraction(for: intent)
|
||||
let updatedContent = try? request.content.updating(from: intent)
|
||||
self.contentHandler?(updatedContent ?? content)
|
||||
@ -111,15 +111,15 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
|
||||
private func handleDefaultNotification(content: UNMutableNotificationContent) throws {
|
||||
guard let metadata = content.userInfo["metadata"] as? [AnyHashable: Any] else {
|
||||
throw ParseNotificationPayloadError.missingMetadata("The notification has no metadata.")
|
||||
guard let meta = content.userInfo["meta"] as? [AnyHashable: Any] else {
|
||||
throw ParseNotificationPayloadError.missingMetadata("The notification has no meta.")
|
||||
}
|
||||
|
||||
if let imageIdentifier = metadata["image"] as? String {
|
||||
if let imageIdentifier = meta["image"] as? String {
|
||||
attachMedia(to: content, withIdentifier: [imageIdentifier], fileType: UTType.webP, doScaleDown: true)
|
||||
} else if let pfpIdentifier = metadata["pfp"] as? String {
|
||||
} else if let pfpIdentifier = meta["pfp"] as? String {
|
||||
attachMedia(to: content, withIdentifier: [pfpIdentifier], fileType: UTType.webP, doScaleDown: true)
|
||||
} else if let imagesIdentifier = metadata["images"] as? Array<String> {
|
||||
} else if let imagesIdentifier = meta["images"] as? Array<String> {
|
||||
attachMedia(to: content, withIdentifier: imagesIdentifier, fileType: UTType.webP, doScaleDown: true)
|
||||
} else {
|
||||
contentHandler?(content)
|
||||
@ -136,7 +136,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
return
|
||||
}
|
||||
|
||||
let targetSize = 800
|
||||
let targetSize = 512
|
||||
let scaleProcessor = ResizingImageProcessor(referenceSize: CGSize(width: targetSize, height: targetSize), mode: .aspectFit)
|
||||
|
||||
for attachmentUrl in attachmentUrls {
|
||||
@ -202,13 +202,13 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
self.contentHandler?(content)
|
||||
}
|
||||
|
||||
private func createMessageIntent(with sender: INPerson, metadata: [AnyHashable: Any], body: String) -> INSendMessageIntent {
|
||||
private func createMessageIntent(with sender: INPerson, meta: [AnyHashable: Any], body: String) -> INSendMessageIntent {
|
||||
INSendMessageIntent(
|
||||
recipients: nil,
|
||||
outgoingMessageType: .outgoingMessageText,
|
||||
content: body,
|
||||
speakableGroupName: metadata["room_name"] != nil ? INSpeakableString(spokenPhrase: metadata["room_name"] as! String) : nil,
|
||||
conversationIdentifier: "\(metadata["room_id"] ?? "")",
|
||||
speakableGroupName: meta["room_name"] != nil ? INSpeakableString(spokenPhrase: meta["room_name"] as! String) : nil,
|
||||
conversationIdentifier: "\(meta["room_id"] ?? "")",
|
||||
serviceName: nil,
|
||||
sender: sender,
|
||||
attachments: nil
|
||||
|
@ -31,6 +31,8 @@ target 'Runner' do
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
|
||||
pod 'Alamofire'
|
||||
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
|
||||
target 'RunnerTests' do
|
||||
|
@ -344,6 +344,6 @@ SPEC CHECKSUMS:
|
||||
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
||||
WebRTC-SDK: dff00a3892bc570b6014e046297782084071657e
|
||||
|
||||
PODFILE CHECKSUM: c8120fa04387477e9e62f36b6c37495c69fca3bb
|
||||
PODFILE CHECKSUM: f5ad824c068cb5da52a3e19417c4e0de970c1231
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
@ -11,6 +11,7 @@
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
73268D1C2DEAFD670076E970 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73268D152DEAFD670076E970 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
73D4264B2DEB815D006C0AAE /* NotifyDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D4264A2DEB815D006C0AAE /* NotifyDelegate.swift */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
755557017FD1B99AFC4F9127 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29812C17FFBE7DBBC7203981 /* Pods_RunnerTests.framework */; };
|
||||
8529D00678947B00A0162116 /* Pods_NotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA0CA8A3E15DEE023BB27438 /* Pods_NotificationService.framework */; };
|
||||
@ -78,6 +79,7 @@
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
73268D152DEAFD670076E970 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
737E920B2DB6A9FF00BE9CDB /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
73D4264A2DEB815D006C0AAE /* NotifyDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotifyDelegate.swift; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
@ -241,6 +243,7 @@
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
73D4264A2DEB815D006C0AAE /* NotifyDelegate.swift */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
@ -545,6 +548,7 @@
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
73D4264B2DEB815D006C0AAE /* NotifyDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -3,11 +3,14 @@ import UIKit
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
let notifyDelegate = NotifyDelegate()
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
UNUserNotificationCenter.current().delegate = notifyDelegate
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
|
54
ios/Runner/NotifyDelegate.swift
Normal file
54
ios/Runner/NotifyDelegate.swift
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// NotifyDelegate.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by LittleSheep on 2025/6/1.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Alamofire
|
||||
|
||||
class NotifyDelegate: UIResponder, UNUserNotificationCenterDelegate {
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
if let textResponse = response as? UNTextInputNotificationResponse {
|
||||
let content = response.notification.request.content
|
||||
guard let metadata = content.userInfo["meta"] as? [AnyHashable: Any] else {
|
||||
return
|
||||
}
|
||||
|
||||
var token: String = ""
|
||||
if let tokenJson = UserDefaults.standard.string(forKey: "dyn_user_tk"),
|
||||
let tokenData = tokenJson.data(using: String.Encoding.utf8),
|
||||
let tokenDict = try? JSONSerialization.jsonObject(with: tokenData) as? [String: Any],
|
||||
let tokenValue = tokenDict["token"] as? String {
|
||||
token = tokenValue
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let serverUrl = "https://nt.solian.app"
|
||||
let url = "\(serverUrl)/chat/\(metadata["room_id"])/messages"
|
||||
|
||||
let parameters: [String: Any] = [
|
||||
"content": textResponse.userText,
|
||||
"replied_message_id": metadata["message_id"]
|
||||
]
|
||||
|
||||
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: HTTPHeaders(
|
||||
[HTTPHeader(name: "Authorization", value: "AtField \(token)")]
|
||||
))
|
||||
.validate()
|
||||
.responseString { response in
|
||||
switch response.result {
|
||||
case .success(_):
|
||||
break
|
||||
case .failure(let error):
|
||||
print("Failed to send chat reply message: \(error)")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler()
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
|
||||
func getAttachmentUrl(for identifier: String) -> String {
|
||||
let serverBaseUrl = "https://api.sn.solsynth.dev"
|
||||
let serverBaseUrl = "https://nt.solian.app"
|
||||
|
||||
return identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/cgi/uc/attachments/\(identifier)"
|
||||
return identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/files/\(identifier)"
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ part 'call_button.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<SnRealtimeCall?> ongoingCall(Ref ref, String roomId) async {
|
||||
if (roomId.isEmpty) return null;
|
||||
try {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final resp = await apiClient.get('/chat/realtime/$roomId');
|
||||
|
@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 3.0.0+97
|
||||
version: 3.0.0+98
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.2
|
||||
|
Loading…
x
Reference in New Issue
Block a user