Skip to content

feat(favorites): Add line filtering to favorite stops#8

Merged
Sloy merged 11 commits into
masterfrom
favorite-line-filtering
Dec 29, 2025
Merged

feat(favorites): Add line filtering to favorite stops#8
Sloy merged 11 commits into
masterfrom
favorite-line-filtering

Conversation

@Sloy

@Sloy Sloy commented Dec 29, 2025

Copy link
Copy Markdown
Owner

Summary

Adds the ability to filter favorite stops by specific bus lines. Users can now select which lines they want to see arrivals for on the home screen, reducing visual clutter and focusing on relevant information.

Changes

Database Layer

  • Added selectedLineIds column to FavoriteStopEntity (nullable List<LineId>)
  • Created IntListConverter for Room serialization (List ↔ comma-separated String)
  • Fixed bug in IntListConverter.deserialize() to handle empty strings

Domain Layer

  • Added selectedLineIds: Set<LineId>? to FavoriteStop domain model
  • Implemented isLineSelected() helper with three-state logic:
    • null → all lines selected (backward compatibility)
    • isEmpty() → no lines selected
    • contains(lineId) → specific lines selected
  • Created FavoriteStopEntity.fromEntity() extension for clean DTO mapping

UI - Edit Favorites Screen

  • Made line indicators interactive with toggle selection
  • Added visual feedback: selected (full opacity + checkmark badge), unselected (40% opacity)
  • Implemented smooth animations: spring-based alpha transition + bouncy checkmark scale
  • Added soft haptic feedback (CLOCK_TICK) on line toggle
  • Extracted SelectableLineIndicator composable for reusability
  • Increased spacing (8dp) between line indicators for better touch targets

UI - Home Screen (For You)

  • Filter arrivals based on selectedLineIds in FavoriteItemViewModel
  • Skip API polling entirely when no lines selected (performance optimization)
  • Show shimmer only for selected lines during loading
  • Update ViewModel key to include selectedLineIds (ensures UI updates on change)

API Integration

  • Added selectedLineIds: List<LineId>? to FavoriteStopDto
  • Updated all DTO ↔ Entity mappings to include the new field
  • Implemented server-wins sync strategy in RemoteAndLocalFavoriteRepository:
    • Server favorites replace local versions on conflict
    • Local-only favorites preserved and uploaded
    • Ensures line selections sync across devices

Analytics

  • Added EditFavoriteLineClicked(isSelected: Boolean) event

Testing

  • ✅ Line selections persist locally and sync to server
  • ✅ Empty selection handling (no crash, no API calls)
  • ✅ Backward compatibility (existing favorites with null show all lines)
  • ✅ ViewModel recreates when selections change (immediate UI update)
  • ✅ Sync properly updates favorites when server changes
  • ✅ Stop detail screen unaffected (still shows all lines)

Screenshots

Breaking Changes

None. Fully backward compatible with existing favorites.

Sloy added 7 commits December 29, 2025 12:58
This commit adds a new Product Requirement Document (PRD) that outlines the feature for allowing users to filter bus lines on their favorite stops.

The document details the goals, implementation plan, data model changes, UI/UX considerations, and success criteria for the feature.
This commit introduces the ability for users to filter which bus lines are displayed for each of their favorite stops.

Key Changes:
- A new `selectedLineIds` field has been added to the `FavoriteStop` model, as well as to the corresponding `FavoriteStopEntity` and `ApiFavoriteStop` data classes. This field stores a list of line IDs that the user wants to see for a specific favorite stop.
- A database migration has been added to increase the database version to 10 and add the new `selectedLineIds` column (of type `TEXT`) to the `favorites` table. Existing favorite stops will have a `NULL` value, which is interpreted as all lines being selected.
- The `FavoriteStop` domain model now includes an `isLineSelected` helper function to check if a specific line should be displayed based on the user's selection.
Implement Step 2 of line filtering feature - update domain models and repositories to handle line selections.

- Add FavoriteStopEntity.fromEntity() extension to centralize conversion
- Update LocalFavoriteRepository mappings to include selectedLineIds
- Update RemoteAndLocalFavoriteRepository mappings to include selectedLineIds
- Convert List<LineId>? (entity) to Set<LineId>? (domain) in all mappings
- Maintain backward compatibility with null meaning all lines selected
This commit introduces the ability for users to filter bus lines directly from the "Edit Favorites" screen. Users can now tap on line indicators to select or deselect which lines they want to see for each favorite stop.

Key Changes:
- A new `SelectableLineIndicator` composable has been created to handle the selection UI, including animations and a checkmark for selected lines.
- The `EditFavoriteListItem` now manages the state of selected lines and passes changes up to the `EditFavoritesScreen`.
- Haptic feedback is provided when a user selects or deselects a line.
- A new analytics event, `EditFavoriteLineClicked`, is tracked to monitor feature usage.
Implement Step 4 of line filtering feature - apply line selections to filter arrivals on home screen.

- Add filtering logic in FavoriteItemViewModel
  - Handle three cases: null (all lines), empty (no API call), specific lines (filter)
  - Early return for empty selection to skip unnecessary polling
  - Add filterBySelectedLines() helper extension function
- Update FavoriteListItem UI
  - Show only shimmer for selected lines during loading
  - Handle empty line selection state (no shimmer indicators)
- Fix ViewModel key to include selectedLineIds
  - Ensures ViewModel recreates when line selections change
  - Fixes issue where changes weren't reflected until app restart
Fix crash when deserializing empty selectedLineIds from database.

When selectedLineIds is an empty list, it gets serialized as empty string. The deserializer was trying to parse "" as integers, causing NumberFormatException and infinite crash loop.

- Add empty string check in IntListConverter.deserialize()
- Return empty list for empty string input
- Prevents crash when favorites have no lines selected
Fix sync logic to properly update favorites when fields change on server.

Previous logic only handled additions/removals, not updates. When selectedLineIds (or other fields) changed on the server, local versions were never updated.

New strategy (server wins):
- Replace all local favorites with server versions
- Keep local-only favorites and upload them to server
- Server version always takes precedence for conflicts

This ensures line selections, custom names, and custom icons sync properly across devices.
Copilot AI review requested due to automatic review settings December 29, 2025 18:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds line filtering functionality to favorite stops, allowing users to select which bus lines they want to monitor for each favorite. The feature includes database changes, domain model updates, interactive UI in the Edit Favorites screen, and server synchronization support while maintaining full backward compatibility.

Key Changes:

  • Added selectedLineIds field throughout the data stack (database, domain, API) with nullable type for backward compatibility
  • Implemented interactive line selection UI with visual feedback and animations in Edit Favorites screen
  • Filter arrivals and skip API calls based on selected lines on the home screen

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Clicks.kt Added analytics event for line selection toggles
DI.kt Injected Analytics dependency into EditFavoritesViewModel
EditFavoritesViewModel.kt Added analytics tracking method
EditFavoritesScreen.kt Implemented interactive line selection UI with animations and haptic feedback
FavoriteListItem.kt Updated ViewModel key and shimmer logic to respect line selections
FavoriteItemViewModel.kt Added filtering logic to skip API calls and filter arrivals by selected lines
FavoriteStop.kt Added selectedLineIds field and isLineSelected() helper method
RemoteAndLocalFavoriteRepository.kt Updated sync strategy to use server-wins approach and refactored mapping
LocalFavoriteRepository.kt Refactored to use new fromEntity() extension function
TypeConverters.kt Fixed bug in IntListConverter.deserialize() to handle empty strings
SevibusDatabase.kt Incremented database version and added AutoMigration
Entities.kt Added selectedLineIds field and created fromEntity() extension
ApiModels.kt Added selectedLineIds field to DTO
Stubs.kt Updated test data to include line selections
10.json Database schema for version 10
PRD_LINE_FILTERING.md Product requirements document

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/src/main/java/com/sloy/sevibus/feature/foryou/favorites/FavoriteListItem.kt Outdated
Comment thread app/src/main/java/com/sloy/sevibus/data/repository/LocalFavoriteRepository.kt Outdated
…teRepository.kt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 29, 2025 18:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/src/main/java/com/sloy/sevibus/feature/foryou/favorites/FavoriteListItem.kt Outdated
…voriteListItem.kt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 29, 2025 18:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/src/main/java/com/sloy/sevibus/feature/foryou/favorites/FavoriteListItem.kt Outdated
Comment on lines +242 to +244
var selectedLines by remember(favorite.selectedLineIds, favorite.stop.lines) {
mutableStateOf(favorite.selectedLineIds ?: favorite.stop.lines.map { it.id }.toSet())
}

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The selected lines state is not updated when favorite changes through onLineSelectionChanged callback. When the parent updates favoritesLocalList, the remember key includes favorite.selectedLineIds but favorite is a lambda parameter that refers to the original instance. This could cause the UI state to become out of sync with the updated favorite. Consider using LaunchedEffect to observe changes or restructure the state management.

Copilot uses AI. Check for mistakes.
…voriteListItem.kt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 29, 2025 18:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Fix state synchronization issue identified in PR review where local state could become out of sync with parent state.

Previous implementation maintained duplicate state (local mutableStateOf + parent list), causing potential desync when parent updates. Now derives selectedLines directly from favorite parameter, making parent the single source of truth.

- Remove local selectedLines mutableState
- Derive selectedLines from favorite.selectedLineIds
- Remove redundant local state assignment
- Simplifies state management and prevents sync bugs
@Sloy Sloy merged commit b130fa6 into master Dec 29, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants