macOS client store and API
This commit is contained in:
@@ -1,94 +0,0 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
struct ProjectRow: Decodable {
|
||||
let id: String
|
||||
let name: String
|
||||
}
|
||||
|
||||
struct TaskRow: Decodable, Hashable {
|
||||
let id: String
|
||||
let title: String
|
||||
let status: String
|
||||
}
|
||||
|
||||
struct BlobRow: Decodable {
|
||||
let id: String
|
||||
}
|
||||
|
||||
struct SpikeError: Error, CustomStringConvertible {
|
||||
let description: String
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class Server {
|
||||
var origin = UserDefaults.standard.string(forKey: "byre.server") ?? ""
|
||||
var tasks: [TaskRow] = []
|
||||
var taskId = ""
|
||||
var status = ""
|
||||
var lines: [String] = []
|
||||
|
||||
func log(_ line: String) {
|
||||
lines.append(line)
|
||||
}
|
||||
|
||||
private var base: URLComponents?
|
||||
|
||||
func url(_ path: String, _ query: [URLQueryItem] = []) throws -> URL {
|
||||
guard var c = base else {
|
||||
throw SpikeError(description: "not connected")
|
||||
}
|
||||
c.path = path
|
||||
if !query.isEmpty {
|
||||
c.queryItems = query
|
||||
}
|
||||
guard let u = c.url else {
|
||||
throw SpikeError(description: "bad URL for \(path)")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func data(_ request: URLRequest) async throws -> Data {
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
let code = (response as! HTTPURLResponse).statusCode
|
||||
if !(200..<300).contains(code) {
|
||||
throw SpikeError(description: "\(code) \(String(decoding: data, as: UTF8.self))")
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func get<T: Decodable>(_ type: T.Type, _ path: String, _ query: [URLQueryItem] = []) async throws -> T {
|
||||
try JSONDecoder().decode(T.self, from: try await data(URLRequest(url: try url(path, query))))
|
||||
}
|
||||
|
||||
func connect() async {
|
||||
guard let c = URLComponents(string: origin), c.scheme == "https", let host = c.host, !host.isEmpty, c.path.isEmpty || c.path == "/", c.url != nil else {
|
||||
base = nil
|
||||
status = "Server URL must be an https origin with no path, such as https://host.ts.net:8270"
|
||||
return
|
||||
}
|
||||
base = c
|
||||
UserDefaults.standard.set(origin, forKey: "byre.server")
|
||||
do {
|
||||
var all: [TaskRow] = []
|
||||
for p in try await get([ProjectRow].self, "/api/projects") {
|
||||
all += try await get([TaskRow].self, "/api/projects/\(p.id)/tasks")
|
||||
}
|
||||
tasks = all.filter { $0.status == "active" } + all.filter { $0.status != "active" }
|
||||
if !tasks.contains(where: { $0.id == taskId }) {
|
||||
taskId = tasks.first?.id ?? ""
|
||||
}
|
||||
status = "\(tasks.count) tasks"
|
||||
} catch {
|
||||
status = "\(error)"
|
||||
}
|
||||
}
|
||||
|
||||
func upload(_ image: Data) async throws -> String {
|
||||
var req = URLRequest(url: try url("/api/blobs"))
|
||||
req.httpMethod = "POST"
|
||||
req.httpBody = image
|
||||
return try JSONDecoder().decode(BlobRow.self, from: try await data(req)).id
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user