chore: show error with 18n
This commit is contained in:
parent
0cb8d1bf6e
commit
732c5392d4
|
@ -1,5 +1,25 @@
|
|||
{
|
||||
"common": {
|
||||
"errors": {
|
||||
"item": {
|
||||
"username": "username",
|
||||
"email": "email",
|
||||
"password": "password",
|
||||
"agreement": "agreement",
|
||||
"locale": "locale",
|
||||
"reason": "reason"
|
||||
},
|
||||
"ERR_BLOCKED": "is blocked",
|
||||
"ERR_UNREACHABLE": "is unreachable",
|
||||
"ERR_TAKEN": "is taken",
|
||||
"ERR_RESERVED": "is reserved",
|
||||
"ERR_ACCEPTED": "must be accepted",
|
||||
"ERR_BLANK": "can't be blank",
|
||||
"ERR_INVALID": "is invalid",
|
||||
"ERR_TOO_LONG": "is too long",
|
||||
"ERR_TOO_SHORT": "is too short",
|
||||
"ERR_INCLUSION": "is inclusion"
|
||||
},
|
||||
"alerts": {
|
||||
"sign_up_failure": {
|
||||
"title": "Sign Up Failure"
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */; };
|
||||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; };
|
||||
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; };
|
||||
2D650FAB25ECDC9300851B58 /* Mastodon+Entidy+ErrorDetailReson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D650FAA25ECDC9300851B58 /* Mastodon+Entidy+ErrorDetailReson.swift */; };
|
||||
2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */; };
|
||||
2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Toot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */; };
|
||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; };
|
||||
|
@ -260,6 +261,7 @@
|
|||
2D5A3D6125CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewController+DebugAction.swift"; sourceTree = "<group>"; };
|
||||
2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+Timeline.swift"; sourceTree = "<group>"; };
|
||||
2D61335D25C1894B00CAE157 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = "<group>"; };
|
||||
2D650FAA25ECDC9300851B58 /* Mastodon+Entidy+ErrorDetailReson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entidy+ErrorDetailReson.swift"; sourceTree = "<group>"; };
|
||||
2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = "<group>"; };
|
||||
2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Toot.swift"; sourceTree = "<group>"; };
|
||||
2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1007,6 +1009,7 @@
|
|||
DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */,
|
||||
2D32EAB925CB9B0500C9ED86 /* UIView.swift */,
|
||||
0FAA101B25E10E760017CCDE /* UIFont.swift */,
|
||||
2D650FAA25ECDC9300851B58 /* Mastodon+Entidy+ErrorDetailReson.swift */,
|
||||
);
|
||||
path = Extension;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1502,6 +1505,7 @@
|
|||
2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */,
|
||||
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
|
||||
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */,
|
||||
2D650FAB25ECDC9300851B58 /* Mastodon+Entidy+ErrorDetailReson.swift in Sources */,
|
||||
DB118A8C25E4BFB500FAB162 /* HighlightDimmableButton.swift in Sources */,
|
||||
DB084B5725CBC56C00F898ED /* Toot.swift in Sources */,
|
||||
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// Mastodon+Entidy+ErrorDetailReason.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/3/1.
|
||||
//
|
||||
import MastodonSDK
|
||||
|
||||
extension Mastodon.Entity.ErrorDetailReason {
|
||||
func localizedDescription() -> String {
|
||||
switch self.error {
|
||||
case .ERR_BLOCKED:
|
||||
return L10n.Common.Errors.errBlocked
|
||||
case .ERR_UNREACHABLE:
|
||||
return L10n.Common.Errors.errUnreachable
|
||||
case .ERR_TAKEN:
|
||||
return L10n.Common.Errors.errTaken
|
||||
case .ERR_RESERVED:
|
||||
return L10n.Common.Errors.errReserved
|
||||
case .ERR_ACCEPTED:
|
||||
return L10n.Common.Errors.errAccepted
|
||||
case .ERR_BLANK:
|
||||
return L10n.Common.Errors.errBlank
|
||||
case .ERR_INVALID:
|
||||
return L10n.Common.Errors.errInvalid
|
||||
case .ERR_TOO_LONG:
|
||||
return L10n.Common.Errors.errTooLong
|
||||
case .ERR_TOO_SHORT:
|
||||
return L10n.Common.Errors.errTooShort
|
||||
case .ERR_INCLUSION:
|
||||
return L10n.Common.Errors.errInclusion
|
||||
case ._other:
|
||||
return self.errorDescription ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.ErrorDetail {
|
||||
func localizedDescription() -> String {
|
||||
var messages: [String?] = []
|
||||
if let username = self.username {
|
||||
if !username.isEmpty {
|
||||
let errors = username.map {
|
||||
L10n.Common.Errors.Item.username + " " + $0.localizedDescription()
|
||||
}
|
||||
messages.append(contentsOf: errors)
|
||||
}
|
||||
}
|
||||
if let email = self.email {
|
||||
if !email.isEmpty {
|
||||
let errors = email.map {
|
||||
L10n.Common.Errors.Item.email + " " + $0.localizedDescription()
|
||||
}
|
||||
messages.append(contentsOf: errors)
|
||||
}
|
||||
}
|
||||
if let password = self.password {
|
||||
if !password.isEmpty {
|
||||
let errors = password.map {
|
||||
L10n.Common.Errors.Item.password + " " + $0.localizedDescription()
|
||||
}
|
||||
messages.append(contentsOf: errors)
|
||||
}
|
||||
}
|
||||
if let agreement = self.agreement {
|
||||
if !agreement.isEmpty {
|
||||
let errors = agreement.map {
|
||||
L10n.Common.Errors.Item.agreement + " " + $0.localizedDescription()
|
||||
}
|
||||
messages.append(contentsOf: errors)
|
||||
}
|
||||
}
|
||||
if let locale = self.locale {
|
||||
if !locale.isEmpty {
|
||||
let errors = locale.map {
|
||||
L10n.Common.Errors.Item.locale + " " + $0.localizedDescription()
|
||||
}
|
||||
messages.append(contentsOf: errors)
|
||||
}
|
||||
}
|
||||
if let reason = self.reason {
|
||||
if !reason.isEmpty {
|
||||
let errors = reason.map {
|
||||
L10n.Common.Errors.Item.reason + " " + $0.localizedDescription()
|
||||
}
|
||||
messages.append(contentsOf: errors)
|
||||
}
|
||||
}
|
||||
let message = messages
|
||||
.compactMap { $0 }
|
||||
.joined(separator: ", ")
|
||||
return message
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import MastodonSDK
|
||||
// Reference:
|
||||
// https://nshipster.com/swift-foundation-error-protocols/
|
||||
extension UIAlertController {
|
||||
|
@ -43,3 +43,43 @@ extension UIAlertController {
|
|||
}
|
||||
}
|
||||
|
||||
extension UIAlertController {
|
||||
convenience init(
|
||||
for error: Mastodon.API.Error,
|
||||
title: String?,
|
||||
preferredStyle: UIAlertController.Style
|
||||
) {
|
||||
let _title: String
|
||||
let message: String?
|
||||
switch error.mastodonError {
|
||||
case .generic(let mastodonEntityError):
|
||||
|
||||
if let title = title {
|
||||
_title = title
|
||||
} else {
|
||||
_title = error.errorDescription ?? "Error"
|
||||
}
|
||||
var messages: [String?] = []
|
||||
if let details = mastodonEntityError.details {
|
||||
message = details.localizedDescription()
|
||||
} else {
|
||||
messages.append(contentsOf: [
|
||||
error.failureReason,
|
||||
error.recoverySuggestion
|
||||
])
|
||||
message = messages
|
||||
.compactMap { $0 }
|
||||
.joined(separator: " ")
|
||||
}
|
||||
default:
|
||||
_title = "Internal Error"
|
||||
message = error.localizedDescription
|
||||
}
|
||||
|
||||
self.init(
|
||||
title: _title,
|
||||
message: message,
|
||||
preferredStyle: preferredStyle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,42 @@ internal enum L10n {
|
|||
internal static let single = L10n.tr("Localizable", "Common.Countable.Photo.Single")
|
||||
}
|
||||
}
|
||||
internal enum Errors {
|
||||
/// must be accepted
|
||||
internal static let errAccepted = L10n.tr("Localizable", "Common.Errors.ErrAccepted")
|
||||
/// can't be blank
|
||||
internal static let errBlank = L10n.tr("Localizable", "Common.Errors.ErrBlank")
|
||||
/// is blocked
|
||||
internal static let errBlocked = L10n.tr("Localizable", "Common.Errors.ErrBlocked")
|
||||
/// is inclusion
|
||||
internal static let errInclusion = L10n.tr("Localizable", "Common.Errors.ErrInclusion")
|
||||
/// is invalid
|
||||
internal static let errInvalid = L10n.tr("Localizable", "Common.Errors.ErrInvalid")
|
||||
/// is reserved
|
||||
internal static let errReserved = L10n.tr("Localizable", "Common.Errors.ErrReserved")
|
||||
/// is taken
|
||||
internal static let errTaken = L10n.tr("Localizable", "Common.Errors.ErrTaken")
|
||||
/// is too long
|
||||
internal static let errTooLong = L10n.tr("Localizable", "Common.Errors.ErrTooLong")
|
||||
/// is too short
|
||||
internal static let errTooShort = L10n.tr("Localizable", "Common.Errors.ErrTooShort")
|
||||
/// is unreachable
|
||||
internal static let errUnreachable = L10n.tr("Localizable", "Common.Errors.ErrUnreachable")
|
||||
internal enum Item {
|
||||
/// agreement
|
||||
internal static let agreement = L10n.tr("Localizable", "Common.Errors.Item.Agreement")
|
||||
/// email
|
||||
internal static let email = L10n.tr("Localizable", "Common.Errors.Item.Email")
|
||||
/// locale
|
||||
internal static let locale = L10n.tr("Localizable", "Common.Errors.Item.Locale")
|
||||
/// password
|
||||
internal static let password = L10n.tr("Localizable", "Common.Errors.Item.Password")
|
||||
/// reason
|
||||
internal static let reason = L10n.tr("Localizable", "Common.Errors.Item.Reason")
|
||||
/// username
|
||||
internal static let username = L10n.tr("Localizable", "Common.Errors.Item.Username")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum Scene {
|
||||
|
|
|
@ -23,6 +23,22 @@
|
|||
"Common.Controls.Timeline.LoadMore" = "Load More";
|
||||
"Common.Countable.Photo.Multiple" = "photos";
|
||||
"Common.Countable.Photo.Single" = "photo";
|
||||
"Common.Errors.ErrAccepted" = "must be accepted";
|
||||
"Common.Errors.ErrBlank" = "can't be blank";
|
||||
"Common.Errors.ErrBlocked" = "is blocked";
|
||||
"Common.Errors.ErrInclusion" = "is inclusion";
|
||||
"Common.Errors.ErrInvalid" = "is invalid";
|
||||
"Common.Errors.ErrReserved" = "is reserved";
|
||||
"Common.Errors.ErrTaken" = "is taken";
|
||||
"Common.Errors.ErrTooLong" = "is too long";
|
||||
"Common.Errors.ErrTooShort" = "is too short";
|
||||
"Common.Errors.ErrUnreachable" = "is unreachable";
|
||||
"Common.Errors.Item.Agreement" = "agreement";
|
||||
"Common.Errors.Item.Email" = "email";
|
||||
"Common.Errors.Item.Locale" = "locale";
|
||||
"Common.Errors.Item.Password" = "password";
|
||||
"Common.Errors.Item.Reason" = "reason";
|
||||
"Common.Errors.Item.Username" = "username";
|
||||
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
|
||||
"Scene.ConfirmEmail.Button.OpenEmailApp" = "Open Email App";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.Description" = "Check if your email address is correct as well as your junk folder if you haven’t.";
|
||||
|
@ -62,4 +78,4 @@ any server.";
|
|||
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
|
||||
"Scene.ServerRules.Title" = "Some ground rules.";
|
||||
"Scene.Welcome.Slogan" = "Social networking
|
||||
back in your hands.";
|
||||
back in your hands.";
|
|
@ -105,6 +105,20 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
|
||||
let usernameIsTakenLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
let color = Asset.Colors.lightDangerRed.color
|
||||
let font = UIFont.preferredFont(forTextStyle: .caption1)
|
||||
let attributeString = NSMutableAttributedString()
|
||||
|
||||
let errorImage = NSTextAttachment()
|
||||
let configuration = UIImage.SymbolConfiguration(font: font)
|
||||
errorImage.image = UIImage(systemName: "xmark.octagon.fill", withConfiguration: configuration)?.withTintColor(color)
|
||||
let errorImageAttachment = NSAttributedString(attachment: errorImage)
|
||||
attributeString.append(errorImageAttachment)
|
||||
|
||||
let errorString = NSAttributedString(string: L10n.Common.Errors.Item.username + " " + L10n.Common.Errors.errTaken, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: color])
|
||||
attributeString.append(errorString)
|
||||
label.attributedText = attributeString
|
||||
|
||||
return label
|
||||
}()
|
||||
|
||||
|
@ -392,6 +406,7 @@ extension MastodonRegisterViewController {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] error in
|
||||
guard let self = self else { return }
|
||||
guard let error = error as? Mastodon.API.Error else { return }
|
||||
let alertController = UIAlertController(for: error, title: "Sign Up Failure", preferredStyle: .alert)
|
||||
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
||||
alertController.addAction(okAction)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import MastodonSDK
|
||||
|
||||
final class MastodonServerRulesViewController: UIViewController, NeedsDependency {
|
||||
|
||||
|
@ -162,6 +163,7 @@ extension MastodonServerRulesViewController {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] error in
|
||||
guard let self = self else { return }
|
||||
guard let error = error as? Mastodon.API.Error else { return }
|
||||
let alertController = UIAlertController(for: error, title: "Sign Up Failure", preferredStyle: .alert)
|
||||
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
||||
alertController.addAction(okAction)
|
||||
|
|
|
@ -65,37 +65,38 @@ extension Mastodon.Entity.Error {
|
|||
}
|
||||
}
|
||||
}
|
||||
extension Mastodon.Entity {
|
||||
public struct ErrorDetail: Codable {
|
||||
public let username: [ErrorDetailReason]?
|
||||
public let email: [ErrorDetailReason]?
|
||||
public let password: [ErrorDetailReason]?
|
||||
public let agreement: [ErrorDetailReason]?
|
||||
public let locale: [ErrorDetailReason]?
|
||||
public let reason: [ErrorDetailReason]?
|
||||
|
||||
public struct ErrorDetail: Codable {
|
||||
public let username: [ErrorDetailReson]?
|
||||
public let email: [ErrorDetailReson]?
|
||||
public let password: [ErrorDetailReson]?
|
||||
public let agreement: [ErrorDetailReson]?
|
||||
public let locale: [ErrorDetailReson]?
|
||||
public let reason: [ErrorDetailReson]?
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case username
|
||||
case email
|
||||
case password
|
||||
case agreement
|
||||
case locale
|
||||
case reason
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case username
|
||||
case email
|
||||
case password
|
||||
case agreement
|
||||
case locale
|
||||
case reason
|
||||
}
|
||||
}
|
||||
|
||||
public struct ErrorDetailReson: Codable {
|
||||
public init(error: String, errorDescription: String?) {
|
||||
self.error = Mastodon.Entity.Error.SignUpError(rawValue: error) ?? ._other(error)
|
||||
self.errorDescription = errorDescription
|
||||
}
|
||||
|
||||
public let error: Mastodon.Entity.Error.SignUpError
|
||||
public let errorDescription: String?
|
||||
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case error
|
||||
case errorDescription = "description"
|
||||
public struct ErrorDetailReason: Codable {
|
||||
public init(error: String, errorDescription: String?) {
|
||||
self.error = Mastodon.Entity.Error.SignUpError(rawValue: error) ?? ._other(error)
|
||||
self.errorDescription = errorDescription
|
||||
}
|
||||
|
||||
public let error: Mastodon.Entity.Error.SignUpError
|
||||
public let errorDescription: String?
|
||||
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case error
|
||||
case errorDescription = "description"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue