keys-for-all/docs/components/CommunityPool.md
2025-07-22 18:27:21 -07:00

13 KiB

CommunityPool Component

The CommunityPool component manages the donation and distribution of license keys within the VoiceUwu community.

Pseudocode

class CommunityPoolManager {
    // CloudKit container for community features
    private let container = CKContainer(identifier: "iCloud.com.voiceuwu.community")
    private let publicDB: CKDatabase
    
    // Local cache
    @Published private(set) var poolStatus: PoolStatus?
    @Published private(set) var userRequests: [KeyRequest] = []
    @Published private(set) var isLoading = false
    
    init() {
        self.publicDB = container.publicCloudDatabase
        loadPoolStatus()
    }
    
    // MARK: - Pool Status
    
    struct PoolStatus {
        let totalKeys: Int
        let level1Keys: Int
        let level2Keys: Int
        let recentDonations: [DonationInfo]
        let lastUpdated: Date
    }
    
    func loadPoolStatus() async {
        isLoading = true
        defer { isLoading = false }
        
        do {
            // Query pool statistics
            let query = CKQuery(
                recordType: "PoolStatus",
                predicate: NSPredicate(value: true)
            )
            
            let results = try await publicDB.records(matching: query)
            
            if let record = results.matchResults.first?.1.get() {
                poolStatus = PoolStatus(
                    totalKeys: record["totalKeys"] as? Int ?? 0,
                    level1Keys: record["level1Keys"] as? Int ?? 0,
                    level2Keys: record["level2Keys"] as? Int ?? 0,
                    recentDonations: parseRecentDonations(record),
                    lastUpdated: record.modificationDate ?? Date()
                )
            }
        } catch {
            print("Failed to load pool status: \(error)")
        }
    }
    
    // MARK: - Donation System
    
    struct KeyDonation {
        let keys: [String]
        let donorName: String?
        let message: String?
        let isAnonymous: Bool
    }
    
    func donateKeys(_ donation: KeyDonation) async throws {
        // Validate keys before donation
        for key in donation.keys {
            guard KeyValidator().validate(key).isValid else {
                throw DonationError.invalidKey(key)
            }
        }
        
        // Create donation records
        for key in donation.keys {
            let record = CKRecord(recordType: "KeyDonation")
            
            // Store encrypted key
            record["encryptedKey"] = encrypt(key)
            record["keyLevel"] = extractLevel(from: key).rawValue
            record["donationDate"] = Date()
            
            // Donor info (anonymous if requested)
            if !donation.isAnonymous {
                record["donorName"] = donation.donorName ?? "Unknown"
            }
            record["donorHash"] = generateDonorHash()
            
            // Optional message
            if let message = donation.message {
                record["message"] = sanitizeMessage(message)
            }
            
            try await publicDB.save(record)
        }
        
        // Remove keys from local inventory
        KeyManager.shared.removeFromInventory(donation.keys)
        
        // Update local cache
        await loadPoolStatus()
    }
    
    // MARK: - Request System
    
    struct KeyRequest {
        let id: String
        let category: RequestCategory
        let reason: String
        let requestDate: Date
        let status: RequestStatus
        let level: LicenseLevel
    }
    
    enum RequestCategory: String, CaseIterable {
        case student = "student"
        case educator = "educator"
        case financial = "financial"
        case contributor = "contributor"
        
        var displayName: String {
            switch self {
            case .student: return "Student"
            case .educator: return "Educator"
            case .financial: return "Financial Need"
            case .contributor: return "Open Source Contributor"
            }
        }
        
        var verificationRequired: Bool {
            switch self {
            case .student, .educator: return true
            case .financial, .contributor: return false
            }
        }
    }
    
    enum RequestStatus: String {
        case pending = "pending"
        case approved = "approved"
        case denied = "denied"
        case fulfilled = "fulfilled"
    }
    
    func submitRequest(
        category: RequestCategory,
        reason: String,
        level: LicenseLevel,
        verification: VerificationData?
    ) async throws {
        // Check request limits
        let existingRequests = try await loadUserRequests()
        
        // One request per 30 days
        if let lastRequest = existingRequests.last {
            let daysSinceLastRequest = Date().timeIntervalSince(lastRequest.requestDate) / 86400
            if daysSinceLastRequest < 30 {
                throw RequestError.tooSoon(daysRemaining: Int(30 - daysSinceLastRequest))
            }
        }
        
        // Create request record
        let record = CKRecord(recordType: "KeyRequest")
        record["deviceID"] = getAnonymousDeviceID()
        record["category"] = category.rawValue
        record["reason"] = sanitizeReason(reason)
        record["requestDate"] = Date()
        record["status"] = RequestStatus.pending.rawValue
        record["level"] = level.rawValue
        
        // Add verification if required
        if category.verificationRequired {
            guard let verification = verification else {
                throw RequestError.verificationRequired
            }
            record["verification"] = try encodeVerification(verification)
        }
        
        try await publicDB.save(record)
        
        // Reload requests
        userRequests = try await loadUserRequests()
    }
    
    // MARK: - Distribution Algorithm
    
    func checkForApprovedKeys() async throws -> String? {
        let deviceID = getAnonymousDeviceID()
        
        // Query for approved requests
        let predicate = NSPredicate(
            format: "deviceID == %@ AND status == %@",
            deviceID,
            RequestStatus.approved.rawValue
        )
        
        let query = CKQuery(recordType: "KeyRequest", predicate: predicate)
        let results = try await publicDB.records(matching: query)
        
        for (_, result) in results.matchResults {
            if case .success(let record) = result,
               let keyID = record["assignedKeyID"] as? String {
                
                // Fetch the actual key
                let key = try await fetchAssignedKey(keyID)
                
                // Mark as fulfilled
                record["status"] = RequestStatus.fulfilled.rawValue
                try await publicDB.save(record)
                
                return key
            }
        }
        
        return nil
    }
    
