From 666a9cf626ba8ddc3b48c519d8d1639d7beffae0 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: Thu, 26 Sep 2024 09:24:53 +0200 Subject: [PATCH] Update onboarding with modern HA design (#3052) ## Summary ## Screenshots ![CleanShot 2024-09-25 at 18 05 52@2x](https://github.com/user-attachments/assets/631a59c2-0db1-4a0f-9ac8-a62139d78865) ## Link to pull request in Documentation repository Documentation: home-assistant/companion.home-assistant# ## Any other notes --- HomeAssistant.xcodeproj/project.pbxproj | 24 ++-- Podfile | 1 - Podfile.lock | 6 +- .../App/Onboarding/API/OnboardingAuth.swift | 3 +- .../OnboardingNavigationViewController.swift | 26 ++-- .../App/Onboarding/Screens/ActivityView.swift | 21 +++ .../Screens/OnboardingErrorView.swift | 104 +++++++++++++++ .../OnboardingErrorViewController.swift | 122 ------------------ .../OnboardingManualURLViewController.swift | 4 +- .../OnboardingPermissionViewController.swift | 10 +- .../OnboardingScanningViewController.swift | 26 ++-- .../Screens/OnboardingWelcomeView.swift | 69 ++++++++++ .../OnboardingWelcomeViewController.swift | 98 -------------- .../Onboarding/Screens/SafariWebView.swift | 12 ++ .../Resources/en.lproj/Localizable.strings | 3 +- .../Settings/Eureka/SettingsButtonRow.swift | 2 +- .../Common/Extensions/XCGLogger+Export.swift | 59 +++++---- Sources/Shared/Environment/Style.swift | 29 +++-- .../Shared/Resources/Swiftgen/Strings.swift | 2 + 19 files changed, 316 insertions(+), 305 deletions(-) create mode 100644 Sources/App/Onboarding/Screens/ActivityView.swift create mode 100644 Sources/App/Onboarding/Screens/OnboardingErrorView.swift delete mode 100644 Sources/App/Onboarding/Screens/OnboardingErrorViewController.swift create mode 100644 Sources/App/Onboarding/Screens/OnboardingWelcomeView.swift delete mode 100644 Sources/App/Onboarding/Screens/OnboardingWelcomeViewController.swift create mode 100644 Sources/App/Onboarding/Screens/SafariWebView.swift diff --git a/HomeAssistant.xcodeproj/project.pbxproj b/HomeAssistant.xcodeproj/project.pbxproj index e3af95366..49d4372fc 100644 --- a/HomeAssistant.xcodeproj/project.pbxproj +++ b/HomeAssistant.xcodeproj/project.pbxproj @@ -608,6 +608,7 @@ 426CBB6C2C9C550D003CA3AC /* IntentSwitchEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 426CBB6B2C9C550D003CA3AC /* IntentSwitchEntity.swift */; }; 426D9C742C9C60B000F278AF /* ControlEntityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 426D9C722C9C582F00F278AF /* ControlEntityProvider.swift */; }; 426D9C752C9C60B000F278AF /* ControlEntityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 426D9C722C9C582F00F278AF /* ControlEntityProvider.swift */; }; + 426EE49B2CA4194E00A5EF4F /* OnboardingWelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 426EE49A2CA4194E00A5EF4F /* OnboardingWelcomeView.swift */; }; 4273C4872C8857B00065A5B4 /* ControlOpenPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4273C4862C8857B00065A5B4 /* ControlOpenPage.swift */; }; 4273C4882C8857B00065A5B4 /* ControlOpenPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4273C4862C8857B00065A5B4 /* ControlOpenPage.swift */; }; 4273C48A2C8858470065A5B4 /* ControlOpenPageValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4273C4892C8858470065A5B4 /* ControlOpenPageValueProvider.swift */; }; @@ -725,6 +726,9 @@ 42DEDA9A2C5B926400E9D29D /* AppVersionSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42DEDA992C5B926400E9D29D /* AppVersionSensor.swift */; }; 42DEDA9B2C5B926400E9D29D /* AppVersionSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42DEDA992C5B926400E9D29D /* AppVersionSensor.swift */; }; 42E65F082C8079FE00C4A6F2 /* ControlAssistValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E65F072C8079FE00C4A6F2 /* ControlAssistValueProvider.swift */; }; + 42E95C552CA44FC90010ECE3 /* SafariWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E95C542CA44FC90010ECE3 /* SafariWebView.swift */; }; + 42E95C572CA45EFA0010ECE3 /* OnboardingErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E95C562CA45EFA0010ECE3 /* OnboardingErrorView.swift */; }; + 42E95C592CA46AD50010ECE3 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E95C582CA46AD50010ECE3 /* ActivityView.swift */; }; 42EB03062C6E42F900A184A6 /* WatchHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42EB03052C6E42F900A184A6 /* WatchHomeView.swift */; }; 42EB03082C6E430300A184A6 /* WatchHomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42EB03072C6E430300A184A6 /* WatchHomeViewModel.swift */; }; 42EB030A2C6E4D0E00A184A6 /* WatchMagicViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42EB03092C6E4D0E00A184A6 /* WatchMagicViewRow.swift */; }; @@ -945,7 +949,6 @@ B661FB68226B961400E541DD /* WebSocketBridge.js in Resources */ = {isa = PBXBuildFile; fileRef = B661FB67226B961400E541DD /* WebSocketBridge.js */; }; B661FB6A226BBDA900E541DD /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B661FB69226BBDA900E541DD /* SettingsViewController.swift */; }; B661FB6F226BCCAD00E541DD /* ConnectionSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B661FB6E226BCCAD00E541DD /* ConnectionSettingsViewController.swift */; }; - B661FB74226C110A00E541DD /* OnboardingWelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B661FB73226C110A00E541DD /* OnboardingWelcomeViewController.swift */; }; B661FB7A226C197900E541DD /* OnboardingManualURLViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B661FB79226C197900E541DD /* OnboardingManualURLViewController.swift */; }; B661FC7E226C87BB00E541DD /* home.json in Resources */ = {isa = PBXBuildFile; fileRef = B661FC7D226C87BB00E541DD /* home.json */; }; B661FC88226D478300E541DD /* OnboardingScanningViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B661FC87226D478300E541DD /* OnboardingScanningViewController.swift */; }; @@ -1031,7 +1034,6 @@ B6D3B4ED225B26900082BB4F /* SensorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6D3B4EB225B26300082BB4F /* SensorContainer.swift */; }; B6D3B4EE225B26910082BB4F /* SensorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6D3B4EB225B26300082BB4F /* SensorContainer.swift */; }; B6D8A3282271448E00FA765D /* error.json in Resources */ = {isa = PBXBuildFile; fileRef = B6D8A3272271448D00FA765D /* error.json */; }; - B6D8A32A2271455300FA765D /* OnboardingErrorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6D8A3292271455300FA765D /* OnboardingErrorViewController.swift */; }; B6DA3C7122690B1F00DE811C /* NotificationSoundsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DA3C7022690B1F00DE811C /* NotificationSoundsViewController.swift */; }; B6DA3C7322691A5000DE811C /* AKConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DA3C7222691A5000DE811C /* AKConverter.swift */; }; B6DD5E6A24940F6F003A0154 /* OpenInFirefoxControllerSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DD5E6924940F6F003A0154 /* OpenInFirefoxControllerSwift.swift */; }; @@ -1854,6 +1856,7 @@ 426CBB692C9C543F003CA3AC /* ControlSwitchValueProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlSwitchValueProvider.swift; sourceTree = ""; }; 426CBB6B2C9C550D003CA3AC /* IntentSwitchEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentSwitchEntity.swift; sourceTree = ""; }; 426D9C722C9C582F00F278AF /* ControlEntityProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlEntityProvider.swift; sourceTree = ""; }; + 426EE49A2CA4194E00A5EF4F /* OnboardingWelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWelcomeView.swift; sourceTree = ""; }; 4273C4862C8857B00065A5B4 /* ControlOpenPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlOpenPage.swift; sourceTree = ""; }; 4273C4892C8858470065A5B4 /* ControlOpenPageValueProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlOpenPageValueProvider.swift; sourceTree = ""; }; 4273C48C2C8859530065A5B4 /* PageAppEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageAppEntity.swift; sourceTree = ""; }; @@ -1982,6 +1985,9 @@ 42DD84392B15DC3F00936F16 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = ""; }; 42DEDA992C5B926400E9D29D /* AppVersionSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionSensor.swift; sourceTree = ""; }; 42E65F072C8079FE00C4A6F2 /* ControlAssistValueProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlAssistValueProvider.swift; sourceTree = ""; }; + 42E95C542CA44FC90010ECE3 /* SafariWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariWebView.swift; sourceTree = ""; }; + 42E95C562CA45EFA0010ECE3 /* OnboardingErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingErrorView.swift; sourceTree = ""; }; + 42E95C582CA46AD50010ECE3 /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = ""; }; 42EB03052C6E42F900A184A6 /* WatchHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchHomeView.swift; sourceTree = ""; }; 42EB03072C6E430300A184A6 /* WatchHomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchHomeViewModel.swift; sourceTree = ""; }; 42EB03092C6E4D0E00A184A6 /* WatchMagicViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchMagicViewRow.swift; sourceTree = ""; }; @@ -2291,7 +2297,6 @@ B661FB67226B961400E541DD /* WebSocketBridge.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = WebSocketBridge.js; sourceTree = ""; }; B661FB69226BBDA900E541DD /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; B661FB6E226BCCAD00E541DD /* ConnectionSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionSettingsViewController.swift; sourceTree = ""; }; - B661FB73226C110A00E541DD /* OnboardingWelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWelcomeViewController.swift; sourceTree = ""; }; B661FB79226C197900E541DD /* OnboardingManualURLViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingManualURLViewController.swift; sourceTree = ""; }; B661FC7D226C87BB00E541DD /* home.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = home.json; sourceTree = ""; }; B661FC87226D478300E541DD /* OnboardingScanningViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScanningViewController.swift; sourceTree = ""; }; @@ -2355,7 +2360,6 @@ B6D0D2810993156972329EA6 /* Pods-iOS-Extensions-Matter.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOS-Extensions-Matter.release.xcconfig"; path = "Pods/Target Support Files/Pods-iOS-Extensions-Matter/Pods-iOS-Extensions-Matter.release.xcconfig"; sourceTree = ""; }; B6D3B4EB225B26300082BB4F /* SensorContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorContainer.swift; sourceTree = ""; }; B6D8A3272271448D00FA765D /* error.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = error.json; sourceTree = ""; }; - B6D8A3292271455300FA765D /* OnboardingErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingErrorViewController.swift; sourceTree = ""; }; B6DA3C7022690B1F00DE811C /* NotificationSoundsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSoundsViewController.swift; sourceTree = ""; }; B6DA3C7222691A5000DE811C /* AKConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AKConverter.swift; sourceTree = ""; }; B6DAC734215F069300727D2A /* NotificationCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCategory.swift; sourceTree = ""; }; @@ -2796,12 +2800,14 @@ 1168BF35271811C200DD4D15 /* Screens */ = { isa = PBXGroup; children = ( - B6D8A3292271455300FA765D /* OnboardingErrorViewController.swift */, B661FC87226D478300E541DD /* OnboardingScanningViewController.swift */, 11DA6B4E2713912F008ADFAF /* OnboardingPermissionViewController.swift */, B661FB79226C197900E541DD /* OnboardingManualURLViewController.swift */, 11E99A4F27156854003C8A65 /* OnboardingTerminalViewController.swift */, - B661FB73226C110A00E541DD /* OnboardingWelcomeViewController.swift */, + 42E95C562CA45EFA0010ECE3 /* OnboardingErrorView.swift */, + 426EE49A2CA4194E00A5EF4F /* OnboardingWelcomeView.swift */, + 42E95C582CA46AD50010ECE3 /* ActivityView.swift */, + 42E95C542CA44FC90010ECE3 /* SafariWebView.swift */, ); path = Screens; sourceTree = ""; @@ -6513,6 +6519,7 @@ 11195F6B267EFB1F003DF674 /* NotificationManagerLocalPushInterface.swift in Sources */, B6022213226DAC9D00E8DBFE /* ScaledFont.swift in Sources */, 1112AE9B25F71775007A541A /* LocationHistoryListViewController.swift in Sources */, + 426EE49B2CA4194E00A5EF4F /* OnboardingWelcomeView.swift in Sources */, B68EDD03215F0E2900DD6B28 /* NotificationCategoryConfigurator.swift in Sources */, D0FF79D220D87D200034574D /* ClientEventTableViewController.swift in Sources */, 42D5ACCE2C636F2B00D9C4E2 /* WatchConfigurationViewModel.swift in Sources */, @@ -6543,6 +6550,7 @@ 42F1DA6D2B4ED29C002729BC /* CarPlayPaginatedListTemplate.swift in Sources */, 11DA6B4B27137A60008ADFAF /* InputAccessoryView.swift in Sources */, 42FCD0142B9B29740057783F /* ThreadCredentialDetailsView.swift in Sources */, + 42E95C552CA44FC90010ECE3 /* SafariWebView.swift in Sources */, 11A71C6D24A4641600D9565F /* ZoneManagerEvent.swift in Sources */, 42FCD0122B9B29740057783F /* ThreadCredentialsManagementView+Build.swift in Sources */, 11F3D7512495434C00C05BBA /* SensorDetailViewController.swift in Sources */, @@ -6623,7 +6631,6 @@ 42F1DA5F2B4D4B32002729BC /* CarPlayServerListTemplate.swift in Sources */, 420FE8492B556A0200878E06 /* CarPlayActionsViewModel.swift in Sources */, 4273C48E2C8859530065A5B4 /* PageAppEntity.swift in Sources */, - B6D8A32A2271455300FA765D /* OnboardingErrorViewController.swift in Sources */, B648AE252275918F006972AF /* Segues.swift in Sources */, 11ADB13E24C29E6900FF5EB2 /* ZoneManagerRegionFilter.swift in Sources */, 11DE822E24FAC51100E636B8 /* IncomingURLHandler.swift in Sources */, @@ -6637,7 +6644,9 @@ 11A48D8124CA8ADB0021BDD9 /* NotificationCategory+Observation.swift in Sources */, 1100D51D2496AECE00B1073C /* PermissionStatusRow.swift in Sources */, D0B25BD221323CA600678C2C /* ClientEventPayloadViewController.swift in Sources */, + 42E95C572CA45EFA0010ECE3 /* OnboardingErrorView.swift in Sources */, B641BC1F1E2097EF002CCBC1 /* AboutViewController.swift in Sources */, + 42E95C592CA46AD50010ECE3 /* ActivityView.swift in Sources */, B675ECC3221BB0E600C65D31 /* SearchPushRow.swift in Sources */, 11C05F2D254919210031D038 /* AccountInitialsImage.swift in Sources */, B605C891226E9DAC00EF46DD /* Permissions.swift in Sources */, @@ -6661,7 +6670,6 @@ 42FCCFE32B9B1B610057783F /* BarcodeScannerCameraView.swift in Sources */, 11B62DBE24F2EDD800E5CB55 /* EurekaCondition+Additions.swift in Sources */, 4289DDAA2C85AB4C003591C2 /* AssistAppIntent.swift in Sources */, - B661FB74226C110A00E541DD /* OnboardingWelcomeViewController.swift in Sources */, 1164DA2125FBEE8600515E8A /* TemplateEditViewController.swift in Sources */, 4273C4882C8857B00065A5B4 /* ControlOpenPage.swift in Sources */, 11A71C8B24A5848B00D9565F /* ZoneManagerProcessor.swift in Sources */, diff --git a/Podfile b/Podfile index 35f545cf5..87901c947 100644 --- a/Podfile +++ b/Podfile @@ -77,7 +77,6 @@ abstract_target 'iOS' do pod 'FirebaseMessaging' - pod 'lottie-ios' pod 'SwiftMessages' pod 'ViewRow', git: 'https://github.com/EurekaCommunity/ViewRow', branch: 'master' diff --git a/Podfile.lock b/Podfile.lock index 9b42c2c92..4e4a662be 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -65,7 +65,6 @@ PODS: - PromiseKit (~> 6.13) - Improv-iOS (0.0.6) - KeychainAccess (4.2.2) - - lottie-ios (4.1.2) - MBProgressHUD (1.2.0) - nanopb (2.30909.0): - nanopb/decode (= 2.30909.0) @@ -139,7 +138,6 @@ DEPENDENCIES: - HAKit/PromiseKit (from `https://github.com/home-assistant/HAKit.git`, tag `0.4.2`) - Improv-iOS (~> 0.0.6) - KeychainAccess - - lottie-ios - MBProgressHUD (~> 1.2.0) - ObjectMapper (from `https://github.com/tristanhimmelman/ObjectMapper.git`, branch `master`) - OHHTTPStubs/Swift @@ -171,7 +169,6 @@ SPEC REPOS: - GoogleUtilities - Improv-iOS - KeychainAccess - - lottie-ios - MBProgressHUD - nanopb - ObjcExceptionBridging @@ -279,7 +276,6 @@ SPEC CHECKSUMS: HAKit: 92e9bd30717270df15025184b608b7902a773286 Improv-iOS: 8973990c1b1f3e3aed7fc600c8efce95359cadd0 KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 - lottie-ios: b3846a9fa8ca1a1a3bebfa256ebe8b6fd4aaaaad MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406 nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 ObjcExceptionBridging: c30e00eb3700467e695faeea30e26e18bd445001 @@ -303,6 +299,6 @@ SPEC CHECKSUMS: XCGLogger: 1943831ef907df55108b0b18657953f868de973b ZIPFoundation: d170fa8e270b2a32bef9dcdcabff5b8f1a5deced -PODFILE CHECKSUM: e74a19f202198268745428ea4069d3a9738407d8 +PODFILE CHECKSUM: 7db8d8bc3dbacceb0cd00e82d8ee470965549665 COCOAPODS: 1.15.2 diff --git a/Sources/App/Onboarding/API/OnboardingAuth.swift b/Sources/App/Onboarding/API/OnboardingAuth.swift index b1f214c12..d35679d9b 100644 --- a/Sources/App/Onboarding/API/OnboardingAuth.swift +++ b/Sources/App/Onboarding/API/OnboardingAuth.swift @@ -3,6 +3,7 @@ import Foundation import HAKit import PromiseKit import Shared +import SwiftUI class OnboardingAuth { func successController(server: Server?) -> UIViewController { @@ -10,7 +11,7 @@ class OnboardingAuth { } func failureController(error: Error) -> UIViewController { - OnboardingErrorViewController(error: error) + UIHostingController(rootView: OnboardingErrorView(error: error)) } var login: OnboardingAuthLogin = OnboardingAuthLoginImpl() diff --git a/Sources/App/Onboarding/Container/OnboardingNavigationViewController.swift b/Sources/App/Onboarding/Container/OnboardingNavigationViewController.swift index a533c1e90..140165284 100644 --- a/Sources/App/Onboarding/Container/OnboardingNavigationViewController.swift +++ b/Sources/App/Onboarding/Container/OnboardingNavigationViewController.swift @@ -1,5 +1,6 @@ import Eureka import Shared +import SwiftUI import UIKit enum OnboardingBarAppearance { @@ -56,13 +57,15 @@ class OnboardingNavigationViewController: UINavigationController, RowControllerT let rootViewController: UIViewController + let onboardingWelcomeViewController = UIHostingController(rootView: OnboardingWelcomeView()) + switch onboardingStyle { - case .initial: rootViewController = OnboardingWelcomeViewController() + case .initial: rootViewController = onboardingWelcomeViewController case .secondary: rootViewController = OnboardingScanningViewController() case let .required(type): switch type { case .full: - rootViewController = OnboardingWelcomeViewController() + rootViewController = onboardingWelcomeViewController case .permissions: rootViewController = OnboardingPermissionViewControllerFactory.next(server: nil) } @@ -89,31 +92,22 @@ class OnboardingNavigationViewController: UINavigationController, RowControllerT fatalError("init(coder:) has not been implemented") } - override var childForStatusBarStyle: UIViewController? { - nil - } - - override var preferredStatusBarStyle: UIStatusBarStyle { - .lightContent - } - override func viewDidLoad() { super.viewDidLoad() delegate = self - view.tintColor = Current.style.onboardingTintColor - - overrideUserInterfaceStyle = .dark + view.tintColor = Asset.Colors.haPrimary.color + navigationController?.navigationBar.tintColor = Asset.Colors.haPrimary.color let appearance = with(UINavigationBarAppearance()) { $0.configureWithOpaqueBackground() - $0.backgroundColor = Current.style.onboardingBackground + $0.backgroundColor = .systemBackground $0.shadowColor = .clear - $0.titleTextAttributes = [.foregroundColor: UIColor.white] + $0.titleTextAttributes = [.foregroundColor: UIColor.label] } navigationBar.standardAppearance = appearance navigationBar.scrollEdgeAppearance = appearance - navigationBar.tintColor = .white + navigationBar.tintColor = .label } @objc private func cancelTapped(_ sender: UIBarButtonItem) { diff --git a/Sources/App/Onboarding/Screens/ActivityView.swift b/Sources/App/Onboarding/Screens/ActivityView.swift new file mode 100644 index 000000000..2352ca96b --- /dev/null +++ b/Sources/App/Onboarding/Screens/ActivityView.swift @@ -0,0 +1,21 @@ +import SwiftUI +import UIKit + +struct ActivityView: UIViewControllerRepresentable { + var activityItems: [Any] + var applicationActivities: [UIActivity]? = nil + + func makeUIViewController(context: UIViewControllerRepresentableContext) + -> UIActivityViewController { + let controller = UIActivityViewController( + activityItems: activityItems, + applicationActivities: applicationActivities + ) + return controller + } + + func updateUIViewController( + _ uiViewController: UIActivityViewController, + context: UIViewControllerRepresentableContext + ) {} +} diff --git a/Sources/App/Onboarding/Screens/OnboardingErrorView.swift b/Sources/App/Onboarding/Screens/OnboardingErrorView.swift new file mode 100644 index 000000000..29d9f4569 --- /dev/null +++ b/Sources/App/Onboarding/Screens/OnboardingErrorView.swift @@ -0,0 +1,104 @@ +import HAKit +import SFSafeSymbols +import Shared +import SwiftUI + +struct OnboardingErrorView: View { + @Environment(\.openURL) private var openURL + @State private var viewAppeared: Bool = false + @State private var showShareSheet: Bool = false + + let error: Error + + var body: some View { + VStack(spacing: Spaces.two) { + Text(L10n.Onboarding.ConnectionError.title) + .font(.title.bold()) + Image(systemSymbol: .xmarkCircleFill) + .font(.system(size: 60)) + .foregroundStyle(.white, .red) + ScrollView { + errorContent + } + VStack { + exportLogsView + Button(L10n.Onboarding.ConnectionError.moreInfoButton) { + openURL(documentationURL(for: error)) + } + } + } + .onAppear { + viewAppeared = true + } + .sheet(isPresented: $showShareSheet, content: { + ActivityView(activityItems: [Current.Log.archiveURL()]) + }) + } + + private var errorContent: some View { + var errorComponents: [NSAttributedString] = [ + NSAttributedString(string: error.localizedDescription), + ] + + if let error = error as? OnboardingAuthError { + if let code = error.errorCode { + errorComponents.append(errorCode(code)) + } + + if let source = error.responseString { + let font: UIFont + + font = .monospacedSystemFont(ofSize: 14.0, weight: .regular) + + errorComponents.append(NSAttributedString( + string: source, + attributes: [.font: font] + )) + } + } else { + let nsError = error as NSError + errorComponents.append(errorCode(String(format: "%@ %d", nsError.domain, nsError.code))) + } + + return VStack { + ForEach(errorComponents, id: \.self) { error in + Text(AttributedString(error)) + .textSelection(.enabled) + } + } + .padding() + } + + private func errorCode(_ value: String) -> NSAttributedString { + NSAttributedString(string: L10n.Onboarding.ConnectionTestResult.errorCode + "\n" + value) + } + + @ViewBuilder + private var exportLogsView: some View { + if #available(iOS 16.0, *) { + if let archiveURL = Current.Log.archiveURL() { + ShareLink(item: archiveURL, label: { + Text(Current.Log.exportTitle) + }) + } + } else { + Button(Current.Log.exportTitle) { + showShareSheet = true + } + } + } + + private func documentationURL(for error: Error) -> URL { + var string = "https://companion.home-assistant.io/docs/troubleshooting/errors" + + if let error = error as? OnboardingAuthError { + string += "#\(error.kind.documentationAnchor)" + } + + return URL(string: string)! + } +} + +#Preview { + OnboardingErrorView(error: HAError.internal(debugDescription: "")) +} diff --git a/Sources/App/Onboarding/Screens/OnboardingErrorViewController.swift b/Sources/App/Onboarding/Screens/OnboardingErrorViewController.swift deleted file mode 100644 index 364bccf69..000000000 --- a/Sources/App/Onboarding/Screens/OnboardingErrorViewController.swift +++ /dev/null @@ -1,122 +0,0 @@ -import Lottie -import Shared -import UIKit - -class OnboardingErrorViewController: UIViewController { - private var animationView: LottieAnimationView? - private var moreInfoButton: UIButton? - private var goBackButton: UIButton? - - let error: Error - init(error: Error) { - self.error = error - super.init(nibName: nil, bundle: nil) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = Current.style.onboardingBackground - - let (_, stackView, equalSpacers) = UIView.contentStackView(in: view, scrolling: true) - - stackView.addArrangedSubview(with(UILabel()) { - $0.text = L10n.Onboarding.ConnectionError.title - Current.style.onboardingTitle($0) - }) - - stackView.addArrangedSubview(with(LottieAnimationView()) { - $0.animation = LottieAnimation.named("error") - $0.loopMode = .playOnce - $0.play() - }) - - stackView.addArrangedSubview(with(UITextView()) { - var errorComponents: [NSAttributedString] = [ - NSAttributedString(string: error.localizedDescription), - ] - - func errorCode(_ value: String) -> NSAttributedString { - NSAttributedString(string: L10n.Onboarding.ConnectionTestResult.errorCode + "\n" + value) - } - - if let error = error as? OnboardingAuthError { - if let code = error.errorCode { - errorComponents.append(errorCode(code)) - } - - if let source = error.responseString { - let font: UIFont - - font = .monospacedSystemFont(ofSize: 14.0, weight: .regular) - - errorComponents.append(NSAttributedString( - string: source, - attributes: [.font: font] - )) - } - } else { - let nsError = error as NSError - errorComponents.append(errorCode(String(format: "%@ %d", nsError.domain, nsError.code))) - } - - $0.isScrollEnabled = false - $0.isEditable = false - $0.isSelectable = true - $0.backgroundColor = .clear - $0.textContainer.lineFragmentPadding = 0 - $0.textContainerInset = .zero - let baseAttributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.preferredFont(forTextStyle: .body), - .foregroundColor: Current.style.onboardingLabel, - ] - $0.attributedText = errorComponents.reduce(into: NSMutableAttributedString()) { base, added in - if base.length > 0 { - base.append(NSAttributedString(string: "\n\n", attributes: baseAttributes)) - } - - base.append(with(NSMutableAttributedString(attributedString: added)) { - $0.addMissingAttributes(baseAttributes) - }) - } - }) - - stackView.addArrangedSubview(equalSpacers.next()) - stackView.addArrangedSubview(with(UIButton(type: .custom)) { - $0.setTitle(Current.Log.exportTitle, for: .normal) - $0.addTarget(self, action: #selector(exportTapped(_:)), for: .touchUpInside) - Current.style.onboardingButtonSecondary($0) - }) - stackView.addArrangedSubview(with(UIButton(type: .custom)) { - $0.setTitle(L10n.Onboarding.ConnectionError.moreInfoButton, for: .normal) - $0.isHidden = !(error is OnboardingAuthError) - $0.addTarget(self, action: #selector(moreInfoTapped(_:)), for: .touchUpInside) - Current.style.onboardingButtonPrimary($0) - }) - } - - private func documentationURL(for error: Error) -> URL { - var string = "https://companion.home-assistant.io/docs/troubleshooting/errors" - - if let error = error as? OnboardingAuthError { - string += "#\(error.kind.documentationAnchor)" - } - - return URL(string: string)! - } - - @objc private func moreInfoTapped(_ sender: UIButton) { - openURLInBrowser(documentationURL(for: error), self) - } - - @objc private func exportTapped(_ sender: UIButton) { - Current.Log.export(from: self, sender: sender, openURLHandler: { url in - UIApplication.shared.open(url, options: [:], completionHandler: nil) - }) - } -} diff --git a/Sources/App/Onboarding/Screens/OnboardingManualURLViewController.swift b/Sources/App/Onboarding/Screens/OnboardingManualURLViewController.swift index 4bcfb6b7e..406723144 100644 --- a/Sources/App/Onboarding/Screens/OnboardingManualURLViewController.swift +++ b/Sources/App/Onboarding/Screens/OnboardingManualURLViewController.swift @@ -17,7 +17,7 @@ class OnboardingManualURLViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = Current.style.onboardingBackground + view.backgroundColor = .systemBackground let (scrollView, stackView, equalSpacers) = UIView.contentStackView(in: view, scrolling: true) self.scrollView = scrollView @@ -30,7 +30,7 @@ class OnboardingManualURLViewController: UIViewController, UITextFieldDelegate { stackView.addArrangedSubview(with(UILabel()) { $0.text = L10n.Onboarding.ManualSetup.description $0.font = .preferredFont(forTextStyle: .body) - $0.textColor = Current.style.onboardingLabelSecondary + $0.textColor = Asset.Colors.haPrimary.color $0.textAlignment = .natural $0.numberOfLines = 0 }) diff --git a/Sources/App/Onboarding/Screens/OnboardingPermissionViewController.swift b/Sources/App/Onboarding/Screens/OnboardingPermissionViewController.swift index 5ba47685e..3d7aecac1 100644 --- a/Sources/App/Onboarding/Screens/OnboardingPermissionViewController.swift +++ b/Sources/App/Onboarding/Screens/OnboardingPermissionViewController.swift @@ -25,7 +25,7 @@ class OnboardingPermissionViewController: UIViewController, OnboardingViewContro override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = Current.style.onboardingBackground + view.backgroundColor = .systemBackground navigationItem.hidesBackButton = true let (_, stackView, equalSpacers) = UIView.contentStackView(in: view, scrolling: true) @@ -44,7 +44,7 @@ class OnboardingPermissionViewController: UIViewController, OnboardingViewContro let descriptionLabel = with(UILabel()) { $0.text = permission.enableDescription $0.font = .preferredFont(forTextStyle: .body) - $0.textColor = Current.style.onboardingLabelSecondary + $0.textColor = .secondaryLabel $0.numberOfLines = 0 $0.textAlignment = .center } @@ -69,8 +69,8 @@ class OnboardingPermissionViewController: UIViewController, OnboardingViewContro }) $0.addArrangedSubview(with(UILabel()) { $0.text = bulletPoint.1 - $0.textColor = Current.style.onboardingLabel - $0.font = .preferredFont(forTextStyle: .body) + $0.textColor = .label + $0.font = .boldSystemFont(ofSize: UIFont.preferredFont(forTextStyle: .body).pointSize) $0.numberOfLines = 0 $0.setContentHuggingPriority(.defaultLow, for: .horizontal) }) @@ -98,7 +98,7 @@ class OnboardingPermissionViewController: UIViewController, OnboardingViewContro stackView.addArrangedSubview(with(UILabel()) { $0.font = .preferredFont(forTextStyle: .footnote) - $0.textColor = Current.style.onboardingLabelSecondary + $0.textColor = Asset.Colors.haPrimary.color $0.text = L10n.Onboarding.Permissions.changeLaterNote $0.numberOfLines = 0 $0.textAlignment = .center diff --git a/Sources/App/Onboarding/Screens/OnboardingScanningViewController.swift b/Sources/App/Onboarding/Screens/OnboardingScanningViewController.swift index 398022e53..f050c1a8b 100644 --- a/Sources/App/Onboarding/Screens/OnboardingScanningViewController.swift +++ b/Sources/App/Onboarding/Screens/OnboardingScanningViewController.swift @@ -6,20 +6,20 @@ class OnboardingScanningInstanceCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) with(textLabel) { - $0?.textColor = Current.style.onboardingLabel + $0?.textColor = .label $0?.numberOfLines = 0 $0?.font = UIFont.boldSystemFont(ofSize: UIFont.preferredFont(forTextStyle: .body).pointSize) } with(detailTextLabel) { - $0?.textColor = Current.style.onboardingLabelSecondary + $0?.textColor = .secondaryLabel $0?.numberOfLines = 0 $0?.font = .preferredFont(forTextStyle: .body) } backgroundView = with(UIView()) { - $0.backgroundColor = Current.style.onboardingBackground + $0.backgroundColor = .systemBackground } selectedBackgroundView = with(UIView()) { - $0.backgroundColor = UIColor(white: 0, alpha: 0.25) + $0.backgroundColor = .secondarySystemBackground $0.layer.cornerRadius = 4.0 } backgroundColor = .clear @@ -81,7 +81,8 @@ class OnboardingScanningViewController: UIViewController { let (_, stackView, _) = UIView.contentStackView(in: view, scrolling: false) - view.backgroundColor = Current.style.onboardingBackground + view.backgroundColor = .systemBackground + title = L10n.Onboarding.Scanning.title stackView.addArrangedSubview(with(UILabel()) { $0.text = L10n.Onboarding.Scanning.title @@ -94,9 +95,9 @@ class OnboardingScanningViewController: UIViewController { $0.dataSource = self $0.cellLayoutMarginsFollowReadableWidth = true - $0.backgroundColor = Current.style.onboardingBackground + $0.backgroundColor = .systemBackground $0.backgroundView = with(UIView()) { - $0.backgroundColor = Current.style.onboardingBackground + $0.backgroundColor = .systemBackground } // hides the empty separators @@ -111,7 +112,7 @@ class OnboardingScanningViewController: UIViewController { let manualHintLabel: UILabel = with(UILabel()) { $0.text = L10n.Onboarding.Scanning.manualHint - $0.textColor = Current.style.onboardingLabelSecondary + $0.textColor = Asset.Colors.haPrimary.color $0.font = .preferredFont(forTextStyle: .footnote) $0.numberOfLines = 1 $0.baselineAdjustment = .alignCenters @@ -240,9 +241,14 @@ extension OnboardingScanningViewController: UITableViewDelegate { tableView.isUserInteractionEnabled = true tableView.deselectRow(at: indexPath, animated: true) }.done { [self] server in - show(authentication.successController(server: server), sender: self) + let controller = authentication.successController(server: server) + if controller is OnboardingTerminalViewController { + show(controller, sender: self) + } else { + navigationController?.pushViewController(controller, animated: true) + } }.catch { [self] error in - show(authentication.failureController(error: error), sender: self) + navigationController?.pushViewController(authentication.failureController(error: error), animated: true) } } } diff --git a/Sources/App/Onboarding/Screens/OnboardingWelcomeView.swift b/Sources/App/Onboarding/Screens/OnboardingWelcomeView.swift new file mode 100644 index 000000000..87d630560 --- /dev/null +++ b/Sources/App/Onboarding/Screens/OnboardingWelcomeView.swift @@ -0,0 +1,69 @@ +import Foundation +import Shared +import SwiftUI + +struct OnboardingWelcomeView: View { + @State private var showLearnMore = false + + var body: some View { + VStack(spacing: .zero) { + Spacer() + logoBlock + textBlock + Spacer() + continueButton + } + .frame(maxWidth: 600) + .fullScreenCover(isPresented: $showLearnMore) { + SafariWebView(url: URL(string: "http://www.home-assistant.io")!) + } + } + + private var logoBlock: some View { + Image(uiImage: Asset.SharedAssets.logo.image) + .padding(.vertical, Spaces.four) + } + + private var textBlock: some View { + ScrollView { + VStack(spacing: Spaces.two) { + Text(L10n.Onboarding.Welcome.title(Current.device.systemName())) + .font(.title.bold()) + .frame(maxWidth: .infinity, alignment: .center) + .multilineTextAlignment(.center) + Text(L10n.Onboarding.Welcome.description) + .foregroundStyle(Color(uiColor: .secondaryLabel)) + Button(L10n.Onboarding.Welcome.learnMore) { + showLearnMore = true + } + .tint(Color.asset(Asset.Colors.haPrimary)) + } + .padding() + } + } + + private var continueButton: some View { + NavigationLink(L10n.continueLabel) { + OnboardingScanningView() + } + .font(.callout.bold()) + .foregroundStyle(.white) + .frame(maxWidth: .infinity) + .frame(height: 55) + .background(Color.asset(Asset.Colors.haPrimary)) + .clipShape(RoundedRectangle(cornerRadius: 12)) + .padding(Spaces.two) + } +} + +#Preview { + OnboardingWelcomeView() +} + +struct OnboardingScanningView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + OnboardingScanningViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} diff --git a/Sources/App/Onboarding/Screens/OnboardingWelcomeViewController.swift b/Sources/App/Onboarding/Screens/OnboardingWelcomeViewController.swift deleted file mode 100644 index 8f4bf0377..000000000 --- a/Sources/App/Onboarding/Screens/OnboardingWelcomeViewController.swift +++ /dev/null @@ -1,98 +0,0 @@ -import Eureka -import Lottie -import RealmSwift -import SafariServices -import Shared -import UIKit - -class OnboardingWelcomeViewController: UIViewController, OnboardingViewController { - private var animationView: LottieAnimationView? - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - animationView?.play(toMarker: "Circles Formed") - } - - var preferredBarAppearance: OnboardingBarAppearance { .hidden } - - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = Current.style.onboardingBackground - - let (_, stackView, equalSpacers) = UIView.contentStackView(in: view, scrolling: true) - - stackView.addArrangedSubview(equalSpacers.next()) - stackView.addArrangedSubview(with(LottieAnimationView(animation: .named("ha-loading"))) { - animationView = $0 - $0.loopMode = .playOnce - - NSLayoutConstraint.activate([ - with($0.widthAnchor.constraint(equalToConstant: 240.0)) { - $0.priority = .defaultHigh - }, - $0.widthAnchor.constraint(lessThanOrEqualToConstant: 240.0), - $0.widthAnchor.constraint(equalTo: $0.heightAnchor), - ]) - }) - - stackView.addArrangedSubview(with(UILabel()) { - $0.text = L10n.Onboarding.Welcome.title(Current.device.systemName()) - Current.style.onboardingTitle($0) - }) - - stackView.addArrangedSubview(with(UILabel()) { - $0.text = L10n.Onboarding.Welcome.description - $0.font = .preferredFont(forTextStyle: .body) - $0.textColor = Current.style.onboardingLabelSecondary - $0.textAlignment = .center - $0.numberOfLines = 0 - }) - stackView.addArrangedSubview(with(UIButton(type: .system)) { - $0.setAttributedTitle(NSAttributedString( - string: L10n.Nfc.List.learnMore, - attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue] - ), for: .normal) - $0.titleLabel?.font = .preferredFont(forTextStyle: .body) - $0.setTitleColor(Current.style.onboardingLabelSecondary, for: .normal) - $0.addTarget(self, action: #selector(learnMoreTapped(_:)), for: .touchUpInside) - - UIView.performWithoutAnimation { [button = $0] in - // Prevent the button from fading in initially - button.layoutIfNeeded() - } - }) - - stackView.addArrangedSubview(equalSpacers.next()) - - stackView.addArrangedSubview(with(UIButton(type: .custom)) { - $0.setTitle(L10n.continueLabel, for: .normal) - $0.addTarget(self, action: #selector(continueTapped(_:)), for: .touchUpInside) - Current.style.onboardingButtonPrimary($0) - }) - - updateAnimationHiddenState() - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - updateAnimationHiddenState() - } - - private func updateAnimationHiddenState() { - let animationHidden = traitCollection.verticalSizeClass == .compact - animationView?.isHidden = animationHidden - } - - @objc private func continueTapped(_ sender: UIButton) { - show(OnboardingScanningViewController(), sender: self) - } - - @objc private func learnMoreTapped(_ sender: UIButton) { - present( - SFSafariViewController(url: .init(string: "http://www.home-assistant.io")!), - animated: true, - completion: nil - ) - } -} diff --git a/Sources/App/Onboarding/Screens/SafariWebView.swift b/Sources/App/Onboarding/Screens/SafariWebView.swift new file mode 100644 index 000000000..80ff75c08 --- /dev/null +++ b/Sources/App/Onboarding/Screens/SafariWebView.swift @@ -0,0 +1,12 @@ +import SafariServices +import SwiftUI + +struct SafariWebView: UIViewControllerRepresentable { + let url: URL + + func makeUIViewController(context: Context) -> SFSafariViewController { + SFSafariViewController(url: url) + } + + func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {} +} diff --git a/Sources/App/Resources/en.lproj/Localizable.strings b/Sources/App/Resources/en.lproj/Localizable.strings index cf372df6b..ca4661f32 100644 --- a/Sources/App/Resources/en.lproj/Localizable.strings +++ b/Sources/App/Resources/en.lproj/Localizable.strings @@ -373,6 +373,7 @@ Tags will work on any device with Home Assistant installed which has hardware su \ Home Assistant is free and open source home automation software with a focus on local control and privacy."; "onboarding.welcome.title" = "Welcome to Home Assistant %@!"; +"onboarding.welcome.learn_more" = "Learn more"; "open_label" = "Open"; "preview_output" = "Preview Output"; "reload_label" = "Reload"; @@ -993,4 +994,4 @@ Home Assistant is free and open source home automation software with a focus on "widgets.scripts.description" = "Run Scripts"; "widgets.scripts.not_configured" = "No Scripts Configured"; "widgets.scripts.title" = "Scripts"; -"yes_label" = "Yes"; \ No newline at end of file +"yes_label" = "Yes"; diff --git a/Sources/App/Settings/Eureka/SettingsButtonRow.swift b/Sources/App/Settings/Eureka/SettingsButtonRow.swift index b2954bb04..9d8687c20 100644 --- a/Sources/App/Settings/Eureka/SettingsButtonRow.swift +++ b/Sources/App/Settings/Eureka/SettingsButtonRow.swift @@ -29,7 +29,7 @@ public final class SettingsButtonRow: _ButtonRowOf, RowType { cell.imageView?.image = icon.settingsIcon(for: cell.traitCollection) } else if let image { cell.imageView?.image = image.scaledToSize(.init(width: 24, height: 24)) - .withTintColor(AppConstants.darkerTintColor) + .withTintColor(Asset.Colors.haPrimary.color) } else { cell.imageView?.image = nil } diff --git a/Sources/Shared/Common/Extensions/XCGLogger+Export.swift b/Sources/Shared/Common/Extensions/XCGLogger+Export.swift index ebe90f236..e4b5f7953 100644 --- a/Sources/Shared/Common/Extensions/XCGLogger+Export.swift +++ b/Sources/Shared/Common/Extensions/XCGLogger+Export.swift @@ -16,13 +16,42 @@ public extension XCGLogger { func export(from source: UIViewController, sender: UIView, openURLHandler: (URL) -> Void) { Current.Log.verbose("Logs directory is: \(Shared.AppConstants.LogsDirectory)") + guard let archiveURL = archiveURL() else { return } guard !Current.isCatalyst else { // on Catalyst we can just open the directory to get to Finder - openURLHandler(Shared.AppConstants.LogsDirectory) + openURLHandler(archiveURL) return } + Current.Log.debug("Exporting logs as filename \(archiveURL.absoluteString)") + let fileManager = FileManager.default + + let controller = UIActivityViewController(activityItems: [archiveURL], applicationActivities: nil) + + controller.completionWithItemsHandler = { type, completed, _, _ in + let didCancelEntirely = type == nil && !completed + let didCompleteEntirely = completed + + if didCancelEntirely || didCompleteEntirely { + try? fileManager.removeItem(at: archiveURL) + } + } + + with(controller.popoverPresentationController) { + $0?.sourceView = sender + } + + source.present(controller, animated: true, completion: nil) + } + + func archiveURL() -> URL? { + Current.Log.verbose("Logs directory is: \(Shared.AppConstants.LogsDirectory)") + + guard !Current.isCatalyst else { + return Shared.AppConstants.LogsDirectory + } + let fileManager = FileManager.default let fileName = DateFormatter( @@ -33,7 +62,10 @@ public extension XCGLogger { Current.Log.debug("Exporting logs as filename \(fileName)") let archiveURL = fileManager.temporaryDirectory.appendingPathComponent(fileName, isDirectory: false) - let archive = Archive(url: archiveURL, accessMode: .create)! + guard let archive = Archive(url: archiveURL, accessMode: .create) else { + Current.Log.error("Failed to create archive at \(archiveURL.path)") + return nil + } do { if let backupURL = Realm.backup() { @@ -63,27 +95,10 @@ public extension XCGLogger { relativeTo: logFile.deletingLastPathComponent() ) } - - let controller = UIActivityViewController(activityItems: [archiveURL], applicationActivities: nil) - - controller.completionWithItemsHandler = { type, completed, _, _ in - let didCancelEntirely = type == nil && !completed - let didCompleteEntirely = completed - - if didCancelEntirely || didCompleteEntirely { - try? fileManager.removeItem(at: archiveURL) - } - } - - with(controller.popoverPresentationController) { - $0?.sourceView = sender - } - - source.present(controller, animated: true, completion: nil) + return archiveURL } catch { - let alert = UIAlertController(title: nil, message: error.localizedDescription, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: L10n.okLabel, style: .cancel, handler: nil)) - source.present(alert, animated: true, completion: nil) + Current.Log.error("Error getting logs URL: \(error.localizedDescription)") + return nil } } } diff --git a/Sources/Shared/Environment/Style.swift b/Sources/Shared/Environment/Style.swift index 05bce9580..c2f753bce 100644 --- a/Sources/Shared/Environment/Style.swift +++ b/Sources/Shared/Environment/Style.swift @@ -3,13 +3,9 @@ import UIKit public struct Style { #if os(iOS) - public var onboardingBackground: UIColor = AppConstants.darkerTintColor - public var onboardingTintColor: UIColor = .white - public var onboardingLabel: UIColor = .white - public var onboardingLabelSecondary: UIColor = .white.withAlphaComponent(0.85) public var onboardingTitle: (_ label: UILabel) -> Void = { label in - label.font = .preferredFont(forTextStyle: .title1) - label.textColor = Current.style.onboardingLabel + label.font = .boldSystemFont(ofSize: UIFont.preferredFont(forTextStyle: .title1).pointSize) + label.textColor = .label label.textAlignment = .center label.numberOfLines = 0 label.accessibilityTraits.insert(.header) @@ -25,17 +21,24 @@ public struct Style { widthAnchor.constraint(equalTo: superview.readableContentGuide.widthAnchor) .isActive = true default: - widthAnchor.constraint(equalTo: superview.layoutMarginsGuide.widthAnchor) - .isActive = true + break } } } } private static func onboardingButton(_ button: UIButton) { - button.layer.cornerRadius = 6.0 + button.layer.cornerRadius = 12 button.layer.masksToBounds = true - button.contentEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + + var config = UIButton.Configuration.filled() + config.contentInsets = .init( + top: Spaces.two, + leading: Spaces.two, + bottom: Spaces.two, + trailing: Spaces.two + ) + button.configuration = config button.titleLabel?.font = UIFont.systemFont( ofSize: UIFont.preferredFont(forTextStyle: .callout).pointSize, @@ -50,7 +53,9 @@ public struct Style { public var onboardingButtonPrimary: (_ button: UIButton) -> Void = { button in Self.onboardingButton(button) - button.setTitleColor(AppConstants.darkerTintColor, for: .normal) + button.setTitleColor(.white, for: .normal) + button.backgroundColor = Asset.Colors.haPrimary.color + button.setBackgroundImage( UIImage(size: CGSize(width: 1, height: 1), color: .white), for: .normal @@ -60,9 +65,7 @@ public struct Style { for: .highlighted ) - #if targetEnvironment(macCatalyst) button.role = .primary - #endif } public var onboardingButtonSecondary: (_ button: UIButton) -> Void = { button in diff --git a/Sources/Shared/Resources/Swiftgen/Strings.swift b/Sources/Shared/Resources/Swiftgen/Strings.swift index b4414310d..03a6c8b39 100644 --- a/Sources/Shared/Resources/Swiftgen/Strings.swift +++ b/Sources/Shared/Resources/Swiftgen/Strings.swift @@ -1360,6 +1360,8 @@ public enum L10n { /// /// Home Assistant is free and open source home automation software with a focus on local control and privacy. public static var description: String { return L10n.tr("Localizable", "onboarding.welcome.description") } + /// Learn more + public static var learnMore: String { return L10n.tr("Localizable", "onboarding.welcome.learn_more") } /// Welcome to Home Assistant %@! public static func title(_ p1: Any) -> String { return L10n.tr("Localizable", "onboarding.welcome.title", String(describing: p1))