crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

JoiningPuzzleView.swift (2394B)


      1 import SwiftUI
      2 
      3 /// The placeholder shown the instant a share link is tapped, while the share
      4 /// is being accepted and its zone fetched. When the link carried a grid
      5 /// silhouette it paints that grid greyed-out — so the user immediately sees
      6 /// the shape of the puzzle they're joining instead of an empty screen — with a
      7 /// spinner underneath. With no silhouette it falls back to the spinner alone.
      8 struct JoiningPuzzleView: View {
      9     let shape: GridSilhouette.Grid?
     10 
     11     /// Seconds the view has been on screen. The join has no observable stages
     12     /// to drive the message (only coarse start/complete signals exist), so the
     13     /// text advances purely on elapsed time to reassure the user that work is
     14     /// still happening rather than stalled.
     15     @State private var elapsed = 0
     16 
     17     private var message: String {
     18         switch elapsed {
     19         case ..<5:  return "Joining puzzle…"
     20         case ..<10: return "Syncing with iCloud…"
     21         case ..<15: return "Accepting invitation…"
     22         default:    return "Waiting for response…"
     23         }
     24     }
     25 
     26     var body: some View {
     27         ZStack {
     28             Color(.systemBackground).ignoresSafeArea()
     29 
     30             VStack(spacing: 28) {
     31                 if let shape {
     32                     GridThumbnailView(
     33                         width: shape.side,
     34                         height: shape.side,
     35                         // Open cells render grey (not white) to read as
     36                         // "not editable yet" rather than a playable grid.
     37                         cells: shape.blocks.map { $0 ? .block : .filled },
     38                         size: 220
     39                     )
     40                     .opacity(0.55)
     41                     .accessibilityHidden(true)
     42                 }
     43 
     44                 VStack(spacing: 12) {
     45                     ProgressView()
     46                     Text(message)
     47                         .font(.headline)
     48                         .foregroundStyle(.secondary)
     49                         .animation(.default, value: message)
     50                 }
     51             }
     52         }
     53         .accessibilityElement(children: .combine)
     54         .accessibilityLabel("Joining puzzle")
     55         .task {
     56             // Cancelled automatically when the placeholder clears on join.
     57             while !Task.isCancelled {
     58                 try? await Task.sleep(for: .seconds(1))
     59                 elapsed += 1
     60             }
     61         }
     62     }
     63 }