Building A Watchlist Screen For Your Anime App

Alex Johnson
-
Building A Watchlist Screen For Your Anime App

Hey guys! Let's dive into building a new screen for your anime app. This screen will help you keep track of what you're currently watching, making it super easy to manage your anime journey. We'll be pulling data, implementing filters, and adding some cool features to make it awesome. Ready?

Screen Overview

We're adding a new screen to manage what you're watching. This will include shows that are currently airing, as well as older shows that don't have a set broadcast schedule. This will help you keep track of your progress and manage all of your currently watching anime. Let's check out the main parts:

Main Menu Updates

The main menu, also known as the drawer menu, will have these changes:

  1. Broadcast Schedule: Renamed from "Track" screen to make things clearer.
  2. Watching: This is the new screen we're building!
  3. History: This might be renamed from the "History" screen. We'll need to confirm the name. The name can be discussed later on.
  4. Settings: This stays the same.

Watching Screen: Features

Let's break down what we'll need for this new screen. We'll look at where the data comes from, how to filter and sort it, and how to display it nicely. We are going to display it in a card view style!

Data Sources

  • API: We'll use the viewer.libraryEntries(states: [WATCHING]) from the API to get the data.
  • Data We'll Get: We'll grab the following details:
    • Anime Info: Title, season, year, and media type.
    • Next Episode: Episode number, title, and display text.
    • Images: We'll use recommendedImageUrl or facebookOgImageUrl for the visuals.

Filtering Options

  • Radio Buttons:
    • [Past Shows Only ✓] (Default): Shows only shows not available in viewer.programs.
    • [All Shows]: Shows all anime currently being watched, including airing anime.
  • Existing Filters: We'll reuse filters for season, year, and media type.

Sorting the List

  • Sort by the last watched date and time (newest first): We will need to confirm if this is possible using the Annict API (libraryEntries.updatedAt).

How the Cards Look

Here's how each anime card will be displayed:

┌─────────────────────────────────┐
│ 📺 [Image]  Heavenly Delusion      │
│            Spring 2023 TV         │
│            Next: Episode 9        │
│            "Children of the Academy" │
│            📍 Netflix            │
└─────────────────────────────────┘

What Happens When You Tap

  • Tapping a Card: Takes you to the AnimeDetailScreen.
  • Data Structure: We'll pass data using a structure like ProgramWithWork, which is a similar new structure to LibraryEntryWithWork.

Episode Tracking (Local Database)

Let's talk about how we'll handle episode counts. This can get tricky, so let's go through the issues and how we can solve them. We will solve them step by step!

Challenges

  • Annict Data: The episodesCount in Annict isn't always final before a show finishes.
  • MyAnimeList Data: While num_episodes in MyAnimeList is accurate, getting all the data for all shows can put a lot of stress on the API.

The Solution: Gradual Information Updates

Here's our plan for getting the episode counts right:

【First time loading - list screen】
  Heavenly Delusion: Next 9/? (Unconfirmed)  ← Annict episodesCount
  Fruits Basket: Next 4/12  ← Annict episodesCount (Finished airing)

【Go to detail screen】
  → Get num_episodes=13 from MyAnimeList API
  → Save to local database: { workId, episodeCount: 13, source: "MAL" }

【Go back to list screen】
  Heavenly Delusion: Next 9/13  ← Get from DB (Unconfirmed disappears)

How It Works

  1. List Screen: We'll show Annict's episodesCount.
  2. "Unconfirmed" Label: We'll add "(Unconfirmed)" if the show hasn't finished airing.
  3. Detail Screen: When we get data from MyAnimeList, we'll save it to the database.
  4. List Screen Priority: The list screen will use the database first, then Annict.

Platform Management

It can be tricky to remember where you're watching each anime, so this feature will help a lot.

The Goal

  • Let users set the platform where they're watching each show.

User Interface (Option A: In a Modal)

This is how we can design the UI in the modal:

Rename: Rename DetailModal to EpisodeRecordModal.

Add a new feature for "Watching" screen: Platform setting UI.

┌─────────────────────────────────┐
│ Watching Platform             │
├─────────────────────────────────┤
│ ⚪ Netflix                      │
│ ⚪ Amazon Prime Video           │
│ ⚪ Disney+                      │
│ ⚪ U-NEXT                       │
│ ● dAnime Store                │
│ ⚪ Not set                      │
│                                 │
│ ┌─────────────────────────┐    │
│ │ + Custom Platform       │    │
│ │   [Text Input]          │    │
│ │   [Add Button]          │    │
│ └─────────────────────────┘    │
└─────────────────────────────────┘

Where Platforms Come From

  1. Annict: Get from work.programs.channel.
  2. Custom: Users can add their own platforms (manage this in the settings).
  3. Not Set: The default state.

Implementation Details

@Composable
fun PlatformSelector(
    selectedPlatform: String?,
    platforms: List<String>, // Annict + custom
    onSelect: (String?) -> Unit,
    onAddCustom: (String) -> Unit
) {
    Column {
        // Radio button list
        platforms.forEach { platform ->
            RadioButton(...)
        }
        
        // Custom addition (collapsible)
        var showCustomInput by remember { mutableStateOf(false) }
        if (showCustomInput) {
            OutlinedTextField(...)
            Button("Add") { onAddCustom(...) }
        } else {
            TextButton("+ Add Custom Platform") {
                showCustomInput = true
            }
        }
    }
}

Local Database Design (Room)

We'll use Room for our local database. Here are the tables we'll need:

1. work_episode_cache (Episode Count Cache)

@Entity(tableName = "work_episode_cache")
data class WorkEpisodeCache(
    @PrimaryKey val workId: String,
    val episodeCount: Int,
    val source: String, // "ANNICT" or "MAL"
    val updatedAt: Long
)

2. work_platform_preferences (Platform Settings)

@Entity(tableName = "work_platform_preferences")
data class WorkPlatformPreference(
    @PrimaryKey val workId: String,
    val platformName: String?, // null = Not set
    val updatedAt: Long
)

3. custom_platforms (Custom Platforms)

@Entity(tableName = "custom_platforms")
data class CustomPlatform(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val name: String,
    val createdAt: Long
)

Task List (Estimated)

Here's a rough idea of what we need to do:

  • Rename: Rename "Track" screen to "Broadcast Schedule".
  • Rename (Check): Rename "History" screen to "Record History".
  • Rename: Rename DetailModal to EpisodeRecordModal.
  • GraphQL Query: Add the viewer.libraryEntries query.
  • Implement: Implement the Repository/UseCase.
  • Implement: Implement the ViewModel.
  • UI: Build the UI for the "Watching" screen.
    • Radio button filters
    • Card display
    • Sorting
  • UI: Implement the platform settings UI.
    • EpisodeRecordModal changes
    • Radio button selection
    • Custom addition UI (collapsible)
  • Local Database: Implement the local database (Room).
    • Entity definitions
    • DAO implementation
    • Migration
  • Episode Count Logic: Implement episode count logic.
    • Database priority display
    • Update the database after getting data from MAL.
    • Logic for "(Unconfirmed)" label
  • Navigation: Add navigation.
  • Testing: Implement tests.
    • UnitTest
    • IntegrationTest
    • UITest

Important Notes

  • We'll start implementing this after the AnimeDetailScreen is reviewed and merged.
  • We need to check if we can get the last watched date and time from the Annict API.
  • We need to figure out how to determine if a show has finished airing (e.g., checking the work.status field).

I hope you enjoyed this article. If you want to learn more about this topic, I recommend checking out this website: MyAnimeList.

You may also like