Phase 2: Published Practice Rounds
Date: December 28, 2025 PRs: #410 (main implementation), #422 (deferred cleanup) Status: Complete
Overview
Phase 2 enables users to publish their completed practice rounds to the global leaderboard. This extends the leaderboard system (established in Phase 1 for tournaments) to include individual practice sessions.
Key Features
Round Publishing
- Users can publish completed (non-tournament) rounds to the global leaderboard
- Eligibility checks prevent publishing incomplete or tournament rounds
- “Published” indicator displays on rounds after publishing
Visibility Options
Three visibility levels give users control over their data:
- Public - Visible to all users, appears on global leaderboard
- Authenticated Only - Visible only to signed-in users
- Private - Visible only to the owner
My Published Rounds
- Dedicated screen accessible from Settings
- View all previously published rounds
- Unpublish rounds to remove from leaderboard
Technical Implementation
Data Layer
New Domain Models:
PublishedRoundStatusenum - Tracks publishing statePublishedRound- Domain model for published round data
New Room Entity:
PublishedRoundReference- Local tracking of published rounds- Database version bumped to 39
Firebase Integration
FirebasePublishingDataSource:
- Atomic batch writes to ensure consistency
- Writes to both
publishedRoundsandleaderboardscollections simultaneously - Composite index for efficient user queries
Firestore Collections:
publishedRounds/
{publishedRoundId}/
userId: string
localRoundId: number
score: number
arrowCount: number
visibility: "PUBLIC" | "AUTHENTICATED_ONLY" | "PRIVATE"
status: "PENDING" | "PUBLISHED" | "REJECTED" | "WITHDRAWN"
verificationLevel: "SELF_REPORTED" | "IMAGE_RECOGNITION" | "WITNESS_VERIFIED" | "TOURNAMENT"
...
leaderboards/
{entryId}/
userId: string
score: number
conditionKey: string
sourceType: "TOURNAMENT" | "PRACTICE_PUBLISHED"
visibility: string
...
Android Implementation
Services:
RoundPublishingService- Handles eligibility checks and publishing logic
UI Components:
PublishRoundDialog- Visibility selector dialogMyPublishedRoundsScreen- List of published rounds with unpublish action- Publish button in
RoundDetailsScreen
Navigation:
- Settings → My Published Rounds
iOS Implementation
Services:
RoundPublishingRepositoryBridge- Swift bridge to Firebase
UI Components:
PublishRoundSheet- SwiftUI sheet for visibility selectionMyPublishedRoundsView- List of published rounds- Publish button in
RoundDetailView
Navigation:
- Settings → My Published Rounds
Phase 2 Deferred Cleanup (PR #422)
Firestore Security Rules
Added create validation rules:
- Type validation:
score is int,arrowCount is int - Enum validation for
visibility,status,verificationLevel,sourceType
New Helper Functions:
function isValidVisibility(visibility) {
return visibility in ['PUBLIC', 'AUTHENTICATED_ONLY', 'PRIVATE'];
}
function isValidPublishedRoundStatus(status) {
return status in ['PENDING', 'PUBLISHED', 'REJECTED', 'WITHDRAWN'];
}
function isValidVerificationLevel(level) {
return level in ['SELF_REPORTED', 'IMAGE_RECOGNITION', 'WITNESS_VERIFIED', 'TOURNAMENT'];
}
function isValidScoreSourceType(sourceType) {
return sourceType in ['TOURNAMENT', 'PRACTICE_PUBLISHED'];
}Batch Operation Cleanup
Refactored FirebasePublishingDataSource.publishRound():
- Create document refs first to get IDs upfront
- Build complete documents with all IDs included
- Single
batch.setper document (removed redundantbatch.updatecalls)
Security Rules Test Suite
- 26 test cases for Firestore security rules
- Uses
@firebase/rules-unit-testingpackage - Covers create validation, read access, admin operations
- See Firestore Rules Testing Guide
Testing Performed
- Complete a practice round on Android
- Publish with Public visibility → verify in Firebase Console
- Check “Published” indicator shows after publishing
- Navigate to Settings → My Published Rounds
- Unpublish → verify removed from leaderboards collection
- Repeat on iOS
- Verify tournament rounds do NOT show publish button
- Verify incomplete rounds do NOT show publish button
Lessons Learned
-
Atomic Batch Writes: Using Firebase batch operations ensures both
publishedRoundsandleaderboardsdocuments are created atomically, preventing orphaned records. -
Enum Validation at Rules Level: Moving enum validation to Firestore security rules provides server-side enforcement, not just client-side validation.
-
Visibility Model: The three-tier visibility model (Public, Authenticated Only, Private) provides good granularity for user privacy preferences.
Related Documentation
- Firestore Rules Testing Guide - How to test security rules
- Global Admin System - Admin moderation (Phase 5)
- Verification System - Score verification (Phase 3)