How User Missions Are Tracked in TCG Pocket Collection Tracker: JSON Structure and Storage Logic
The TCG Pocket Collection Tracker tracks user missions through a hybrid system that combines runtime calculation of card ownership with persistent manual completion flags stored in the completed_missions array of the user account record.
The application helps players manage their Pokémon TCG Pocket collections and monitor progress toward themed mission objectives. Understanding how the system tracks mission completion requires examining both the JSON schema that defines mission requirements and the dual-state storage mechanism that handles automatic and manual progress tracking.
How User Missions Are Tracked
The application employs a dual-layer approach to determine mission completion status. This system balances automatic detection based on card ownership with user-controlled manual overrides.
Persistent Storage of Manual Completions
Manual completion states are stored in the user account record within the Supabase database. The AccountRow interface in src/types/index.ts defines this storage:
completed_missions?: string[] // Line 34-35
Each mission is identified by a unique key formatted as {expansionId}_{mission.name}, such as A1_First Choice. When users click the check-mark icon in the Mission component, the application updates this array via the useUpdateAccount mutation:
const handleManualComplete = () => {
const currentCompletedMissions = account.completed_missions || []
const updatedMissions = isManuallyCompleted
? currentCompletedMissions.filter(m => m !== missionKey)
: [...currentCompletedMissions, missionKey]
const mergedAccount = { ...account, completed_missions: updatedMissions }
delete mergedAccount.trade_rarity_settings
updateAccount(mergedAccount)
}
This code appears in src/components/Mission.tsx (lines 96-104) and ensures that manual completions persist across sessions.
Runtime Calculation of Mission Progress
In addition to manual flags, the application calculates mission completion dynamically based on the user's current card collection. The mission definitions are loaded from JSON files in assets/themed-collections/ and attached to expansion objects in src/lib/CardsDB.ts.
The Mission component evaluates progress by iterating over mission.requiredCards and checking each options array against owned cards via the useCollection hook. The completion logic combines both calculated and manual states:
mission.completed = isMissionCompleted || isManuallyCompleted
This line in src/components/Mission.tsx (line 83) produces the final boolean that drives UI filtering and visual indicators.
JSON Structure for Mission Objectives and Rewards
Mission definitions follow a strict JSON schema stored in expansion-specific files such as frontend/assets/themed-collections/A1-missions.json. Each mission object contains the following properties:
{
"expansionId": "A1",
"name": "First Choice",
"requiredCards": [
{ "options": ["A1-1", "A1-227"], "amount": 1 },
{ "options": ["A1-33", "A1-230"], "amount": 1 },
{ "options": ["A1-53", "A1-232"], "amount": 1 }
],
"reward": "Wonder Hourglass ×12<br />Shop Ticket ×2<br />Emblem Ticket (Genetic Apex) ×1"
}
The TypeScript interfaces in src/types/index.ts formalize this structure:
export interface Mission {
name: string
requiredCards: MissionCard[]
expansionId: ExpansionId
reward?: string
completed?: boolean
}
export interface MissionCard {
amount: number
options: string[]
owned?: number
}
Key characteristics of this schema include:
- Flexible card requirements: The
optionsarray allows missions to accept any one of several card IDs, supporting variants and promos. - Quantitative thresholds: The
amountfield specifies exactly how many cards from the options list are required. - Runtime enrichment: The
ownedandcompletedproperties are populated dynamically and never serialized to the JSON files.
Filtering Missions by Completion Status
The Missions page in src/pages/collection/Missions.tsx provides filtering capabilities that leverage both the calculated and manual completion states. The filtering logic (lines 32-44) checks the completed_missions array alongside the runtime mission.completed flag:
if (ownedFilter === 'owned') {
missions = missions.filter(m => {
const key = `${m.expansionId}_${m.name}`
const manual = account?.completed_missions?.includes(key) ?? false
return m.completed || manual
})
} else if (ownedFilter === 'missing') {
missions = missions.filter(m => {
const key = `${m.expansionId}_${m.name}`
const manual = account?.completed_missions?.includes(key) ?? false
return !m.completed && !manual
})
}
This dual-check ensures that users see missions they have completed either by owning the required cards or by manually marking them as done.
Summary
- The TCG Pocket Collection Tracker tracks user missions through a hybrid system combining runtime card ownership calculation with persistent manual completion flags.
- Manual completions are stored in the
completed_missionsstring array within the user account record, using keys formatted as{expansionId}_{mission.name}. - Mission objectives and rewards are defined in static JSON files (e.g.,
assets/themed-collections/A1-missions.json) following a strict schema withrequiredCards,options, andamountfields. - The
Missioncomponent insrc/components/Mission.tsxcalculates completion by checking owned cards against required options and merges this with manual flags to produce the final state. - The
Missionspage filters results by checking both the runtimemission.completedflag and thecompleted_missionsarray to display owned, missing, or all missions.
Frequently Asked Questions
How does the TCG Pocket Collection Tracker determine if a mission is completed?
The application uses a dual-layer approach. First, it calculates completion dynamically by checking if the user owns the required cards specified in the mission's requiredCards array. Second, it checks the completed_missions array stored in the user's account record for manual completion flags. If either condition is true, the mission is considered complete.
What is the format of the mission key stored in the completed_missions array?
Mission keys follow the pattern {expansionId}_{mission.name}. For example, a mission named "First Choice" from expansion "A1" would have the key A1_First Choice. This string is added to or removed from the completed_missions array when users toggle the check-mark icon in the mission UI.
Can missions require specific alternative cards or variants?
Yes, the JSON structure supports flexible requirements through the options array within each requiredCards entry. Each requirement can list multiple card IDs in the options array, and the user needs to own the specified amount of cards from that list. This design accommodates variant cards, promos, or alternative rarities that can satisfy the same mission slot.
Where are the mission definitions stored in the codebase?
Static mission definitions reside in JSON files within the assets/themed-collections/ directory (e.g., A1-missions.json). These files are imported by src/lib/CardsDB.ts and attached to their respective Expansion objects. The TypeScript interfaces defining the schema are located in src/types/index.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →