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";
"notification.signed-in-as-%@" = "Logged in as %@";
"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.mentions" = "Mentions";
"ok" = "OK";

View file

@ -129,6 +129,7 @@
D09D972225C65682007E6394 /* SeparatorConfiguredCollectionViewListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09D972125C65682007E6394 /* SeparatorConfiguredCollectionViewListCell.swift */; };
D0A1F4F7252E7D4B004435BF /* TableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A1F4F6252E7D4B004435BF /* TableViewDataSource.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 */; };
D0B5FE9B251583DB00478838 /* ProfileCollection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */; };
D0B8510C25259E56004E0744 /* LoadMoreTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B8510B25259E56004E0744 /* LoadMoreTableViewCell.swift */; };
@ -998,6 +999,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D0B325EB25E88ADC00C24BEA /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

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

View file

@ -1,8 +1,10 @@
// Copyright © 2021 Metabolist. All rights reserved.
import Combine
import CryptoKit
import Foundation
import Mastodon
import MastodonAPI
import Secrets
enum NotificationExtensionServiceError: Error {
@ -48,6 +50,53 @@ public extension PushNotificationParsingService {
salt: salt),
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 {