    private func fetchAssignedKey(_ keyID: String) async throws -> String {
        // Fetch encrypted key from secure storage
        let recordID = CKRecord.ID(recordName: keyID)
        let record = try await publicDB.record(for: recordID)
        
        guard let encryptedKey = record["encryptedKey"] as? String else {
            throw DistributionError.keyNotFound
        }
        
        return decrypt(encryptedKey)
    }
    
    // MARK: - Privacy & Security
    
    private func getAnonymousDeviceID() -> String {
        // Generate consistent anonymous ID
        if let existing = UserDefaults.standard.string(forKey: "anonymousDeviceID") {
            return existing
        }
        
        let newID = UUID().uuidString
        UserDefaults.standard.set(newID, forKey: "anonymousDeviceID")
        return newID
    }
    
    private func generateDonorHash() -> String {
        // One-way hash for donor tracking without identification
        let deviceID = getAnonymousDeviceID()
        let timestamp = Date().timeIntervalSince1970
        let combined = "\(deviceID)\(timestamp)"
        
        return SHA256.hash(data: Data(combined.utf8))
            .compactMap { String(format: "%02x", $0) }
            .joined()
            .prefix(8)
            .uppercased()
    }
    
    private func encrypt(_ key: String) -> String {
        // Simplified encryption for demonstration
        // In production, use proper encryption
        return Data(key.utf8).base64EncodedString()
    }
    
    private func decrypt(_ encrypted: String) -> String {
        // Simplified decryption for demonstration
        guard let data = Data(base64Encoded: encrypted),
              let key = String(data: data, encoding: .utf8) else {
            return ""
        }
        return key
    }
    
    private func sanitizeMessage(_ message: String) -> String {
        // Remove any potentially harmful content
        let cleaned = message
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: "[^a-zA-Z0-9 .,!?'-]", with: "", options: .regularExpression)
        
        return String(cleaned.prefix(200)) // Limit length
    }
}

// MARK: - UI Components

struct CommunityPoolView: View {
    @StateObject private var poolManager = CommunityPoolManager()
    @State private var selectedTab = 0
    
    var body: some View {
        VStack(spacing: 0) {
            // Pool status header
            if let status = poolManager.poolStatus {
                PoolStatusHeader(status: status)
            }
            
            // Tab selection
            Picker("View", selection: $selectedTab) {
                Text("Donate").tag(0)
                Text("Request").tag(1)
                Text("Activity").tag(2)
            }
            .pickerStyle(SegmentedPickerStyle())
            .padding()
            
            // Content
            ScrollView {
                switch selectedTab {
                case 0:
                    DonateKeysView()
                case 1:
                    RequestKeyView()
                case 2:
                    CommunityActivityView()
                default:
                    EmptyView()
                }
            }
        }
        .navigationTitle("Community Pool")
        .task {
            await poolManager.loadPoolStatus()
        }
    }
}

struct PoolStatusHeader: View {
    let status: CommunityPoolManager.PoolStatus
    
    var body: some View {
        VStack(spacing: 12) {
            Text("Community Pool")
                .font(.headline)
            
            HStack(spacing: 20) {
                VStack {
                    Text("\(status.totalKeys)")
                        .font(.title2)
                        .fontWeight(.bold)
                    Text("Total Keys")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                
                Divider()
                    .frame(height: 40)
                
                VStack {
                    Text("\(status.level1Keys)")
                        .font(.title3)
                        .foregroundColor(.blue)
                    Text("Level 1")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                
                VStack {
                    Text("\(status.level2Keys)")
                        .font(.title3)
                        .foregroundColor(.purple)
                    Text("Level 2")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
            }
            
            if !status.recentDonations.isEmpty {
                Text("Recent: \(status.recentDonations.first!.message)")
                    .font(.caption)
                    .foregroundColor(.secondary)
                    .lineLimit(1)
            }
        }
        .padding()
        .background(Color(.secondarySystemBackground))
        .cornerRadius(12)
        .padding()
    }
}

// MARK: - Error Types

enum DonationError: LocalizedError {
    case invalidKey(String)
    case donationFailed
    
    var errorDescription: String? {
        switch self {
        case .invalidKey(let key):
            return "Invalid key: \(key)"
        case .donationFailed:
            return "Failed to donate keys"
        }
    }
}

enum RequestError: LocalizedError {
    case tooSoon(daysRemaining: Int)
    case verificationRequired
    case requestFailed
    
    var errorDescription: String? {
        switch self {
        case .tooSoon(let days):
            return "Please wait \(days) more days before requesting again"
        case .verificationRequired:
            return "Verification required for this category"
        case .requestFailed:
            return "Failed to submit request"
        }
    }
}

Community Pool Features

  1. Anonymous Donations

    • No user tracking
    • Optional messages
    • Donor recognition (optional)
  2. Fair Distribution

    • Category-based priority
    • Request limits (30-day cooldown)
    • Verification for some categories
  3. Privacy First

    • Anonymous device IDs
    • Encrypted key storage
    • No personal data collection
  4. Community Building

    • Recent donation feed
    • Pool statistics
    • Success stories