@@ -6,6 +6,65 @@ class NotificationService: UNNotificationServiceExtension {
66 var bestAttemptContent : UNMutableNotificationContent ?
77 var rocketchat : RocketChat ?
88
9+ // MARK: - Avatar Fetching
10+
11+ func fetchAvatar( from payload: Payload , completion: @escaping ( UNNotificationAttachment ? ) -> Void ) {
12+ guard let username = payload. sender? . username else {
13+ completion ( nil )
14+ return
15+ }
16+
17+ let server = payload. host. removeTrailingSlash ( )
18+ guard let credentials = Storage ( ) . getCredentials ( server: server) else {
19+ completion ( nil )
20+ return
21+ }
22+
23+ // Build authenticated avatar URL (URL encode username for special characters)
24+ guard let encodedUsername = username. addingPercentEncoding ( withAllowedCharacters: . urlPathAllowed) else {
25+ completion ( nil )
26+ return
27+ }
28+ let avatarPath = " /avatar/ \( encodedUsername) ?format=png&size=100&rc_token= \( credentials. userToken) &rc_uid= \( credentials. userId) "
29+ guard let avatarURL = URL ( string: server + avatarPath) else {
30+ completion ( nil )
31+ return
32+ }
33+
34+ // Create request with 3-second timeout
35+ var request = URLRequest ( url: avatarURL, timeoutInterval: 3 )
36+ request. httpMethod = " GET "
37+ request. addValue ( Bundle . userAgent, forHTTPHeaderField: " User-Agent " )
38+
39+ let task = URLSession . shared. dataTask ( with: request) { data, response, error in
40+ guard error == nil ,
41+ let data = data,
42+ let httpResponse = response as? HTTPURLResponse ,
43+ httpResponse. statusCode == 200 else {
44+ completion ( nil )
45+ return
46+ }
47+
48+ // Save to temp file (UNNotificationAttachment requires file URL)
49+ let tempDir = FileManager . default. temporaryDirectory
50+ let fileName = " \( username) _avatar.png "
51+ let fileURL = tempDir. appendingPathComponent ( fileName)
52+
53+ do {
54+ try data. write ( to: fileURL)
55+ let attachment = try UNNotificationAttachment (
56+ identifier: " avatar " ,
57+ url: fileURL,
58+ options: [ UNNotificationAttachmentOptionsTypeHintKey: " public.png " ]
59+ )
60+ completion ( attachment)
61+ } catch {
62+ completion ( nil )
63+ }
64+ }
65+ task. resume ( )
66+ }
67+
968 override func didReceive( _ request: UNNotificationRequest , withContentHandler contentHandler: @escaping ( UNNotificationContent ) -> Void ) {
1069 self . contentHandler = contentHandler
1170 bestAttemptContent = ( request. content. mutableCopy ( ) as? UNMutableNotificationContent )
@@ -126,8 +185,17 @@ class NotificationService: UNNotificationServiceExtension {
126185 }
127186 }
128187
129- if let bestAttemptContent = bestAttemptContent {
130- contentHandler ? ( bestAttemptContent)
188+ // Fetch avatar and deliver notification
189+ fetchAvatar ( from: payload) { [ weak self] attachment in
190+ guard let self = self else { return }
191+
192+ if let attachment = attachment {
193+ self . bestAttemptContent? . attachments = [ attachment]
194+ }
195+
196+ if let bestAttemptContent = self . bestAttemptContent {
197+ self . contentHandler ? ( bestAttemptContent)
198+ }
131199 }
132200 }
133201}
0 commit comments