Goals


Firestore Data Model

/leagues/{leagueId}

/drafts/{draftId}
  status: "pending" | "active" | "complete"
  leagueId: string
  createdBy: uid
  rounds: number
  scheduledAt:Timestamp|null
  pickSeconds: number         // per-pick clock
  startedAt: Timestamp|null
  completedAt: Timestamp|null

  // snake state
  order: string[]             // array of userIds in base order (slot 0..N-1)
  current: {
    round: number             // 1-based
    slotIndex: number         // 0..N-1
    direction: 1 | -1         // 1 forward, -1 backward
    onClockUserId: string
    pickDeadline: Timestamp    // server-set deadline per pick
    pickNumber: number         // 1..(rounds * order.length)
  }

  // optional flags
  allowAutoPick: boolean
  pauseReason?: string

/drafts/{draftId}/participants/{uid}
  displayName: string
  photoURL?: string
  joinedAt: Timestamp
  isConnected: boolean
  lastSeen: Timestamp
  // optional user prerank queue for auto-pick:
  prerank: string[]           // ordered list of playerIds

/drafts/{draftId}/picks/{pickNumber}
  pickNumber: number
  round: number
  slotIndex: number
  userId: string
  playerId: string
  madeAt: Timestamp

/drafts/{draftId}/playerPool/{playerId}
  name: string
  team?: string
  // availability
  takenBy: string|null        // uid
  takenAt: Timestamp|null

Why playerPool under each draft? It lets you enforce “not already taken” atomically and query quickly without touching global players.


Realtime Presence

Use Realtime Database for presence (best-practice with Firebase). Cloud Function mirrors presence to Firestore participant docs.

This gives accurate online state without chatty Firestore writes.


Security Rules (Firestore)

Key invariants: