mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-23 15:11:14 +00:00
+16








fb3357c638
* ui: what's new in 7.0 (#7265) * docs: add Hosting your own Chat Relay guide (#7263) * docs: add Hosting your own Chat Relay guide * cli: run headless relay without a terminal --headless (no -e) now runs via simplexChatCore instead of the terminal UI, so the relay runs as a systemd service without a TTY (withTerminal requires one). Drains the event queue logging only errors, and sets stdout to line-buffering so logs reach the journal. * docs/chat-relay: debounce service * Apply suggestions from code review Co-authored-by: Evgeny <evgeny@poberezkin.com> --------- Co-authored-by: Evgeny <evgeny@poberezkin.com> * Translated using Weblate (German) Currently translated at 100.0% (2868 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (German) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (German) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (Russian) Currently translated at 96.5% (2381 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (Russian) Currently translated at 96.5% (2381 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (Russian) Currently translated at 96.5% (2381 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (French) Currently translated at 93.6% (2309 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/ * Translated using Weblate (Italian) Currently translated at 100.0% (2868 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.9% (2867 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 98.6% (2431 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/zh_Hans/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2868 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/ * Translated using Weblate (Dutch) Currently translated at 82.5% (2035 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/ * Translated using Weblate (Arabic) Currently translated at 29.8% (735 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ar/ * Translated using Weblate (Ukrainian) Currently translated at 87.0% (2145 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/uk/ * Translated using Weblate (Polish) Currently translated at 87.6% (2161 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pl/ * Translated using Weblate (Turkish) Currently translated at 86.8% (2140 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/ * Translated using Weblate (Turkish) Currently translated at 86.8% (2140 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/ * Translated using Weblate (Turkish) Currently translated at 100.0% (2868 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/ * Translated using Weblate (Turkish) Currently translated at 100.0% (2868 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2868 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Indonesian) Currently translated at 94.8% (2719 of 2868 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/id/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2867 of 2867 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (German) Currently translated at 100.0% (2867 of 2867 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (German) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (German) Currently translated at 100.0% (2880 of 2880 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Italian) Currently translated at 100.0% (2880 of 2880 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2880 of 2880 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (German) Currently translated at 100.0% (2885 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Italian) Currently translated at 100.0% (2885 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.3% (2450 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/zh_Hans/ * Translated using Weblate (German) Currently translated at 100.0% (2885 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Italian) Currently translated at 100.0% (2885 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.9% (2884 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2885 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2885 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2885 of 2885 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (2886 of 2886 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.9% (2885 of 2886 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2886 of 2886 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (2890 of 2890 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.9% (2889 of 2890 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2890 of 2890 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2890 of 2890 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (German) Currently translated at 100.0% (2890 of 2890 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2890 of 2890 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2465 of 2465 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/ * process translations * restore and fix * process xliff * ui: fix translation typos and mistranslations (de, es, it, tr, hu, id, ar, zh-CN) --------- Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Co-authored-by: Evgeny <evgeny@poberezkin.com> Co-authored-by: mlanp <github@lang.xyz> Co-authored-by: Pixelcode <pixelcode@dismail.de> Co-authored-by: J R <jr@simplex.chat> Co-authored-by: Evgeny Poberezkin <e.poberezkin@me.com> Co-authored-by: Evgeny <ep@simplex.chat> Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com> Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org> Co-authored-by: Hosted Weblate user 54392 <hamburger2048@users.noreply.hosted.weblate.org> Co-authored-by: re me <jisizhang001@gmail.com> Co-authored-by: No name <usir.alerts@onionmail.org> Co-authored-by: M1K4 <oomikaoo@gmail.com> Co-authored-by: Muhammad <muhammad.aem@outlook.com> Co-authored-by: Max <worklmaksim@gmail.com> Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com> Co-authored-by: Abdullah Koyuncu <wisewebworks@outlook.com> Co-authored-by: Omer <abulomer2001@gmail.com> Co-authored-by: echoloji <mehmetksm@tuta.io> Co-authored-by: summoner001 <summoner@disroot.org> Co-authored-by: Rafi <rafimuhmad90@protonmail.com> Co-authored-by: BlacAmDK <BD1079259369@gmail.com> Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org> Co-authored-by: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com>
198 lines
8.9 KiB
Swift
198 lines
8.9 KiB
Swift
//
|
|
// ErrorAlert.swift
|
|
// SimpleXChat
|
|
//
|
|
// Created by Levitating Pineapple on 20/07/2024.
|
|
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
public struct ErrorAlert: Error {
|
|
public let title: LocalizedStringKey
|
|
public let message: LocalizedStringKey?
|
|
|
|
public init(
|
|
title: LocalizedStringKey,
|
|
message: LocalizedStringKey? = nil
|
|
) {
|
|
self.title = title
|
|
self.message = message
|
|
}
|
|
|
|
public init<A: View>(
|
|
title: LocalizedStringKey,
|
|
message: LocalizedStringKey? = nil,
|
|
@ViewBuilder actions: @escaping () -> A
|
|
) {
|
|
self.title = title
|
|
self.message = message
|
|
}
|
|
|
|
public init(_ title: LocalizedStringKey) {
|
|
self = ErrorAlert(title: title)
|
|
}
|
|
|
|
public init(_ error: any Error) {
|
|
self = if let chatError = error as? ChatError {
|
|
if let a = getNetworkErrorAlert(chatError) {
|
|
ErrorAlert(title: "\(a.title)", message: a.message.map { "\($0)" })
|
|
} else {
|
|
ErrorAlert("\(chatErrorString(chatError))")
|
|
}
|
|
} else {
|
|
ErrorAlert("\(error.localizedDescription)")
|
|
}
|
|
}
|
|
}
|
|
|
|
extension LocalizedStringKey: @unchecked Sendable { }
|
|
|
|
extension View {
|
|
/// Bridges ``ErrorAlert`` to the generic alert API.
|
|
/// - Parameters:
|
|
/// - errorAlert: Binding to the Error, which is rendered in the alert
|
|
/// - actions: View Builder containing action buttons.
|
|
/// System defaults to `Ok` dismiss error action, when no actions are provided.
|
|
/// System implicitly adds `Cancel` action, if a destructive action is present
|
|
///
|
|
/// - Returns: View, which displays ErrorAlert?, when set.
|
|
@ViewBuilder public func alert<A: View>(
|
|
_ errorAlert: Binding<ErrorAlert?>,
|
|
@ViewBuilder actions: (ErrorAlert) -> A = { _ in EmptyView() }
|
|
) -> some View {
|
|
alert(
|
|
errorAlert.wrappedValue?.title ?? "",
|
|
isPresented: Binding<Bool>(
|
|
get: { errorAlert.wrappedValue != nil },
|
|
set: { if !$0 { errorAlert.wrappedValue = nil } }
|
|
),
|
|
actions: {
|
|
if let alert = errorAlert.wrappedValue { actions(alert) }
|
|
},
|
|
message: {
|
|
if let message = errorAlert.wrappedValue?.message {
|
|
Text(message)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
public func getNetworkErrorAlert(_ e: ChatError) -> (title: String, message: String?)? {
|
|
switch e {
|
|
case let .errorAgent(.BROKER(addr, .TIMEOUT)):
|
|
(
|
|
title: NSLocalizedString("Connection timeout", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Please check your network connection with %@ and try again.", comment: ""), serverHostname(addr))
|
|
)
|
|
case let .errorAgent(.BROKER(addr, .NETWORK(.unknownCAError))):
|
|
(
|
|
title: NSLocalizedString("Connection error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Fingerprint in server address does not match certificate: %@.", comment: ""), serverHostname(addr))
|
|
)
|
|
case let .errorAgent(.BROKER(addr, .NETWORK)):
|
|
(
|
|
title: NSLocalizedString("Connection error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Please check your network connection with %@ and try again.", comment: ""), serverHostname(addr))
|
|
)
|
|
case let .errorAgent(.BROKER(addr, .HOST)):
|
|
(
|
|
title: NSLocalizedString("Connection error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Server address is incompatible with network settings: %@.", comment: ""), serverHostname(addr))
|
|
)
|
|
case let .errorAgent(.BROKER(addr, .TRANSPORT(.version))):
|
|
(
|
|
title: NSLocalizedString("Connection error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Server version is incompatible with your app: %@.", comment: ""), serverHostname(addr))
|
|
)
|
|
case let .errorAgent(.SMP(serverAddress, .PROXY(proxyErr))):
|
|
smpProxyErrorAlert(proxyErr, serverAddress)
|
|
case let .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr)))):
|
|
proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer)
|
|
default: nil
|
|
}
|
|
}
|
|
|
|
private func smpProxyErrorAlert(_ proxyErr: ProxyError, _ srvAddr: String) -> (title: String, message: String?)? {
|
|
switch proxyErr {
|
|
case .BROKER(brokerErr: .TIMEOUT):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Error connecting to forwarding server %@. Please try later.", comment: ""), serverHostname(srvAddr))
|
|
)
|
|
case .BROKER(brokerErr: .NETWORK(.unknownCAError)):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Fingerprint in forwarding server address does not match certificate: %@.", comment: ""), serverHostname(srvAddr))
|
|
)
|
|
case .BROKER(brokerErr: .NETWORK):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Error connecting to forwarding server %@. Please try later.", comment: ""), serverHostname(srvAddr))
|
|
)
|
|
case .BROKER(brokerErr: .HOST):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Forwarding server address is incompatible with network settings: %@.", comment: ""), serverHostname(srvAddr))
|
|
)
|
|
case .BROKER(brokerErr: .TRANSPORT(.version)):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Forwarding server version is incompatible with network settings: %@.", comment: ""), serverHostname(srvAddr))
|
|
)
|
|
default:
|
|
nil
|
|
}
|
|
}
|
|
|
|
private func proxyDestinationErrorAlert(_ proxyErr: ProxyError, _ proxyServer: String, _ relayServer: String) -> (title: String, message: String?)? {
|
|
switch proxyErr {
|
|
case .BROKER(brokerErr: .TIMEOUT):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Forwarding server %@ failed to connect to destination server %@. Please try later.", comment: ""), serverHostname(proxyServer), serverHostname(relayServer))
|
|
)
|
|
case .BROKER(brokerErr: .NETWORK(.unknownCAError)):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Fingerprint in destination server address does not match certificate: %@.", comment: ""), serverHostname(relayServer))
|
|
)
|
|
case .BROKER(brokerErr: .NETWORK):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Forwarding server %@ failed to connect to destination server %@. Please try later.", comment: ""), serverHostname(proxyServer), serverHostname(relayServer))
|
|
)
|
|
case .NO_SESSION:
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Forwarding server %@ failed to connect to destination server %@. Please try later.", comment: ""), serverHostname(proxyServer), serverHostname(relayServer))
|
|
)
|
|
case .BROKER(brokerErr: .HOST):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Destination server address of %1$@ is incompatible with forwarding server %2$@ settings.", comment: ""), serverHostname(relayServer), serverHostname(proxyServer))
|
|
)
|
|
case .BROKER(brokerErr: .TRANSPORT(.version)):
|
|
(
|
|
title: NSLocalizedString("Private routing error", comment: ""),
|
|
message: String.localizedStringWithFormat(NSLocalizedString("Destination server version of %1$@ is incompatible with forwarding server %2$@.", comment: ""), serverHostname(relayServer), serverHostname(proxyServer))
|
|
)
|
|
default:
|
|
nil
|
|
}
|
|
}
|
|
|
|
public func serverHostname(_ srv: String) -> String {
|
|
parseServerAddress(srv)?.hostnames.first ?? srv
|
|
}
|
|
|
|
public func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey {
|
|
switch err {
|
|
case let .noDown(dbMigrations):
|
|
"database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))"
|
|
case let .different(appMigration, dbMigration):
|
|
"different migration in the app/database: \(appMigration) / \(dbMigration)"
|
|
}
|
|
}
|