Account for missing i18n in Mastodon notifications

This commit is contained in:
Justin Mazzocchi 2021-02-25 18:46:32 -08:00
parent f0eeb558a5
commit 2f0e35f343
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
4 changed files with 97 additions and 25 deletions

View file

@ -163,6 +163,10 @@
"metatext" = "Metatext"; "metatext" = "Metatext";
"notification.signed-in-as-%@" = "Logged in as %@"; "notification.signed-in-as-%@" = "Logged in as %@";
"notification.new-items" = "New notifications"; "notification.new-items" = "New notifications";
"notification.poll" = "A poll you have voted in has ended";
"notification.poll.own" = "Your poll has ended";
"notification.poll.unknown" = "A poll has ended";
"notification.status-%@" = "%@ just posted";
"notifications.all" = "All"; "notifications.all" = "All";
"notifications.mentions" = "Mentions"; "notifications.mentions" = "Mentions";
"ok" = "OK"; "ok" = "OK";

View file

@ -129,6 +129,7 @@
D09D972225C65682007E6394 /* SeparatorConfiguredCollectionViewListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09D972125C65682007E6394 /* SeparatorConfiguredCollectionViewListCell.swift */; }; D09D972225C65682007E6394 /* SeparatorConfiguredCollectionViewListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09D972125C65682007E6394 /* SeparatorConfiguredCollectionViewListCell.swift */; };
D0A1F4F7252E7D4B004435BF /* TableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */; }; D0A1F4F7252E7D4B004435BF /* TableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */; };
D0A7AC7325748BFF00E4E8AB /* ReportStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A7AC7225748BFF00E4E8AB /* ReportStatusView.swift */; }; D0A7AC7325748BFF00E4E8AB /* ReportStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A7AC7225748BFF00E4E8AB /* ReportStatusView.swift */; };
D0B325EB25E88ADC00C24BEA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0C7D45724F76169001EBDBB /* Localizable.strings */; };
D0B32F50250B373600311912 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B32F4F250B373600311912 /* RegistrationView.swift */; }; D0B32F50250B373600311912 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B32F4F250B373600311912 /* RegistrationView.swift */; };
D0B5FE9B251583DB00478838 /* ProfileCollection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */; }; D0B5FE9B251583DB00478838 /* ProfileCollection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */; };
D0B8510C25259E56004E0744 /* LoadMoreTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B8510B25259E56004E0744 /* LoadMoreTableViewCell.swift */; }; D0B8510C25259E56004E0744 /* LoadMoreTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B8510B25259E56004E0744 /* LoadMoreTableViewCell.swift */; };
@ -998,6 +999,7 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D0B325EB25E88ADC00C24BEA /* Localizable.strings in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View file

@ -1,5 +1,6 @@
// Copyright © 2020 Metabolist. All rights reserved. // Copyright © 2020 Metabolist. All rights reserved.
import Combine
import Mastodon import Mastodon
import SDWebImage import SDWebImage
import ServiceLayer import ServiceLayer
@ -14,6 +15,7 @@ final class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)? var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent? var bestAttemptContent: UNMutableNotificationContent?
var cancellables = Set<AnyCancellable>()
override func didReceive( override func didReceive(
_ request: UNNotificationRequest, _ request: UNNotificationRequest,
@ -47,18 +49,27 @@ final class NotificationService: UNNotificationServiceExtension {
bestAttemptContent.sound = .default bestAttemptContent.sound = .default
} }
if appPreferences.notificationAccountName, var identity: Identity?
let accountName = try? AllIdentitiesService(environment: Self.environment).identity(id: identityId)?.handle {
bestAttemptContent.subtitle = accountName if appPreferences.notificationAccountName {
identity = try? AllIdentitiesService(environment: Self.environment).identity(id: identityId)
if let handle = identity?.handle {
bestAttemptContent.subtitle = handle
}
} }
if appPreferences.notificationPictures { Self.attachment(imageURL: pushNotification.icon)
Self.addImage(url: pushNotification.icon, .map { [$0] }
bestAttemptContent: bestAttemptContent, .replaceError(with: [])
contentHandler: contentHandler) .handleEvents(receiveOutput: { bestAttemptContent.attachments = $0 })
} else { .zip(parsingService.title(pushNotification: pushNotification,
contentHandler(bestAttemptContent) identityId: identityId,
} accountId: identity?.account?.id)
.replaceError(with: pushNotification.title)
.handleEvents(receiveOutput: { bestAttemptContent.title = $0 }))
.sink { _ in contentHandler(bestAttemptContent) }
.store(in: &cancellables)
} }
override func serviceExtensionTimeWillExpire() { override func serviceExtensionTimeWillExpire() {
@ -73,26 +84,32 @@ private extension NotificationService {
userNotificationCenter: .current(), userNotificationCenter: .current(),
reduceMotion: { false }) reduceMotion: { false })
static func addImage(url: URL, enum ImageError: Error {
bestAttemptContent: UNMutableNotificationContent, case dataMissing
contentHandler: @escaping (UNNotificationContent) -> Void) { }
let fileName = url.lastPathComponent
static func attachment(imageURL: URL) -> AnyPublisher<UNNotificationAttachment, Error> {
let fileName = imageURL.lastPathComponent
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let fileURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent(fileName) .appendingPathComponent(fileName)
SDWebImageManager.shared.loadImage(with: url, options: [], progress: nil) { _, data, _, _, _, _ in return Future<UNNotificationAttachment, Error> { promise in
if let data = data { SDWebImageManager.shared.loadImage(with: imageURL, options: [], progress: nil) { _, data, error, _, _, _ in
do { if let error = error {
promise(.failure(error))
} else if let data = data {
let result = Result<UNNotificationAttachment, Error> {
try data.write(to: fileURL) try data.write(to: fileURL)
bestAttemptContent.attachments =
[try UNNotificationAttachment(identifier: fileName, url: fileURL)] return try UNNotificationAttachment(identifier: fileName, url: fileURL)
contentHandler(bestAttemptContent)
} catch {
contentHandler(bestAttemptContent)
} }
promise(result)
} else { } else {
contentHandler(bestAttemptContent) promise(.failure(ImageError.dataMissing))
} }
} }
} }
.eraseToAnyPublisher()
}
} }

View file

@ -1,8 +1,10 @@
// Copyright © 2021 Metabolist. All rights reserved. // Copyright © 2021 Metabolist. All rights reserved.
import Combine
import CryptoKit import CryptoKit
import Foundation import Foundation
import Mastodon import Mastodon
import MastodonAPI
import Secrets import Secrets
enum NotificationExtensionServiceError: Error { enum NotificationExtensionServiceError: Error {
@ -48,6 +50,53 @@ public extension PushNotificationParsingService {
salt: salt), salt: salt),
identityId) identityId)
} }
func title(pushNotification: PushNotification,
identityId: Identity.Id,
accountId: Account.Id?) -> AnyPublisher<String, Error> {
switch pushNotification.notificationType {
case .poll, .status:
let secrets = Secrets(identityId: identityId, keychain: environment.keychain)
let instanceURL: URL
do {
instanceURL = try secrets.getInstanceURL()
} catch {
return Fail(error: error).eraseToAnyPublisher()
}
let mastodonAPIClient = MastodonAPIClient(session: .shared, instanceURL: instanceURL)
mastodonAPIClient.accessToken = pushNotification.accessToken
let endpoint = NotificationEndpoint.notification(id: String(pushNotification.notificationId))
return mastodonAPIClient.request(endpoint)
.map {
switch pushNotification.notificationType {
case .status:
return String.localizedStringWithFormat(
NSLocalizedString("notification.status-%@", comment: ""),
$0.account.displayName)
case .poll:
switch accountId ?? (try? AllIdentitiesService(environment: environment)
.identity(id: identityId)?.account?.id) {
case .some($0.account.id):
return NSLocalizedString("notification.poll.own", comment: "")
case .some:
return NSLocalizedString("notification.poll", comment: "")
default:
return NSLocalizedString("notification.poll.unknown", comment: "")
}
default:
return pushNotification.title
}
}
.eraseToAnyPublisher()
default:
return Just(pushNotification.title).setFailureType(to: Error.self).eraseToAnyPublisher()
}
}
} }
private extension PushNotificationParsingService { private extension PushNotificationParsingService {