127 lines
4.5 KiB
Swift
127 lines
4.5 KiB
Swift
import ServiceManagement
|
|
import SwiftUI
|
|
|
|
struct SettingsView: View {
|
|
@State private var config: AppConfig
|
|
@State private var isTestingConnection = false
|
|
@State private var connectionResult: String?
|
|
|
|
init() {
|
|
_config = State(initialValue: AppStateService.shared.config ?? AppConfig())
|
|
}
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section("Feishu Bitable") {
|
|
TextField("App Token (from Bitable URL)", text: $config.appToken)
|
|
TextField("Table ID", text: $config.tableId)
|
|
TextField("Your Name (as in Assignee column, optional)", text: $config.assigneeName)
|
|
TextField("Feishu domain", text: $config.feishuBaseDomain)
|
|
|
|
HStack {
|
|
Button("Test Connection") {
|
|
testConnection()
|
|
}
|
|
.disabled(isTestingConnection || !config.isConfigured)
|
|
|
|
if isTestingConnection {
|
|
ProgressView()
|
|
.scaleEffect(0.7)
|
|
}
|
|
if let connectionResult {
|
|
Text(connectionResult)
|
|
.font(.caption)
|
|
.foregroundStyle(connectionResult.contains("✓") ? .green : .red)
|
|
}
|
|
}
|
|
}
|
|
|
|
Section("Polling") {
|
|
Picker("Check every", selection: $config.pollIntervalSeconds) {
|
|
Text("1 minute").tag(60)
|
|
Text("5 minutes").tag(300)
|
|
Text("10 minutes").tag(600)
|
|
Text("30 minutes").tag(1800)
|
|
}
|
|
}
|
|
|
|
Section("Display") {
|
|
Toggle("Show floating widget", isOn: $config.showFloatingWidget)
|
|
.onChange(of: config.showFloatingWidget) { _, enabled in
|
|
if enabled {
|
|
AppDelegate.shared?.showFloatingWidget()
|
|
} else {
|
|
AppDelegate.shared?.hideFloatingWidget()
|
|
}
|
|
}
|
|
Toggle("Launch at login", isOn: $config.launchAtLogin)
|
|
}
|
|
|
|
Section("Field Mappings") {
|
|
TextField("Title field", text: $config.fieldMappings.titleField)
|
|
TextField("Priority field", text: $config.fieldMappings.priorityField)
|
|
TextField("Status field", text: $config.fieldMappings.statusField)
|
|
TextField("Assignee field", text: $config.fieldMappings.assigneeField)
|
|
TextField("Reporter field", text: $config.fieldMappings.reporterField)
|
|
TextField("Created At field", text: $config.fieldMappings.createdAtField)
|
|
TextField("Updated At field", text: $config.fieldMappings.updatedAtField)
|
|
}
|
|
|
|
HStack {
|
|
Button("Save") {
|
|
save()
|
|
}
|
|
.keyboardShortcut(.return)
|
|
|
|
Button("Disconnect Feishu") {
|
|
TokenManager.shared.clearTokens()
|
|
PollerService.shared.stop()
|
|
}
|
|
.foregroundStyle(.red)
|
|
}
|
|
}
|
|
.padding()
|
|
.frame(width: 440, height: 560)
|
|
}
|
|
|
|
private func save() {
|
|
AppStateService.shared.saveConfig(config)
|
|
connectionResult = "Saved ✓"
|
|
|
|
if config.launchAtLogin {
|
|
try? SMAppService.mainApp.register()
|
|
} else {
|
|
try? SMAppService.mainApp.unregister()
|
|
}
|
|
|
|
if config.showFloatingWidget {
|
|
AppDelegate.shared?.showFloatingWidget()
|
|
} else {
|
|
AppDelegate.shared?.hideFloatingWidget()
|
|
}
|
|
|
|
Task {
|
|
PollerService.shared.restart(interval: TimeInterval(config.pollIntervalSeconds))
|
|
}
|
|
}
|
|
|
|
private func testConnection() {
|
|
isTestingConnection = true
|
|
connectionResult = nil
|
|
Task {
|
|
do {
|
|
let token = try await TokenManager.shared.getAccessToken()
|
|
let count = try await FeishuService().fetchRecordCount(
|
|
appToken: config.appToken,
|
|
tableId: config.tableId,
|
|
accessToken: token
|
|
)
|
|
connectionResult = "Connected ✓ (\(count) records)"
|
|
} catch {
|
|
connectionResult = "Failed: \(error.localizedDescription)"
|
|
}
|
|
isTestingConnection = false
|
|
}
|
|
}
|
|
}
|