import SwiftUI struct SharedMember: Decodable { let ID: UInt64 let Address: String } struct SharedPerson: Decodable, Identifiable { let Member: SharedMember let Role: String var id: UInt64 { Member.ID } } struct SharedLink: Decodable, Identifiable { let ID: UInt64 let Role: String var id: UInt64 { self.ID } } struct SharingPage: Decodable { let People: [SharedPerson]? let Links: [SharedLink]? let NewLink: String let Notice: String } struct SheetSharing: View { @Bindable var model: SheetModel @State private var sharing: SharingPage? @State private var address = "" @State private var role = "viewer" @State private var linkRole = "viewer" var body: some View { Group { Text("People you share with can see your team’s name.").font(.callout) TextField("Email address", text: $address) Picker("Access", selection: $role) { ForEach(["viewer", "commenter", "editor", "owner"], id: \.self) { Text($0.capitalized).tag($0) } } Button("Grant access") { Task { await model.command("/share", ["address": address, "role": role]) await load() } }.disabled(address.isEmpty) ForEach(sharing?.People ?? []) { person in LabeledContent(person.Member.Address, value: person.Role) Button("Remove access", role: .destructive) { Task { await model.command("/share/revoke", ["member": String(person.id)]) await load() } } } Picker("Link access", selection: $linkRole) { Text("Viewer").tag("viewer") Text("Commenter").tag("commenter") } Button("Create link") { Task { do { sharing = try await model.form(model.base + "/share/link", ["role": linkRole]) } catch { model.error = error.localizedDescription } } } if let sharing, !sharing.NewLink.isEmpty { Text(sharing.NewLink).textSelection(.enabled) ShareLink(item: sharing.NewLink) Text("Copy this link now. It is shown only once.").font(.caption) } ForEach(sharing?.Links ?? []) { link in Button("Revoke \(link.Role) link \(link.ID)", role: .destructive) { Task { await model.command("/share/link/revoke", ["link": String(link.ID)]) await load() } } } Text(model.notice).font(.callout) }.task { await load() } } private func load() async { do { sharing = try await model.client.request(model.base + "/share") } catch { model.error = error.localizedDescription } } }