From b35f1b432fc24fbab1fa05593c9a0bca7e4d95a6 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Thu, 23 Apr 2026 23:12:34 -0500 Subject: feat(v3.6.0): add iCloud sharing for workflows and tracked domains - implement CloudKit sharing (CKShare) for TrackedDomain and DomainWorkflow - support read-only and editable permissions - add Share actions and shared state indicators in UI - handle shared record ownership and participant roles - implement deterministic conflict resolution to prevent duplication - ensure compatibility with existing iCloud sync layer - maintain local-first behavior with graceful offline handling - remove debug logging for domain availability/rdap messages notes: - no custom backend or accounts introduced - sharing is Apple ecosystem only (iCloud-based) - large history data remains local-only --- DomainDig/WorkflowsView.swift | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'DomainDig/WorkflowsView.swift') diff --git a/DomainDig/WorkflowsView.swift b/DomainDig/WorkflowsView.swift index 5025a0b..06ae96b 100644 --- a/DomainDig/WorkflowsView.swift +++ b/DomainDig/WorkflowsView.swift @@ -129,6 +129,11 @@ private struct WorkflowRowView: View { var body: some View { VStack(alignment: .leading, spacing: appDensity.metrics.rowSpacing + 1) { HStack(alignment: .firstTextBaseline, spacing: 8) { + if workflow.collaboration?.isShared == true { + Image(systemName: "person.2.fill") + .font(.caption2) + .foregroundStyle(.cyan) + } Text(workflow.name) .font(appDensity.font(.callout, design: .default, weight: .semibold)) .foregroundStyle(.primary) @@ -143,6 +148,12 @@ private struct WorkflowRowView: View { .font(appDensity.font(.caption2)) .foregroundStyle(.secondary) + if let collaboration = workflow.collaboration, collaboration.isShared { + Text("\(collaboration.ownership.title) • \(collaboration.permission.title)") + .font(appDensity.font(.caption2)) + .foregroundStyle(.secondary) + } + if let notes = workflow.notes, !notes.isEmpty { Text(notes) .font(appDensity.font(.caption)) @@ -163,6 +174,7 @@ struct WorkflowDetailView: View { @State private var showingEditor = false @State private var draftDomain = "" @State private var includeAllExports = false + @State private var shareEntity: ShareableEntity? private var workflow: DomainWorkflow? { viewModel.workflow(withID: workflowID) @@ -186,6 +198,11 @@ struct WorkflowDetailView: View { .font(appDensity.font(.caption)) .foregroundStyle(.secondary) } + if let collaboration = workflow.collaboration, collaboration.isShared { + Text("\(collaboration.ownership.title) • \(collaboration.permission.title)") + .font(appDensity.font(.caption)) + .foregroundStyle(.secondary) + } } Section("Actions") { @@ -207,6 +224,13 @@ struct WorkflowDetailView: View { } label: { Label("Edit Workflow", systemImage: "pencil") } + .disabled(!viewModel.canEdit(workflow)) + + Button { + shareEntity = .workflow(workflow.id) + } label: { + Label(workflow.collaboration?.isShared == true ? "Manage Share" : "Share Workflow", systemImage: "person.2") + } if latestSummary != nil { Toggle("Export all domains", isOn: $includeAllExports) @@ -263,6 +287,7 @@ struct WorkflowDetailView: View { viewModel.addDomains([draftDomain], to: workflow) draftDomain = "" } + .disabled(!viewModel.canEdit(workflow)) } } @@ -288,9 +313,11 @@ struct WorkflowDetailView: View { .buttonStyle(.plain) } .onDelete { offsets in + guard viewModel.canEdit(workflow) else { return } viewModel.removeWorkflowDomains(at: offsets, from: workflow) } .onMove { offsets, destination in + guard viewModel.canEdit(workflow) else { return } viewModel.moveWorkflowDomains(from: offsets, to: destination, in: workflow) } } @@ -302,6 +329,7 @@ struct WorkflowDetailView: View { .toolbar { if !workflow.domains.isEmpty { EditButton() + .disabled(!viewModel.canEdit(workflow)) } } .sheet(isPresented: $showingEditor) { @@ -310,6 +338,9 @@ struct WorkflowDetailView: View { .sheet(item: workflowSummaryBinding) { summary in WorkflowRunSummaryView(viewModel: viewModel, summary: summary) } + .sheet(item: $shareEntity) { entity in + CloudSharingSheet(entity: entity, title: workflow.name) + } } else { Text("Workflow not found") .font(appDensity.font(.callout)) -- cgit v1.2.3