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 options array allows missions to accept any one of several card IDs, supporting variants and promos.
  • Quantitative thresholds: The amount field specifies exactly how many cards from the options list are required.
  • Runtime enrichment: The owned and completed properties 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_missions string 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 with requiredCards, options, and amount fields.
  • The Mission component in src/components/Mission.tsx calculates completion by checking owned cards against required options and merges this with manual flags to produce the final state.
  • The Missions page filters results by checking both the runtime mission.completed flag and the completed_missions array 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →