Files
iOS/Sources/App/Frontend/WebView/WebViewController+StatusBar.swift
Bruno Pantaleão Gonçalves a21ebf003b Refactor WebViewController (#4383)
Split WebViewController functionality into multiple extension files
(Alerts, EmptyState, Gestures, Navigation, Onboarding, Settings,
StatusBar, URLLoading, WebViewSetup) and add FrontEndConnectionState
enum. Update Xcode project to include the new source files and adjust
several Database test file references and a local Swift package path
(Sources/SharedPush). Also add empty input/output path arrays to several
CocoaPods embed phases. This refactors web view features into modular
components and wires them into the project file.

<!-- 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. -->

## 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. -->
2026-02-23 11:57:56 +01:00

121 lines
4.1 KiB
Swift

import Shared
import SwiftUI
import UIKit
@preconcurrency import WebKit
// MARK: - Status Bar & Toolbar
extension WebViewController {
func setupStatusBarView() -> UIView {
let statusBarView = UIView()
statusBarView.tag = 111
self.statusBarView = statusBarView
view.addSubview(statusBarView)
statusBarView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
statusBarView.topAnchor.constraint(equalTo: view.topAnchor),
statusBarView.leftAnchor.constraint(equalTo: view.leftAnchor),
statusBarView.rightAnchor.constraint(equalTo: view.rightAnchor),
statusBarView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
])
if Current.isCatalyst {
setupStatusBarButtons(in: statusBarView)
}
return statusBarView
}
func setupStatusBarButtons(in statusBarView: UIView) {
// Remove existing stack if present
if let statusBarButtonsStack {
statusBarButtonsStack.removeFromSuperview()
self.statusBarButtonsStack = nil
}
let configuration = StatusBarButtonsConfigurator.Configuration(
server: server,
servers: Current.servers.all,
actions: .init(
refresh: { [weak self] in
self?.refresh()
},
openServer: { [weak self] server in
self?.openServer(server)
},
openInSafari: { [weak self] in
self?.openServerInSafari()
},
goBack: { [weak self] in
self?.goBack()
},
goForward: { [weak self] in
self?.goForward()
},
copy: { [weak self] in
self?.copyCurrentSelectedContent()
},
paste: { [weak self] in
self?.pasteContent()
}
)
)
statusBarButtonsStack = StatusBarButtonsConfigurator.setupButtons(
in: statusBarView,
configuration: configuration
)
}
func openServer(_ server: Server) {
Current.sceneManager.webViewWindowControllerPromise.done { controller in
controller.open(server: server)
}
}
@objc func openServerInSafari() {
if let url = webView.url {
guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return
}
// Remove external_auth=1 query item from URL
urlComponents.queryItems = urlComponents.queryItems?.filter { $0.name != "external_auth" }
if let url = urlComponents.url {
URLOpener.shared.open(url, options: [:], completionHandler: nil)
}
}
}
@objc func copyCurrentSelectedContent() {
// Get selected text from the web view
webView.evaluateJavaScript("window.getSelection().toString();") { result, error in
Current.Log
.error(
"Copy selected content result: \(String(describing: result)), error: \(String(describing: error))"
)
if let selectedText = result as? String, !selectedText.isEmpty {
// Copy to clipboard
UIPasteboard.general.string = selectedText
}
}
}
@objc func pasteContent() {
// Programmatically trigger the standard iOS paste action by calling the paste: selector
// This mimics the user selecting "Paste" from the context menu and allows paste to work properly
if webView.responds(to: #selector(paste(_:))) {
webView.perform(#selector(paste(_:)), with: nil)
}
}
@available(iOS 16.0, *)
@objc func showFindInteraction() {
// Present the find interaction UI
if let findInteraction = webView.findInteraction {
findInteraction.presentFindNavigator(showingReplace: false)
}
}
}