From f7004be5d17832b6215e7acb9f25cf55d25fdba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o=20Gon=C3=A7alves?= <5808343+bgoncal@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:30:22 +0100 Subject: [PATCH 1/2] Add new connection error explanation screen (#3274) ## Summary ## Screenshots ![CleanShot 2024-12-16 at 12 11 07@2x](https://github.com/user-attachments/assets/0b6ac37b-816b-40cd-8db3-5bfe43162042) ## Link to pull request in Documentation repository Documentation: home-assistant/companion.home-assistant# ## Any other notes --- HomeAssistant.xcodeproj/project.pbxproj | 18 ++ .../discord.fill.symbolset/Contents.json | 12 ++ .../discord.fill.symbolset/discord.fill.svg | 200 ++++++++++++++++++ .../github.fill.symbolset/Contents.json | 12 ++ .../github.fill.symbolset/github.fill.svg | 167 +++++++++++++++ .../Resources/en.lproj/Localizable.strings | 10 +- .../WebView/ConnectionErrorDetailsView.swift | 76 +++++++ Sources/App/WebView/NoActiveURLView.swift | 26 ++- Sources/App/WebView/WebViewController.swift | 21 +- .../Components/ExternalLinkButton.swift | 43 ++++ .../Components/SheetCloseButton.swift | 24 +++ Sources/Shared/ExternalLink.swift | 7 + .../Shared/Resources/Swiftgen/Strings.swift | 24 +++ 13 files changed, 616 insertions(+), 24 deletions(-) create mode 100644 Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/Contents.json create mode 100644 Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/discord.fill.svg create mode 100644 Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/Contents.json create mode 100644 Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/github.fill.svg create mode 100644 Sources/App/WebView/ConnectionErrorDetailsView.swift create mode 100644 Sources/Shared/DesignSystem/Components/ExternalLinkButton.swift create mode 100644 Sources/Shared/DesignSystem/Components/SheetCloseButton.swift create mode 100644 Sources/Shared/ExternalLink.swift diff --git a/HomeAssistant.xcodeproj/project.pbxproj b/HomeAssistant.xcodeproj/project.pbxproj index a4745356e..b671e9b74 100644 --- a/HomeAssistant.xcodeproj/project.pbxproj +++ b/HomeAssistant.xcodeproj/project.pbxproj @@ -605,6 +605,9 @@ 4251AAC02C6CE376004CCC9D /* MagicItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D5ACCF2C639AB700D9C4E2 /* MagicItem.swift */; }; 4251AAC12C6CE9C4004CCC9D /* WatchConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4251AAB82C6CE1B4004CCC9D /* WatchConfig.swift */; }; 4251AAC22C6CE9CB004CCC9D /* WatchConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4251AAB82C6CE1B4004CCC9D /* WatchConfig.swift */; }; + 4254C4CA2D103ABB00245021 /* ExternalLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4254C4C92D103ABB00245021 /* ExternalLink.swift */; }; + 4254C4CB2D103ABB00245021 /* ExternalLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4254C4C92D103ABB00245021 /* ExternalLink.swift */; }; + 4254C4CD2D103F7B00245021 /* ExternalLinkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4254C4CC2D103F7B00245021 /* ExternalLinkButton.swift */; }; 425573C72B5572AD00145217 /* CarPlayServerListTemplate+Build.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425573C62B5572AD00145217 /* CarPlayServerListTemplate+Build.swift */; }; 425573C92B5572DB00145217 /* CarPlayServerListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425573C82B5572DB00145217 /* CarPlayServerListViewModel.swift */; }; 425573CC2B5574AD00145217 /* CarPlayAreasZonesTemplate+Build.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425573CB2B5574AD00145217 /* CarPlayAreasZonesTemplate+Build.swift */; }; @@ -703,6 +706,8 @@ 429821172CD0DDCD005ECD39 /* HAButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429821162CD0DDCD005ECD39 /* HAButtonStyles.swift */; }; 429821192CD0DEE2005ECD39 /* HAButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429821162CD0DDCD005ECD39 /* HAButtonStyles.swift */; }; 429BA2AF2C800CAB00A50996 /* SFSymbolEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429BA2AE2C800CAB00A50996 /* SFSymbolEntity.swift */; }; + 429BEA1A2D102F3A00F070F9 /* ConnectionErrorDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429BEA192D102F3A00F070F9 /* ConnectionErrorDetailsView.swift */; }; + 429BEA1D2D10315F00F070F9 /* SheetCloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429BEA1B2D1030EA00F070F9 /* SheetCloseButton.swift */; }; 429C72202B28D0EC00BCD558 /* Haptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429C721F2B28D0EC00BCD558 /* Haptics.swift */; }; 42A2AB7C2C806D5D00C5608D /* SharedPush in Resources */ = {isa = PBXBuildFile; fileRef = 42A2AB7B2C806D5D00C5608D /* SharedPush */; }; 42A2AB7D2C806D5D00C5608D /* SharedPush in Resources */ = {isa = PBXBuildFile; fileRef = 42A2AB7B2C806D5D00C5608D /* SharedPush */; }; @@ -1912,6 +1917,8 @@ 4251AA9A2C6B9DBE004CCC9D /* MagicItemEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicItemEditViewModel.swift; sourceTree = ""; }; 4251AAB82C6CE1B4004CCC9D /* WatchConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConfig.swift; sourceTree = ""; }; 4251AABA2C6CE1D0004CCC9D /* MagicItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicItemProvider.swift; sourceTree = ""; }; + 4254C4C92D103ABB00245021 /* ExternalLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLink.swift; sourceTree = ""; }; + 4254C4CC2D103F7B00245021 /* ExternalLinkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLinkButton.swift; sourceTree = ""; }; 425573C62B5572AD00145217 /* CarPlayServerListTemplate+Build.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarPlayServerListTemplate+Build.swift"; sourceTree = ""; }; 425573C82B5572DB00145217 /* CarPlayServerListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlayServerListViewModel.swift; sourceTree = ""; }; 425573CB2B5574AD00145217 /* CarPlayAreasZonesTemplate+Build.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarPlayAreasZonesTemplate+Build.swift"; sourceTree = ""; }; @@ -2011,6 +2018,8 @@ 429821132CD0DD85005ECD39 /* BluetoothPermissionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothPermissionViewModel.swift; sourceTree = ""; }; 429821162CD0DDCD005ECD39 /* HAButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HAButtonStyles.swift; sourceTree = ""; }; 429BA2AE2C800CAB00A50996 /* SFSymbolEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFSymbolEntity.swift; sourceTree = ""; }; + 429BEA192D102F3A00F070F9 /* ConnectionErrorDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionErrorDetailsView.swift; sourceTree = ""; }; + 429BEA1B2D1030EA00F070F9 /* SheetCloseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetCloseButton.swift; sourceTree = ""; }; 429C721F2B28D0EC00BCD558 /* Haptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptics.swift; sourceTree = ""; }; 42A2AB7B2C806D5D00C5608D /* SharedPush */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = SharedPush; sourceTree = ""; }; 42A2AB7F2C80751E00C5608D /* ControlAssist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlAssist.swift; sourceTree = ""; }; @@ -3114,6 +3123,7 @@ 113FB1122515A065000AC680 /* ScaleFactorMutator.swift */, 11DE822D24FAC51000E636B8 /* IncomingURLHandler.swift */, B64BB3A71E9C6551001E8B46 /* WebViewController.swift */, + 429BEA192D102F3A00F070F9 /* ConnectionErrorDetailsView.swift */, 420C57C62D0A6DE700D2D9AC /* NoActiveURLView.swift */, 42A47A842C45218D00C9B43D /* WebViewExternalMessageHandler.swift */, 42A47A8B2C4547B800C9B43D /* WebViewExternalMessageHandler+Build.swift */, @@ -4124,10 +4134,12 @@ 42CA28B12B101D9C0093B31A /* Components */ = { isa = PBXGroup; children = ( + 429BEA1B2D1030EA00F070F9 /* SheetCloseButton.swift */, 42FCD0052B9B1D9E0057783F /* CollapsibleView.swift */, 42CA28AF2B101D6B0093B31A /* CardView.swift */, 42CA28B52B1022680093B31A /* HAButton.swift */, 42790C452C4808FA00E31B38 /* AppleLikeBottomSheet.swift */, + 4254C4CC2D103F7B00245021 /* ExternalLinkButton.swift */, ); path = Components; sourceTree = ""; @@ -4966,6 +4978,7 @@ 420F53E92C4E9D43003C8415 /* Widget */, D03D891920E0A85300D4F28D /* Shared.h */, 4235075C2CDB756800A19902 /* HAServices.swift */, + 4254C4C92D103ABB00245021 /* ExternalLink.swift */, ); path = Shared; sourceTree = ""; @@ -6908,6 +6921,7 @@ 4296C36D2B90DB640051B63C /* IntentActionAppEntity.swift in Sources */, 42F1DA5B2B4BF7DF002729BC /* WindowSizeObserver.swift in Sources */, 429106892BA9D5F700D452F9 /* AssistView+Build.swift in Sources */, + 429BEA1A2D102F3A00F070F9 /* ConnectionErrorDetailsView.swift in Sources */, 11EFCDD624F5FA8D00314D85 /* WebViewSceneDelegate.swift in Sources */, 42B94BDF2B9606CD00DEE060 /* AssistView.swift in Sources */, 1185DF94271FBA6100ED7D9A /* OnboardingAuthDetails.swift in Sources */, @@ -7265,6 +7279,7 @@ 491E990025D543560077BBE3 /* LogbookEntry.swift in Sources */, 11B38EF2275C54A300205C7B /* CallServiceIntentHandler.swift in Sources */, 42D3E4C02C5D321C00444BE6 /* NotificationIdentifier.swift in Sources */, + 4254C4CB2D103ABB00245021 /* ExternalLink.swift in Sources */, 42CE8FB12B46C3DA00C707F9 /* CoreStrings+Values.swift in Sources */, 4239D1832C4FFCCE003497FC /* WatchUserDefaults.swift in Sources */, B67CE8A722200F220034C1D0 /* HAAPI+RequestHelpers.swift in Sources */, @@ -7344,6 +7359,7 @@ files = ( 118F046924CB895A00CBBD5C /* UIColor+CSS3+Hex.swift in Sources */, 1109F81F24A1C011002590F2 /* SensorProvider.swift in Sources */, + 4254C4CA2D103ABB00245021 /* ExternalLink.swift in Sources */, 4297ADA82C89C74A00790812 /* GRDB+Initialization.swift in Sources */, 1141182A24AFA10900E6525C /* WebhookResponseHandler.swift in Sources */, 118BDA8825A6DBBA00731016 /* FrontmostAppSensor.swift in Sources */, @@ -7460,6 +7476,7 @@ 4214388C2CF5F1D700E2D44D /* ServerFixture.swift in Sources */, 11B38EE3275C54A200205C7B /* OpenPageIntentHandler.swift in Sources */, 113D29DE24946EDA0014067C /* CLLocationManager+OneShotLocation.swift in Sources */, + 429BEA1D2D10315F00F070F9 /* SheetCloseButton.swift in Sources */, 11ADF940267D34B10040A7E3 /* NotificationsCommandManager.swift in Sources */, 11C4627F24B04CB800031902 /* Promise+RetryNetworking.swift in Sources */, D03D893920E0AF8E00D4F28D /* ClientEvent.swift in Sources */, @@ -7527,6 +7544,7 @@ 4251AABC2C6CE224004CCC9D /* MagicItemProvider.swift in Sources */, D0EEF303214D8F0300D1D360 /* String+HA.swift in Sources */, 11B38EE6275C54A200205C7B /* CallServiceIntentHandler.swift in Sources */, + 4254C4CD2D103F7B00245021 /* ExternalLinkButton.swift in Sources */, 491E98FF25D543560077BBE3 /* LogbookEntry.swift in Sources */, 3997926A2B7F904A00231B54 /* MobileAppConfigPushCategory.swift in Sources */, 42CE8FA72B45D1E900C707F9 /* CoreStrings.swift in Sources */, diff --git a/Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/Contents.json b/Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/Contents.json new file mode 100644 index 000000000..3e3395226 --- /dev/null +++ b/Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "discord.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/discord.fill.svg b/Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/discord.fill.svg new file mode 100644 index 000000000..4819b0373 --- /dev/null +++ b/Sources/App/Resources/Assets.xcassets/CustomSymbols/discord.fill.symbolset/discord.fill.svg @@ -0,0 +1,200 @@ + + + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.4.0 + Requires Xcode 14 or greater + Generated from discord.fill + Typeset at 100 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/Contents.json b/Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/Contents.json new file mode 100644 index 000000000..092d1f2a1 --- /dev/null +++ b/Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "github.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/github.fill.svg b/Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/github.fill.svg new file mode 100644 index 000000000..9964bc129 --- /dev/null +++ b/Sources/App/Resources/Assets.xcassets/CustomSymbols/github.fill.symbolset/github.fill.svg @@ -0,0 +1,167 @@ + + + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.4.0 + Requires Xcode 14 or greater + Generated from github.fill + Typeset at 100 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/App/Resources/en.lproj/Localizable.strings b/Sources/App/Resources/en.lproj/Localizable.strings index 611cb4e12..9da8f05f8 100644 --- a/Sources/App/Resources/en.lproj/Localizable.strings +++ b/Sources/App/Resources/en.lproj/Localizable.strings @@ -1094,4 +1094,12 @@ Home Assistant is free and open source home automation software with a focus on "widgets.sensors.description" = "Display state of sensors"; "widgets.sensors.not_configured" = "No Sensors Configured"; "widgets.sensors.title" = "Sensors"; -"yes_label" = "Yes"; \ No newline at end of file +"connection.error.generic_title" = "Uh oh! Looks like we are unable to establish a connection."; +"connection.error.details.title" = "Connection error"; +"connection.error.details.label.description" = "Description"; +"connection.error.details.label.domain" = "Domain"; +"connection.error.details.label.code" = "Code"; +"connection.error.details.button.doc" = "Read documentation"; +"connection.error.details.button.discord" = "Ask in Discord"; +"connection.error.details.button.github" = "Report issue in Github"; +"yes_label" = "Yes"; diff --git a/Sources/App/WebView/ConnectionErrorDetailsView.swift b/Sources/App/WebView/ConnectionErrorDetailsView.swift new file mode 100644 index 000000000..51c6e8392 --- /dev/null +++ b/Sources/App/WebView/ConnectionErrorDetailsView.swift @@ -0,0 +1,76 @@ +import SFSafeSymbols +import Shared +import SwiftUI + +struct ConnectionErrorDetailsView: View { + @Environment(\.dismiss) private var dismiss + let error: Error + var body: some View { + ScrollView { + VStack(alignment: .leading) { + HStack { + SheetCloseButton { + dismiss() + } + .frame(maxWidth: .infinity, alignment: .trailing) + } + Text(L10n.Connection.Error.Details.title) + .font(.title.bold()) + VStack(alignment: .leading, spacing: Spaces.two) { + makeRow(title: L10n.Connection.Error.Details.Label.description, body: error.localizedDescription) + makeRow(title: L10n.Connection.Error.Details.Label.domain, body: (error as NSError).domain) + makeRow(title: L10n.Connection.Error.Details.Label.code, body: "\((error as NSError).code)") + } + .padding(.vertical) + documentationLink + discordLink + githubLink + } + .padding() + } + } + + private func makeRow(title: String, body: String) -> some View { + VStack(alignment: .leading) { + Text(title) + .font(.headline.bold()) + Text(body) + .textSelection(.enabled) + } + } + + private var documentationLink: some View { + ExternalLinkButton( + icon: Image(systemSymbol: .docTextFill), + title: L10n.Connection.Error.Details.Button.doc, + url: ExternalLink.companionAppDocs, + tint: .init(uiColor: Asset.Colors.haPrimary.color) + ) + } + + private var discordLink: some View { + ExternalLinkButton( + icon: Image("discord.fill"), + title: L10n.Connection.Error.Details.Button.discord, + url: ExternalLink.discord, + tint: .purple + ) + } + + private var githubLink: some View { + ExternalLinkButton( + icon: Image("github.fill"), + title: L10n.Connection.Error.Details.Button.github, + url: ExternalLink.githubReportIssue, + tint: .black + ) + } +} + +#Preview { + ConnectionErrorDetailsView(error: SomeError.some) +} + +enum SomeError: Error { + case some +} diff --git a/Sources/App/WebView/NoActiveURLView.swift b/Sources/App/WebView/NoActiveURLView.swift index babbdcf10..5d25c2463 100644 --- a/Sources/App/WebView/NoActiveURLView.swift +++ b/Sources/App/WebView/NoActiveURLView.swift @@ -81,23 +81,21 @@ struct NoActiveURLView: View { private var header: some View { HStack { - Group { - Button { - Current.Log.info("Tapped settings button in NoActiveURLView") - showSettings() - } label: { - Image(systemSymbol: .gear) - } - Spacer() - Button { - Current.Log.info("Dismissed NoActiveURLView") - dismiss() - } label: { - Image(systemSymbol: .xmark) - } + Button { + Current.Log.info("Tapped settings button in NoActiveURLView") + showSettings() + } label: { + Image(systemSymbol: .gear) } .font(.title2) .foregroundStyle(Color(uiColor: .secondaryLabel)) + Spacer() + Button { + Current.Log.info("Dismissed NoActiveURLView") + dismiss() + } label: { + Image(systemSymbol: .xmark) + } } } diff --git a/Sources/App/WebView/WebViewController.swift b/Sources/App/WebView/WebViewController.swift index 4d5823347..663a1d079 100644 --- a/Sources/App/WebView/WebViewController.swift +++ b/Sources/App/WebView/WebViewController.swift @@ -734,23 +734,26 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg func showSwiftMessage(error: Error, duration: SwiftMessages.Duration = .seconds(seconds: 15)) { Current.Log.error(error) - - let nsError = error as NSError - var config = swiftMessagesConfig() config.duration = duration let view = MessageView.viewFromNib(layout: .messageView) - view.configureTheme(.error) view.configureContent( - title: error.localizedDescription, - body: "\(nsError.domain) \(nsError.code)", + title: L10n.Connection.Error.genericTitle, + body: error.localizedDescription, iconImage: nil, iconText: nil, - buttonImage: nil, - buttonTitle: L10n.okLabel, - buttonTapHandler: { _ in + buttonImage: MaterialDesignIcons.helpCircleIcon.image( + ofSize: .init(width: 35, height: 35), + color: Asset.Colors.haPrimary.color + ), + buttonTitle: nil, + buttonTapHandler: { [weak self] _ in SwiftMessages.hide() + self?.presentOverlayController( + controller: UIHostingController(rootView: ConnectionErrorDetailsView(error: error)), + animated: true + ) } ) view.titleLabel?.numberOfLines = 0 diff --git a/Sources/Shared/DesignSystem/Components/ExternalLinkButton.swift b/Sources/Shared/DesignSystem/Components/ExternalLinkButton.swift new file mode 100644 index 000000000..3aecfd12e --- /dev/null +++ b/Sources/Shared/DesignSystem/Components/ExternalLinkButton.swift @@ -0,0 +1,43 @@ +import SwiftUI + +public struct ExternalLinkButton: View { + let icon: Image + let title: String + let url: URL + let tint: Color + + public init(icon: Image, title: String, url: URL, tint: Color) { + self.icon = icon + self.title = title + self.url = url + self.tint = tint + } + + public var body: some View { + Link(destination: url) { + HStack(spacing: Spaces.two) { + icon + .frame(width: 30, height: 30) + .font(.title2) + .tint(tint) + Text(title) + .frame(maxWidth: .infinity, alignment: .leading) + .tint(Color(uiColor: .label)) + .font(.body.bold()) + } + } + .frame(maxWidth: 600) + .padding() + .background(Color(.systemGray6)) + .clipShape(RoundedRectangle(cornerRadius: 12)) + } +} + +#Preview { + ExternalLinkButton( + icon: Image(systemName: "xmark"), + title: "Go there", + url: URL(string: "https://google.com")!, + tint: .blue + ) +} diff --git a/Sources/Shared/DesignSystem/Components/SheetCloseButton.swift b/Sources/Shared/DesignSystem/Components/SheetCloseButton.swift new file mode 100644 index 000000000..ce273b184 --- /dev/null +++ b/Sources/Shared/DesignSystem/Components/SheetCloseButton.swift @@ -0,0 +1,24 @@ +import SFSafeSymbols +import SwiftUI + +public struct SheetCloseButton: View { + let action: () -> Void + + public init(action: @escaping () -> Void) { + self.action = action + } + + public var body: some View { + Button { + action() + } label: { + Image(systemSymbol: .xmark) + } + .font(.title2) + .foregroundStyle(Color(uiColor: .secondaryLabel)) + } +} + +#Preview { + SheetCloseButton(action: {}) +} diff --git a/Sources/Shared/ExternalLink.swift b/Sources/Shared/ExternalLink.swift new file mode 100644 index 000000000..7ac0ff1f0 --- /dev/null +++ b/Sources/Shared/ExternalLink.swift @@ -0,0 +1,7 @@ +import Foundation + +public enum ExternalLink { + public static var companionAppDocs = URL(string: "https://companion.home-assistant.io")! + public static var discord = URL(string: "https://discord.com/channels/330944238910963714/1284965926336335993")! + public static var githubReportIssue = URL(string: "https://github.com/home-assistant/iOS/issues/new/choose")! +} diff --git a/Sources/Shared/Resources/Swiftgen/Strings.swift b/Sources/Shared/Resources/Swiftgen/Strings.swift index dac564f8b..81e008cea 100644 --- a/Sources/Shared/Resources/Swiftgen/Strings.swift +++ b/Sources/Shared/Resources/Swiftgen/Strings.swift @@ -755,6 +755,30 @@ public enum L10n { } public enum Connection { + public enum Error { + /// Uh oh! Looks like we are unable to establish a connection. + public static var genericTitle: String { return L10n.tr("Localizable", "connection.error.generic_title") } + public enum Details { + /// Connection error + public static var title: String { return L10n.tr("Localizable", "connection.error.details.title") } + public enum Button { + /// Ask in Discord + public static var discord: String { return L10n.tr("Localizable", "connection.error.details.button.discord") } + /// Read documentation + public static var doc: String { return L10n.tr("Localizable", "connection.error.details.button.doc") } + /// Report issue in Github + public static var github: String { return L10n.tr("Localizable", "connection.error.details.button.github") } + } + public enum Label { + /// Code + public static var code: String { return L10n.tr("Localizable", "connection.error.details.label.code") } + /// Description + public static var description: String { return L10n.tr("Localizable", "connection.error.details.label.description") } + /// Domain + public static var domain: String { return L10n.tr("Localizable", "connection.error.details.label.domain") } + } + } + } public enum Permission { public enum InternalUrl { /// To access Home Assistant locally in a secure way, you need to grant the location permission ('Always'). From 199c4f5bad8fd49a6c0367850b0652247465405d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:51:41 +0100 Subject: [PATCH 2/2] Update Localized Strings (#3273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatically created by zacwest. Co-authored-by: Home Assistant Bot Co-authored-by: Bruno Pantaleão Gonçalves <5808343+bgoncal@users.noreply.github.com> --- .../App/Resources/de.lproj/Localizable.strings | 12 ++++++------ .../App/Resources/el.lproj/Localizable.strings | 4 ++-- .../App/Resources/ru.lproj/Localizable.strings | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Sources/App/Resources/de.lproj/Localizable.strings b/Sources/App/Resources/de.lproj/Localizable.strings index 9096e2609..70299916d 100644 --- a/Sources/App/Resources/de.lproj/Localizable.strings +++ b/Sources/App/Resources/de.lproj/Localizable.strings @@ -499,7 +499,7 @@ Home Assistant ist eine kostenlose und Open-Source Hausautomatisierungssoftware "settings.connection_section.sensor_send_type.title" = "Sensorinformationen gesendet"; "settings.connection_section.servers" = "Server"; "settings.connection_section.ssid_permission_and_accuracy_message" = "Für den Zugriff auf SSIDs im Hintergrund sind die Standortberechtigung „Immer“ und die Standortgenauigkeit „Vollständig“ erforderlich. Tippe hier, um deine Einstellungen zu ändern."; -"settings.connection_section.ssid_permission_message" = "Zugriff auf SSIDs im Hintergrund erfordern die Standortberechtigung „Immer“. Tippe hier, um die Einstellung zu ändern."; +"settings.connection_section.ssid_permission_message" = "Zugriff auf SSIDs im Hintergrund erfordert die Standortberechtigung „Immer“. Tippe hier, um die Einstellung zu ändern."; "settings.connection_section.validate_error.edit_url" = "URL bearbeiten"; "settings.connection_section.validate_error.title" = "Fehler beim Speichern der URL"; "settings.connection_section.validate_error.use_anyway" = "Trotzdem verwenden"; @@ -518,8 +518,8 @@ Home Assistant ist eine kostenlose und Open-Source Hausautomatisierungssoftware "settings.details_section.watch_row_complications.title" = "Komplikationen"; "settings.details_section.watch_row_configuration.title" = "Konfiguration"; "settings.developer.annoying_background_notifications.title" = "Lästige Hintergrundinformation"; -"settings.developer.camera_notification.notification.body" = "Beispiel: Benachrichtigung mit Kamera"; -"settings.developer.camera_notification.title" = "Zeige die Kamerabenachrichtigung"; +"settings.developer.camera_notification.notification.body" = "Aufklappen, um die Kamerainhalt-Erweiterung einzublenden"; +"settings.developer.camera_notification.title" = "Kamerainhalt in Benachrichtigung einblenden"; "settings.developer.copy_realm.alert.message" = "Bereich von %@ nach %@ kopiert"; "settings.developer.copy_realm.alert.title" = "Kopierter Bereich"; "settings.developer.copy_realm.title" = "Kopiere AppGroup/store.realm nach Documents/default.realm"; @@ -533,8 +533,8 @@ Home Assistant ist eine kostenlose und Open-Source Hausautomatisierungssoftware "settings.developer.export_log_files.title" = "Log-Dateien exportieren"; "settings.developer.footer" = "Verwende diese nicht, wenn du nicht weißt, was du tust!"; "settings.developer.header" = "Entwickler"; -"settings.developer.map_notification.notification.body" = "Beispiel: Benachrichtigung mit Karte"; -"settings.developer.map_notification.title" = "Zeige die Kartenbenachrichtigung"; +"settings.developer.map_notification.notification.body" = "Aufklappen, um die Karteninhalt-Erweiterung einzublenden"; +"settings.developer.map_notification.title" = "Karteninhalt in Benachrichtigung einblenden"; "settings.developer.mock_thread_credentials_sharing.title" = "Teilen von Simulator-Thread-Zugangsdaten"; "settings.developer.show_log_files.title" = "Log-Dateien in Finder anzeigen"; "settings.developer.sync_watch_context.title" = "Watch-Kontext synchronisieren"; @@ -713,7 +713,7 @@ Home Assistant ist eine kostenlose und Open-Source Hausautomatisierungssoftware "settings_details.notifications.sounds.footer" = "Integrierte, System-, oder benutzerdefinierte Sounds können in deinen Mitteilungen verwendet werden."; "settings_details.notifications.sounds.import_custom" = "Benutzerdefinierte Töne importieren"; "settings_details.notifications.sounds.import_file_sharing" = "Importiere Töne aus der iTunes-Dateifreigabe"; -"settings_details.notifications.sounds.import_mac_instructions" = "Füge benutzerdefinierte Sounds zu deinem Sound-Ordner hinzu, um diese in Benachrichtigungen zu verwenden. Nutze deren Dateiname als den Sound-Wert im Dienst-Aufruf."; +"settings_details.notifications.sounds.import_mac_instructions" = "Füge benutzerdefinierte Töne zu deinem Sounds-Ordner hinzu, um diese in Benachrichtigungen zu verwenden. Nutze deren Dateiname als den Sound-Wert im Dienst-Aufruf."; "settings_details.notifications.sounds.import_mac_open_folder" = "Ordner im Finder öffnen"; "settings_details.notifications.sounds.import_system" = "Systemtöne importieren"; "settings_details.notifications.sounds.imported" = "Importiert"; diff --git a/Sources/App/Resources/el.lproj/Localizable.strings b/Sources/App/Resources/el.lproj/Localizable.strings index feefb96e7..2174ce50c 100644 --- a/Sources/App/Resources/el.lproj/Localizable.strings +++ b/Sources/App/Resources/el.lproj/Localizable.strings @@ -100,7 +100,7 @@ "app_intents.state.toggle" = "Διακόπτης"; "app_intents.widget_action.actions_parameter_configuration" = "Ποιες ενέργειες;"; "assist.button.listening.title" = "Ακρόαση..."; -"assist.error.pipelines_response" = "Απέτυχε η λήψη των Assist pipelines, παρακαλώ ελέγξτε τη διαμόρφωση των pipelines."; +"assist.error.pipelines_response" = "Αποτυχία λήψης των Assist pipelines, παρακαλώ ελέγξτε τη διαμόρφωση των pipelines."; "assist.pipelines_picker.title" = "Assist Pipelines"; "assist.watch.mic_button.title" = "Tap to "; "assist.watch.not_reachable.title" = "Το Assist απαιτεί συνδεσιμότητα iPhone. Το iPhone σας δεν είναι προσβάσιμο αυτή τη στιγμή."; @@ -239,7 +239,7 @@ "ha_api.api_error.cant_build_url" = "Δεν είναι δυνατή η δημιουργία URL API"; "ha_api.api_error.invalid_response" = "Λάβαμε μη έγκυρη απάντηση από το Home Assistant"; "ha_api.api_error.manager_not_available" = "Το HA API Manager δεν είναι διαθέσιμο"; -"ha_api.api_error.mobile_app_component_not_loaded" = "Το στοιχείο mobile_app δεν έχει φορτωθεί. Προσθέστε το στη διαμόρφωσή σας, κάντε επανεκκίνηση του Home Assistant και δοκιμάστε ξανά."; +"ha_api.api_error.mobile_app_component_not_loaded" = "Το στοιχείο mobile_app δεν έχει φορτωθεί. Παρακαλώ προσθέστε το στη διαμόρφωσή σας, κάντε επανεκκίνηση του Home Assistant και δοκιμάστε ξανά."; "ha_api.api_error.must_upgrade_home_assistant" = "Η έκδοση του Home Assistant ( %@ ) είναι πολύ παλιά, πρέπει να κάνετε αναβάθμιση σε τουλάχιστον έκδοση %@ για να χρησιμοποιήσετε την εφαρμογή."; "ha_api.api_error.no_available_api" = "Δεν υπάρχει διαθέσιμο API, ελέγξτε διπλά αν είναι διαθέσιμο το εσωτερικό URL ή το εξωτερικό URL."; "ha_api.api_error.not_configured" = "Το HA API δεν έχει διαμορφωθεί"; diff --git a/Sources/App/Resources/ru.lproj/Localizable.strings b/Sources/App/Resources/ru.lproj/Localizable.strings index 11819ffe3..1ec244c68 100644 --- a/Sources/App/Resources/ru.lproj/Localizable.strings +++ b/Sources/App/Resources/ru.lproj/Localizable.strings @@ -182,14 +182,14 @@ "close_label" = "Закрыть"; "component.collapsible_view.collapse" = "Свернуть"; "component.collapsible_view.expand" = "Развернуть"; -"connection.permission.internal_url.body1" = "To access Home Assistant locally in a secure way, you need to grant the location permission ('Always')."; -"connection.permission.internal_url.body2" = "This permission allows Home Assistant to detect the wireless network that you're connected to and establish a local connection."; -"connection.permission.internal_url.body3" = "You are always in control if your location is shared with Home Assistant. You can change these settings in the companion app setting screen."; -"connection.permission.internal_url.button_configure" = "Configure local access"; -"connection.permission.internal_url.button_ignore" = "I know what I am doing. Allow local connections without permission access."; -"connection.permission.internal_url.footer" = "If you still want to use the local URL and don't want to provide location permission, you can tap the button below, but please, be aware of the security risks."; -"connection.permission.internal_url.ignore.alert.title" = "Are you sure?"; -"connection.permission.internal_url.title" = "Permission access"; +"connection.permission.internal_url.body1" = "Чтобы безопасно получить доступ к Home Assistant локально, вам нужно предоставить разрешение на доступ к местоположению ('Всегда')."; +"connection.permission.internal_url.body2" = "Это разрешение позволяет Home Assistant обнаруживать беспроводную сеть, к которой вы подключены, и устанавливать локальное соединение."; +"connection.permission.internal_url.body3" = "Вы всегда контролируете, будет ли ваше местоположение передано Home Assistant. Эти настройки можно изменить на экране настроек приложения-компаньона."; +"connection.permission.internal_url.button_configure" = "Настроить локальный доступ"; +"connection.permission.internal_url.button_ignore" = "Я знаю, что делаю. Разрешить локальные подключения без разрешения доступа."; +"connection.permission.internal_url.footer" = "Если вы по-прежнему хотите использовать локальный URL-адрес и не хотите предоставлять разрешение на определение местоположения, вы можете нажать кнопку ниже, но, пожалуйста, помните о рисках безопасности."; +"connection.permission.internal_url.ignore.alert.title" = "Вы уверены?"; +"connection.permission.internal_url.title" = "Доступ к разрешениям"; "continue_label" = "Продолжить"; "copy_label" = "Копировать"; "database.problem.delete" = "Удалить базу данных и выйти из приложения"; @@ -757,7 +757,7 @@ Home Assistant - это бесплатное программное обеспе "settings_sensors.title" = "Датчики"; "share_extension.entered_placeholder" = "'entered' in event"; "share_extension.error.title" = "Не удалось отправить"; -"shortcut_item.open_settings.title" = "Open Settings"; +"shortcut_item.open_settings.title" = "Открыть настройки"; "success_label" = "Успешно"; "thread.active_operational_data_set.title" = "Active operational data set"; "thread.border_agent_id.title" = "Border Agent ID";