Add an iPhone client sharing the macOS app's SwiftUI code
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import AppKit
|
||||
import Foundation
|
||||
import Network
|
||||
import Observation
|
||||
@@ -24,7 +23,7 @@ func slug(_ text: String) -> String {
|
||||
|
||||
final class TailnetNode: Sendable {
|
||||
let sd: tailscale
|
||||
let hostname = "byre-app-\(slug(macName))"
|
||||
let hostname = "byre-app-\(slug(deviceName))"
|
||||
private let proxyHost: String
|
||||
private let proxyPort: UInt16
|
||||
private let proxyCredential: String
|
||||
@@ -165,14 +164,17 @@ final class Tailnet {
|
||||
try? await Task.sleep(for: .seconds(1))
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
Task {
|
||||
await watchService()
|
||||
}
|
||||
#endif
|
||||
Task {
|
||||
await discover()
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private func watchService() async {
|
||||
var loopback: URL?
|
||||
var asked: ContinuousClock.Instant?
|
||||
@@ -198,6 +200,7 @@ final class Tailnet {
|
||||
try? await Task.sleep(for: .seconds(2))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private func discover() async {
|
||||
while true {
|
||||
@@ -252,16 +255,24 @@ final class Tailnet {
|
||||
struct TailnetLoginView: View {
|
||||
var body: some View {
|
||||
let tailnet = Tailnet.shared
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
.frame(width: 460)
|
||||
.padding(.vertical, 24)
|
||||
.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
|
||||
@@ -279,23 +290,34 @@ private struct NodeLoginRow: View {
|
||||
let authURL: URL?
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
HStack(spacing: 8) {
|
||||
Text(hostname).font(Fonts.font(13, weight: 700))
|
||||
Text(role).foregroundStyle(Palette.muted)
|
||||
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)
|
||||
}
|
||||
}
|
||||
if state == "Running" {
|
||||
Text("Signed in").foregroundStyle(Palette.ok)
|
||||
} else {
|
||||
Button("Sign in to Tailscale") {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
if state != "Running" {
|
||||
Button("Sign in") {
|
||||
if let authURL {
|
||||
NSWorkspace.shared.open(authURL)
|
||||
openExternal(authURL)
|
||||
}
|
||||
}
|
||||
.buttonStyle(WebButtonStyle(primary: true))
|
||||
.disabled(authURL == nil)
|
||||
Text(authURL == nil ? "Waiting for Tailscale (\(state))…" : "Waiting for the sign-in (\(state))…")
|
||||
.foregroundStyle(Palette.muted)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
.padding(12)
|
||||
.background(Palette.bg, in: RoundedRectangle(cornerRadius: 8))
|
||||
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Palette.border))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user