mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 09:41:00 +00:00
Account for missing i18n in Mastodon notifications
This commit is contained in:
parent
f0eeb558a5
commit
2f0e35f343
4 changed files with 97 additions and 25 deletions
|
@ -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";
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue