hololake-system-architecture/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift

203 lines
8.4 KiB
Swift

import SwiftUI
import UIKit
struct ContentView: View {
@EnvironmentObject private var client: HoloLakeSyncClient
@State private var pairingText = ""
@State private var captureTitle = ""
@State private var captureBody = ""
var body: some View {
NavigationStack {
ZStack {
LinearGradient(
colors: [HoloLakeDesign.deep, HoloLakeDesign.lake, HoloLakeDesign.violet.opacity(0.78)],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
ScrollView {
VStack(spacing: 16) {
header
if client.session == nil { pairingCard } else { dashboard }
boundaryCard
}
.padding(.horizontal, 18)
.padding(.vertical, 16)
}
}
.toolbar(.hidden, for: .navigationBar)
.alert("HoloLake", isPresented: Binding(
get: { client.errorText != nil },
set: { if !$0 { client.errorText = nil } }
)) {
Button("知道了", role: .cancel) { client.errorText = nil }
} message: {
Text(client.errorText ?? "")
}
}
.preferredColorScheme(.dark)
}
private var header: some View {
HStack(alignment: .center) {
VStack(alignment: .leading, spacing: 5) {
Text("HoloLake").font(.title.weight(.semibold)).tracking(2)
Text("同一频道 · iPhone 轻入口")
.font(.subheadline)
.foregroundStyle(HoloLakeDesign.muted)
}
Spacer()
Circle()
.fill(client.session == nil ? HoloLakeDesign.gold : HoloLakeDesign.mint)
.frame(width: 11, height: 11)
.shadow(color: client.session == nil ? HoloLakeDesign.gold : HoloLakeDesign.mint, radius: 8)
}
.foregroundStyle(HoloLakeDesign.pearl)
.padding(.top, 4)
}
private var pairingCard: some View {
LakeCard {
VStack(alignment: .leading, spacing: 14) {
Text("连接电脑端").font(.title3.weight(.semibold))
Text("先在电脑端打开“多端同步”,再扫描配对码或粘贴配对链接。配对密钥只保存在这台 iPhone 的钥匙串。")
.font(.subheadline)
.foregroundStyle(HoloLakeDesign.muted)
TextField("hololake://pair?…", text: $pairingText, axis: .vertical)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.padding(14)
.background(.black.opacity(0.22), in: RoundedRectangle(cornerRadius: 16))
HStack {
Button("从剪贴板读取") {
pairingText = UIPasteboard.general.string ?? ""
}
.buttonStyle(.bordered)
Button("校验并配对") {
Task { await client.pair(from: pairingText) }
}
.buttonStyle(.borderedProminent)
.tint(HoloLakeDesign.gold)
.foregroundStyle(HoloLakeDesign.deep)
.disabled(pairingText.isEmpty || client.isWorking)
}
}
}
.foregroundStyle(HoloLakeDesign.pearl)
}
private var dashboard: some View {
VStack(spacing: 16) {
LakeCard {
VStack(alignment: .leading, spacing: 14) {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(client.session?.desktopName ?? "电脑端")
.font(.title3.weight(.semibold))
Text(client.stateText)
.font(.caption)
.foregroundStyle(HoloLakeDesign.muted)
}
Spacer()
Button {
Task { await client.sync() }
} label: {
if client.isWorking {
ProgressView()
} else {
Label("同步", systemImage: "arrow.triangle.2.circlepath")
}
}
.buttonStyle(.borderedProminent)
.tint(HoloLakeDesign.mint)
.foregroundStyle(HoloLakeDesign.deep)
.disabled(client.isWorking)
}
if let snapshot = client.snapshot {
HStack(spacing: 10) {
MetricTile(label: "作品", value: "\(snapshot.webNovel.workCount)")
MetricTile(label: "章节", value: "\(snapshot.webNovel.chapterCount)")
MetricTile(label: "教育表", value: "\(snapshot.education.activeTableCount)")
}
} else {
Text("保持静止;只有你点击同步时才会连接电脑。")
.font(.subheadline)
.foregroundStyle(HoloLakeDesign.muted)
}
}
}
LakeCard {
VStack(alignment: .leading, spacing: 12) {
Text("随手记回频道").font(.headline)
TextField("标题(可选)", text: $captureTitle)
.padding(12)
.background(.black.opacity(0.2), in: RoundedRectangle(cornerRadius: 14))
TextField("正文", text: $captureBody, axis: .vertical)
.lineLimit(4...10)
.padding(12)
.background(.black.opacity(0.2), in: RoundedRectangle(cornerRadius: 14))
Button("写入电脑端收件箱") {
let title = captureTitle
let body = captureBody
Task {
await client.sync(captureTitle: title, captureBody: body)
if client.errorText == nil {
captureTitle = ""
captureBody = ""
}
}
}
.buttonStyle(.bordered)
.disabled(captureBody.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || client.isWorking)
}
}
if let works = client.snapshot?.webNovel.works, !works.isEmpty {
LakeCard {
VStack(alignment: .leading, spacing: 12) {
Text("最近作品").font(.headline)
ForEach(works.prefix(5)) { work in
HStack {
Text(work.title).lineLimit(1)
Spacer()
Text(work.status)
.font(.caption)
.foregroundStyle(HoloLakeDesign.muted)
}
if work.id != works.prefix(5).last?.id {
Divider().overlay(.white.opacity(0.12))
}
}
}
}
}
Button("解除本机配对", role: .destructive) {
client.disconnect()
}
.font(.footnote)
}
.foregroundStyle(HoloLakeDesign.pearl)
}
private var boundaryCard: some View {
VStack(alignment: .leading, spacing: 6) {
Text("HLP-IOS-CLIENT-001")
.font(.caption2.monospaced())
.foregroundStyle(HoloLakeDesign.gold)
Text("手机是同一频道的远程身体入口,不复制电脑桌面,不在电脑离线时执行人格或行业任务。")
.font(.caption)
.foregroundStyle(HoloLakeDesign.muted)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 4)
}
}