Show startup as one step checklist and re-check tools on the Server tab

This commit is contained in:
Ian Gulliver
2026-10-23 05:42:30 -07:00
parent 522c903344
commit dfb114ea64
9 changed files with 242 additions and 162 deletions
-102
View File
@@ -3,9 +3,6 @@ import Network
import Observation
import SwiftUI
import TailscaleKit
#if os(iOS)
import SafariServices
#endif
func slug(_ text: String) -> String {
var out = ""
@@ -242,102 +239,3 @@ final class Tailnet {
}
}
struct TailnetLoginView: View {
var body: some View {
let tailnet = Tailnet.shared
VStack(alignment: .leading, spacing: 12) {
Text("Sign in to Tailscale")
.font(Fonts.font(17, weight: 700))
Text("byre reaches its servers through Tailscale. Sign each of these nodes in to the tailnet your byre servers are on.")
.foregroundStyle(Palette.muted)
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 4)
NodeLoginRow(hostname: tailnetNode.hostname, role: "this app", state: tailnet.state, authURL: tailnet.authURL)
if let service = tailnet.service {
NodeLoginRow(hostname: service.hostname, role: "this Mac's server", state: service.state, authURL: URL(string: service.authUrl))
}
}
.padding(20)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Palette.raised, in: RoundedRectangle(cornerRadius: 10))
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Palette.border))
.frame(maxWidth: 460)
.padding(16)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear {
Tailnet.shared.serviceWatchers += 1
}
.onDisappear {
Tailnet.shared.serviceWatchers -= 1
}
}
}
private struct NodeLoginRow: View {
let hostname: String
let role: String
let state: String
let authURL: URL?
@State private var signingIn = false
var body: some View {
HStack(alignment: .center, spacing: 12) {
VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .firstTextBaseline, spacing: 8) {
Text(hostname).font(Fonts.font(13, weight: 700))
Text(role).foregroundStyle(Palette.muted)
}
if state == "Running" {
Text("Signed in").foregroundStyle(Palette.ok)
} else {
Text(authURL == nil ? "Waiting for Tailscale (\(state))…" : "Waiting for the sign-in (\(state))…")
.font(Fonts.font(12))
.foregroundStyle(Palette.muted)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
if state != "Running" {
Button("Sign in") {
#if os(macOS)
if let authURL {
openExternal(authURL)
}
#else
signingIn = true
#endif
}
.buttonStyle(WebButtonStyle(primary: true))
.disabled(authURL == nil)
.fixedSize()
}
}
.padding(12)
.background(Palette.bg, in: RoundedRectangle(cornerRadius: 8))
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Palette.border))
#if os(iOS)
.sheet(isPresented: $signingIn) {
if let authURL {
SafariView(url: authURL)
.ignoresSafeArea()
}
}
.onChange(of: state) {
if state == "Running" {
signingIn = false
}
}
#endif
}
}
#if os(iOS)
private struct SafariView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
SFSafariViewController(url: url)
}
func updateUIViewController(_ controller: SFSafariViewController, context: Context) {}
}
#endif