Skip to content

Commit 503a339

Browse files
authored
Add mac server quick switch picker and button to open current page in external browser (#3407)
<!-- Thank you for submitting a Pull Request and helping to improve Home Assistant. Please complete the following sections to help the processing and review of your changes. Please do not delete anything from this template. --> ## Summary <!-- Provide a brief summary of the changes you have made and most importantly what they aim to achieve --> ## Screenshots <!-- If this is a user-facing change not in the frontend, please include screenshots in light and dark mode. --> ![CleanShot 2025-02-05 at 15 38 01@2x](https://github.com/user-attachments/assets/c9ef5733-3bd3-4d58-99c5-bccea1367a92) ## Link to pull request in Documentation repository <!-- Pull requests that add, change or remove functionality must have a corresponding pull request in the Companion App Documentation repository (https://github.com/home-assistant/companion.home-assistant). Please add the number of this pull request after the "#" --> Documentation: home-assistant/companion.home-assistant# ## Any other notes <!-- If there is any other information of note, like if this Pull Request is part of a bigger change, please include it here. -->
1 parent ddb696d commit 503a339

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

Sources/App/Resources/en.lproj/Localizable.strings

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1162,4 +1162,5 @@ Home Assistant is free and open source home automation software with a focus on
11621162
"widgets.sensors.description" = "Display state of sensors";
11631163
"widgets.sensors.not_configured" = "No Sensors Configured";
11641164
"widgets.sensors.title" = "Sensors";
1165-
"yes_label" = "Yes";
1165+
"web_view.server_selection.title" = "Choose server";
1166+
"yes_label" = "Yes";

Sources/App/WebView/WebViewController.swift

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
4040
let webViewExternalMessageHandler = WebViewExternalMessageHandler.build()
4141

4242
private var initialURL: URL?
43+
private var statusBarButtonsStack: UIStackView?
4344

4445
/// A view controller presented by a request from the webview
4546
var overlayAppController: UIViewController?
@@ -236,16 +237,92 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
236237
statusBarView.tag = 111
237238

238239
view.addSubview(statusBarView)
240+
statusBarView.translatesAutoresizingMaskIntoConstraints = false
239241

240-
statusBarView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
241-
statusBarView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
242-
statusBarView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
243-
statusBarView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
242+
NSLayoutConstraint.activate([
243+
statusBarView.topAnchor.constraint(equalTo: view.topAnchor),
244+
statusBarView.leftAnchor.constraint(equalTo: view.leftAnchor),
245+
statusBarView.rightAnchor.constraint(equalTo: view.rightAnchor),
246+
statusBarView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
247+
])
248+
249+
if Current.isCatalyst {
250+
setupStatusBarButtons(statusBarView: statusBarView)
251+
}
244252

245-
statusBarView.translatesAutoresizingMaskIntoConstraints = false
246253
return statusBarView
247254
}
248255

256+
private func setupStatusBarButtons(statusBarView: UIView) {
257+
let picker = UIButton(type: .system)
258+
picker.setTitle(server.info.name, for: .normal)
259+
picker.translatesAutoresizingMaskIntoConstraints = false
260+
261+
let menuActions = Current.servers.all.map { server in
262+
UIAction(title: server.info.name, handler: { [weak self] _ in
263+
self?.openServer(server)
264+
})
265+
}
266+
267+
// Using UIMenu since UIPickerView is not available on Catalyst
268+
picker.menu = UIMenu(title: L10n.WebView.ServerSelection.title, children: menuActions)
269+
picker.showsMenuAsPrimaryAction = true
270+
271+
let openInSafariButton = UIButton(type: .detailDisclosure)
272+
openInSafariButton.setImage(UIImage(systemSymbol: .safari), for: .normal)
273+
openInSafariButton.backgroundColor = .systemBackground
274+
openInSafariButton.tintColor = Asset.Colors.haPrimary.color
275+
openInSafariButton.layer.cornerRadius = 10
276+
openInSafariButton.addTarget(self, action: #selector(openServerInSafari), for: .touchUpInside)
277+
278+
if let statusBarButtonsStack {
279+
statusBarButtonsStack.removeFromSuperview()
280+
self.statusBarButtonsStack = nil
281+
}
282+
283+
let arrangedSubviews: [UIView] = {
284+
if Current.servers.all.count > 1 {
285+
return [picker, openInSafariButton]
286+
} else {
287+
// No need to display server picker
288+
return [openInSafariButton]
289+
}
290+
}()
291+
292+
let stackView = UIStackView(arrangedSubviews: arrangedSubviews)
293+
stackView.axis = .horizontal
294+
stackView.spacing = Spaces.one
295+
296+
statusBarView.addSubview(stackView)
297+
stackView.translatesAutoresizingMaskIntoConstraints = false
298+
299+
NSLayoutConstraint.activate([
300+
stackView.rightAnchor.constraint(equalTo: statusBarView.rightAnchor, constant: -Spaces.half),
301+
stackView.topAnchor.constraint(equalTo: statusBarView.topAnchor, constant: Spaces.half),
302+
])
303+
statusBarButtonsStack = stackView
304+
}
305+
306+
private func openServer(_ server: Server) {
307+
Current.sceneManager.webViewWindowControllerPromise.done { controller in
308+
controller.open(server: server)
309+
}
310+
}
311+
312+
@objc private func openServerInSafari() {
313+
if let url = webView.url {
314+
guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
315+
return
316+
}
317+
// Remove external_auth=1 query item from URL
318+
urlComponents.queryItems = urlComponents.queryItems?.filter { $0.name != "external_auth" }
319+
320+
if let url = urlComponents.url {
321+
UIApplication.shared.open(url)
322+
}
323+
}
324+
}
325+
249326
public func showSettingsViewController() {
250327
getLatestConfig()
251328
if Current.sceneManager.supportsMultipleScenes, Current.isCatalyst {

Sources/Shared/Resources/Swiftgen/Strings.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3817,6 +3817,13 @@ public enum L10n {
38173817
}
38183818
}
38193819

3820+
public enum WebView {
3821+
public enum ServerSelection {
3822+
/// Choose server
3823+
public static var title: String { return L10n.tr("Localizable", "web_view.server_selection.title") }
3824+
}
3825+
}
3826+
38203827
public enum Widgets {
38213828
public enum Action {
38223829
public enum Name {

0 commit comments

Comments
 (0)