feat: Ipad app production readiness, Colony orchestration, Social posting (#44)
All checks were successful
Production Readiness / backend-contracts (push) Successful in 1m47s
Production Readiness / webos-typecheck (push) Successful in 1m50s
Production Readiness / ipad-parse (push) Successful in 1m34s

#38 Ipad app production readiness, Colony orchestration, Social posting

Co-authored-by: Sayan Datta <sayan@Sayans-MacBook-Air.local>
Reviewed-on: #44
This commit was merged in pull request #44.
This commit is contained in:
2026-05-03 18:30:38 +05:30
parent 59d398abc3
commit eeb684b46c
86 changed files with 20349 additions and 1655 deletions

View File

@@ -0,0 +1,130 @@
import SwiftUI
import UIKit
struct VelocityVaultShareAsset {
let leadId: String?
let assetName: String
let assetType: String
let storagePath: String?
var isShareable: Bool {
leadId?.trimmedNonEmpty != nil && storagePath?.trimmedNonEmpty != nil
}
}
extension URL {
var velocityStoragePath: String {
let cleaned = path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
if cleaned.hasPrefix("assets/") {
return String(cleaned.dropFirst("assets/".count))
}
return cleaned
}
}
extension View {
func vaultSwipeToShare(asset: VelocityVaultShareAsset?) -> some View {
modifier(VaultSwipeToShareModifier(asset: asset))
}
}
private struct VaultSwipeToShareModifier: ViewModifier {
@State private var appStore = AppStore.shared
let asset: VelocityVaultShareAsset?
func body(content: Content) -> some View {
content
.overlay {
ThreeFingerSwipeUpRecognizer {
Task { await shareAsset() }
}
.allowsHitTesting(asset != nil)
}
}
@MainActor
private func shareAsset() async {
guard let asset else { return }
guard let leadId = asset.leadId?.trimmedNonEmpty,
let storagePath = asset.storagePath?.trimmedNonEmpty else {
withAnimation(.interactiveSpring(response: 0.35, dampingFraction: 0.86)) {
appStore.vaultShareError = "Vault share requires a backend lead and stored asset path."
appStore.vaultShareMessage = nil
}
return
}
guard let threadId = appStore.activeCommunicationsThreadID?.trimmedNonEmpty else {
withAnimation(.interactiveSpring(response: 0.35, dampingFraction: 0.86)) {
appStore.vaultShareError = "Open a Communications thread before using Vault Swipe-to-Share."
appStore.vaultShareMessage = nil
}
return
}
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
do {
let link = try await VelocityAPIClient.shared.generateVaultLink(
leadId: leadId,
assetName: asset.assetName,
assetType: asset.assetType,
storagePath: storagePath
)
_ = try await VelocityAPIClient.shared.sendCommsMessage(
threadId: threadId,
body: "Secure Velocity Vault link: \(link.vaultUrl)"
)
withAnimation(.interactiveSpring(response: 0.42, dampingFraction: 0.82)) {
appStore.vaultShareMessage = "Vault link shared to the active thread."
appStore.vaultShareError = nil
}
} catch {
withAnimation(.interactiveSpring(response: 0.35, dampingFraction: 0.86)) {
appStore.vaultShareError = error.localizedDescription
appStore.vaultShareMessage = nil
}
}
}
}
private struct ThreeFingerSwipeUpRecognizer: UIViewRepresentable {
let onSwipe: () -> Void
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)
view.backgroundColor = .clear
let recognizer = UISwipeGestureRecognizer(
target: context.coordinator,
action: #selector(Coordinator.didSwipe(_:))
)
recognizer.direction = .up
recognizer.numberOfTouchesRequired = 3
view.addGestureRecognizer(recognizer)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(onSwipe: onSwipe)
}
final class Coordinator: NSObject {
let onSwipe: () -> Void
init(onSwipe: @escaping () -> Void) {
self.onSwipe = onSwipe
}
@objc func didSwipe(_ recognizer: UISwipeGestureRecognizer) {
guard recognizer.state == .ended else { return }
onSwipe()
}
}
}
private extension String {
var trimmedNonEmpty: String? {
let value = trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
}
}