diff --git a/Packages/App/Sources/AppUIMain/AppUIMain.swift b/Packages/App/Sources/AppUIMain/AppUIMain.swift index 8e871b4d..da6dfc85 100644 --- a/Packages/App/Sources/AppUIMain/AppUIMain.swift +++ b/Packages/App/Sources/AppUIMain/AppUIMain.swift @@ -71,6 +71,9 @@ private extension AppUIMain { } } } catch { + if (error as? PassepartoutError)?.code == .incompleteModule { + return + } fatalError("\(moduleType): empty module is not buildable: \(error)") } } diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift index f2fe53cd..2dd5aacb 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift @@ -139,7 +139,7 @@ private extension ProfileCoordinator { do { try iapManager.verify(profileToSave, extra: profileEditor.extraFeatures) - } catch AppError.ineligibleProfile(var requiredFeatures) { + } catch AppError.ineligibleProfile(let requiredFeatures) { guard !iapManager.isLoadingReceipt else { let V = Strings.Views.Paywall.Alerts.Verification.self errorHandler.handle( diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift b/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift index 62d0bb46..835a9502 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift @@ -108,7 +108,7 @@ private extension ProfileEditView { .onMove(perform: moveModules) .onDelete(perform: removeModules) - addModuleButton + addModuleMenu } .themeSection( header: Strings.Global.Nouns.modules, @@ -137,7 +137,7 @@ private extension ProfileEditView { } } - var addModuleButton: some View { + var addModuleMenu: some View { AddModuleMenu(moduleTypes: profileEditor.availableModuleTypes) { flow?.onNewModule($0) } label: { diff --git a/Packages/App/Sources/UILibrary/Business/ProfileEditor.swift b/Packages/App/Sources/UILibrary/Business/ProfileEditor.swift index 09ea7ada..dbbc1165 100644 --- a/Packages/App/Sources/UILibrary/Business/ProfileEditor.swift +++ b/Packages/App/Sources/UILibrary/Business/ProfileEditor.swift @@ -191,6 +191,12 @@ private extension ProfileEditor { extension ProfileEditor { public func build() throws -> Profile { + + // add this check in the app, the library does not enforce it + guard !editableProfile.activeModulesIds.isEmpty else { + throw PassepartoutError(.noActiveModules) + } + let builder = try editableProfile.builder() let profile = try builder.tryBuild() diff --git a/Packages/App/Sources/UILibrary/L10n/AppError+L10n.swift b/Packages/App/Sources/UILibrary/L10n/AppError+L10n.swift index 0fb5f1f9..7c8417a2 100644 --- a/Packages/App/Sources/UILibrary/L10n/AppError+L10n.swift +++ b/Packages/App/Sources/UILibrary/L10n/AppError+L10n.swift @@ -69,15 +69,22 @@ extension TaskTimeoutError: PassepartoutErrorMappable { extension PassepartoutError: @retroactive LocalizedError { public var errorDescription: String? { + let V = Strings.Errors.App.Passepartout.self switch code { case .connectionModuleRequired: - return Strings.Errors.App.Passepartout.connectionModuleRequired + return V.connectionModuleRequired case .corruptProviderModule: - return Strings.Errors.App.Passepartout.corruptProviderModule(reason?.localizedDescription ?? "") + return V.corruptProviderModule(reason?.localizedDescription ?? "") case .incompatibleModules: - return Strings.Errors.App.Passepartout.incompatibleModules + return V.incompatibleModules + + case .incompleteModule: + guard let builder = userInfo as? any ModuleBuilder else { + break + } + return V.incompleteModule(builder.moduleType.localizedDescription) case .invalidFields: let fields = (userInfo as? [String: String?]) @@ -88,35 +95,36 @@ extension PassepartoutError: @retroactive LocalizedError { .joined(separator: ",") } - return [Strings.Errors.App.Passepartout.invalidFields, fields] + return [V.invalidFields, fields] .compactMap { $0 } .joined(separator: " ") case .missingProviderEntity: - return Strings.Errors.App.Passepartout.missingProviderEntity + return V.missingProviderEntity case .noActiveModules: - return Strings.Errors.App.Passepartout.noActiveModules + return V.noActiveModules case .parsing: let message = userInfo as? String ?? (reason as? LocalizedError)?.localizedDescription - return [Strings.Errors.App.Passepartout.parsing, message] + return [V.parsing, message] .compactMap { $0 } .joined(separator: " ") case .providerRequired: - return Strings.Errors.App.Passepartout.providerRequired + return V.providerRequired case .timeout: - return Strings.Errors.App.Passepartout.timeout + return V.timeout case .unhandled: return reason?.localizedDescription default: - return Strings.Errors.App.Passepartout.default(code.rawValue) + break } + return V.default(code.rawValue) } } diff --git a/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift b/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift index 64429fe6..4722dafe 100644 --- a/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift +++ b/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift @@ -129,6 +129,10 @@ public enum Strings { } /// Some active modules are incompatible, try to only activate one of them. public static let incompatibleModules = Strings.tr("Localizable", "errors.app.passepartout.incompatible_modules", fallback: "Some active modules are incompatible, try to only activate one of them.") + /// Please finish the configuration of the %@ module. + public static func incompleteModule(_ p1: Any) -> String { + return Strings.tr("Localizable", "errors.app.passepartout.incomplete_module", String(describing: p1), fallback: "Please finish the configuration of the %@ module.") + } /// Invalid fields. public static let invalidFields = Strings.tr("Localizable", "errors.app.passepartout.invalid_fields", fallback: "Invalid fields.") /// No server selected in provider. diff --git a/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings index 805e650e..cd94c8fd 100644 --- a/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Verbindung zum Anbieter-Server konnte nicht hergestellt werden (Grund=%@)."; "errors.app.passepartout.default" = "Operation konnte nicht abgeschlossen werden (Code=%@)."; "errors.app.passepartout.incompatible_modules" = "Einige aktive Module sind inkompatibel, versuche, nur eines von ihnen zu aktivieren."; +"errors.app.passepartout.incomplete_module" = "Bitte schließen Sie die Konfiguration des %@-Moduls ab."; "errors.app.passepartout.invalid_fields" = "Ungültige Felder."; "errors.app.passepartout.missing_provider_entity" = "Kein Server beim Anbieter ausgewählt."; "errors.app.passepartout.no_active_modules" = "Das Profil hat keine aktiven Module."; diff --git a/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings index f31910a0..730a6756 100644 --- a/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Δεν ήταν δυνατή η σύνδεση στον διακομιστή παρόχου (λόγος=%@)."; "errors.app.passepartout.default" = "Δεν ήταν δυνατή η ολοκλήρωση της λειτουργίας (κωδικός=%@)."; "errors.app.passepartout.incompatible_modules" = "Μερικές ενεργές μονάδες είναι ασύμβατες, δοκιμάστε να ενεργοποιήσετε μόνο μία."; +"errors.app.passepartout.incomplete_module" = "Ολοκληρώστε τη ρύθμιση του %@ module."; "errors.app.passepartout.invalid_fields" = "Μη έγκυρα πεδία."; "errors.app.passepartout.missing_provider_entity" = "Δεν επιλέχθηκε διακομιστής στον πάροχο."; "errors.app.passepartout.no_active_modules" = "Το προφίλ δεν έχει ενεργές μονάδες."; diff --git a/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings index 48927c7e..add26978 100644 --- a/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings @@ -372,6 +372,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Unable to connect to provider server (reason=%@)."; "errors.app.passepartout.default" = "Unable to complete operation (code=%@)."; "errors.app.passepartout.incompatible_modules" = "Some active modules are incompatible, try to only activate one of them."; +"errors.app.passepartout.incomplete_module" = "Please finish the configuration of the %@ module."; "errors.app.passepartout.invalid_fields" = "Invalid fields."; "errors.app.passepartout.missing_provider_entity" = "No server selected in provider."; "errors.app.passepartout.no_active_modules" = "The profile has no active modules."; diff --git a/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings index 70b59492..3d80a754 100644 --- a/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "No se pudo conectar al servidor del proveedor (motivo=%@)."; "errors.app.passepartout.default" = "No se pudo completar la operación (código=%@)."; "errors.app.passepartout.incompatible_modules" = "Algunos módulos activos son incompatibles, intenta activar solo uno de ellos."; +"errors.app.passepartout.incomplete_module" = "Por favor, completa la configuración del módulo %@."; "errors.app.passepartout.invalid_fields" = "Campos no válidos."; "errors.app.passepartout.missing_provider_entity" = "No se seleccionó un servidor en el proveedor."; "errors.app.passepartout.no_active_modules" = "El perfil no tiene módulos activos."; diff --git a/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings index 2ccd3933..081cb9c6 100644 --- a/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Impossible de se connecter au serveur du fournisseur (raison=%@)."; "errors.app.passepartout.default" = "Impossible de terminer l'opération (code=%@)."; "errors.app.passepartout.incompatible_modules" = "Certains modules actifs sont incompatibles, essayez d'en activer un seul."; +"errors.app.passepartout.incomplete_module" = "Veuillez terminer la configuration du module %@."; "errors.app.passepartout.invalid_fields" = "Champs invalides."; "errors.app.passepartout.missing_provider_entity" = "Aucun serveur sélectionné chez le fournisseur."; "errors.app.passepartout.no_active_modules" = "Le profil n'a pas de modules actifs."; diff --git a/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings index 509774ed..298903dc 100644 --- a/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Impossibile connettersi al server del provider (motivo=%@)."; "errors.app.passepartout.default" = "Impossibile completare l'operazione (codice=%@)."; "errors.app.passepartout.incompatible_modules" = "Alcuni moduli attivi sono incompatibili, prova ad attivare solo uno di essi."; +"errors.app.passepartout.incomplete_module" = "Completa la configurazione del modulo %@."; "errors.app.passepartout.invalid_fields" = "Campi non validi."; "errors.app.passepartout.missing_provider_entity" = "Nessun server selezionato nel provider."; "errors.app.passepartout.no_active_modules" = "Il profilo non ha moduli attivi."; diff --git a/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings index eb98cced..fd5d65e7 100644 --- a/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Kan geen verbinding maken met de provider-server (reden=%@)."; "errors.app.passepartout.default" = "Kan bewerking niet voltooien (code=%@)."; "errors.app.passepartout.incompatible_modules" = "Sommige actieve modules zijn incompatibel. Probeer er slechts één te activeren."; +"errors.app.passepartout.incomplete_module" = "Voltooi de configuratie van de %@-module."; "errors.app.passepartout.invalid_fields" = "Ongeldige velden."; "errors.app.passepartout.missing_provider_entity" = "Geen server geselecteerd bij provider."; "errors.app.passepartout.no_active_modules" = "Het profiel heeft geen actieve modules."; diff --git a/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings index f5d0c793..66067a3a 100644 --- a/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Nie można połączyć się z serwerem dostawcy (powód=%@)."; "errors.app.passepartout.default" = "Nie można ukończyć operacji (kod=%@)."; "errors.app.passepartout.incompatible_modules" = "Niektóre aktywne moduły są niekompatybilne. Spróbuj aktywować tylko jeden."; +"errors.app.passepartout.incomplete_module" = "Proszę dokończyć konfigurację modułu %@."; "errors.app.passepartout.invalid_fields" = "Nieprawidłowe pola."; "errors.app.passepartout.missing_provider_entity" = "Nie wybrano serwera w dostawcy."; "errors.app.passepartout.no_active_modules" = "Profil nie ma aktywnych modułów."; diff --git a/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings index f059be7f..5ece7d44 100644 --- a/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Não foi possível conectar ao servidor do provedor (motivo=%@)."; "errors.app.passepartout.default" = "Não foi possível concluir a operação (código=%@)."; "errors.app.passepartout.incompatible_modules" = "Alguns módulos ativos são incompatíveis, tente ativar apenas um."; +"errors.app.passepartout.incomplete_module" = "Conclua a configuração do módulo %@."; "errors.app.passepartout.invalid_fields" = "Campos inválidos."; "errors.app.passepartout.missing_provider_entity" = "Nenhum servidor selecionado no provedor."; "errors.app.passepartout.no_active_modules" = "O perfil não possui módulos ativos."; diff --git a/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings index 922e09f8..ec12bc25 100644 --- a/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Не удалось подключиться к серверу поставщика (причина=%@)."; "errors.app.passepartout.default" = "Не удалось выполнить операцию (код=%@)."; "errors.app.passepartout.incompatible_modules" = "Некоторые активные модули несовместимы, попробуйте активировать только один."; +"errors.app.passepartout.incomplete_module" = "Пожалуйста, завершите настройку модуля %@."; "errors.app.passepartout.invalid_fields" = "Некорректные поля."; "errors.app.passepartout.missing_provider_entity" = "На провайдере не выбран сервер."; "errors.app.passepartout.no_active_modules" = "В профиле нет активных модулей."; diff --git a/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings index 7afcd5c1..28281aa8 100644 --- a/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Kan inte ansluta till leverantörsservern (anledning=%@)."; "errors.app.passepartout.default" = "Kan inte slutföra åtgärden (kod=%@)."; "errors.app.passepartout.incompatible_modules" = "Vissa aktiva moduler är inkompatibla, försök att endast aktivera en."; +"errors.app.passepartout.incomplete_module" = "Vänligen slutför konfigurationen av %@-modulen."; "errors.app.passepartout.invalid_fields" = "Ogiltiga fält."; "errors.app.passepartout.missing_provider_entity" = "Ingen server vald hos leverantören."; "errors.app.passepartout.no_active_modules" = "Profilen har inga aktiva moduler."; diff --git a/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings index db3904dd..1cfb2d53 100644 --- a/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "Не вдалося підключитися до сервера постачальника (причина=%@)."; "errors.app.passepartout.default" = "Не вдалося виконати операцію (код=%@)."; "errors.app.passepartout.incompatible_modules" = "Деякі активні модулі несумісні, спробуйте активувати лише один."; +"errors.app.passepartout.incomplete_module" = "Будь ласка, завершіть налаштування модуля %@."; "errors.app.passepartout.invalid_fields" = "Некоректні поля."; "errors.app.passepartout.missing_provider_entity" = "Не вибрано сервер у постачальника."; "errors.app.passepartout.no_active_modules" = "Профіль не має активних модулів."; diff --git a/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings index 23230009..e167ed37 100644 --- a/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings @@ -31,6 +31,7 @@ "errors.app.passepartout.corrupt_provider_module" = "无法连接到提供商服务器(原因=%@)。"; "errors.app.passepartout.default" = "无法完成操作(代码=%@)。"; "errors.app.passepartout.incompatible_modules" = "某些激活的模块不兼容,请尝试仅激活一个模块。"; +"errors.app.passepartout.incomplete_module" = "请完成 %@ 模块的配置。"; "errors.app.passepartout.invalid_fields" = "字段无效。"; "errors.app.passepartout.missing_provider_entity" = "未在提供商中选择服务器。"; "errors.app.passepartout.no_active_modules" = "配置文件没有激活模块。"; diff --git a/Packages/App/Sources/UILibrary/UILibrary.swift b/Packages/App/Sources/UILibrary/UILibrary.swift index dc6f4e27..51f305b2 100644 --- a/Packages/App/Sources/UILibrary/UILibrary.swift +++ b/Packages/App/Sources/UILibrary/UILibrary.swift @@ -72,6 +72,9 @@ private extension UILibrary { fatalError("\(moduleType): #2 is not AppFeatureRequiring") } } catch { + if (error as? PassepartoutError)?.code == .incompleteModule { + return + } fatalError("\(moduleType): empty module is not buildable: \(error)") } } diff --git a/Packages/PassepartoutKit-Source b/Packages/PassepartoutKit-Source index c3101f02..191369cb 160000 --- a/Packages/PassepartoutKit-Source +++ b/Packages/PassepartoutKit-Source @@ -1 +1 @@ -Subproject commit c3101f02e620d4ff986a8b846c0ea35fd62d63ec +Subproject commit 191369cb4a9a34acac747f2fffd19ea470f8f79e