diff --git a/lib/models/livetv_channel.dart b/lib/models/livetv_channel.dart index c32f1b1c..212e89b5 100644 --- a/lib/models/livetv_channel.dart +++ b/lib/models/livetv_channel.dart @@ -31,6 +31,21 @@ String favoriteChannelKey(String source, String id) => '$source\u0000$id'; String liveTvChannelScopeKey(LiveTvChannel channel) => '${channel.serverId ?? ''}\u0000${channel.liveDvrKey ?? ''}\u0000${channel.key}'; +List filterLiveTvChannelsForFavorites({ + required List channels, + required bool favoritesOnly, + required Iterable favorites, + required String Function(LiveTvChannel channel) sourceForChannel, +}) { + if (!favoritesOnly || favorites.isEmpty) return channels; + + final channelMap = { + for (final channel in channels) favoriteChannelKey(sourceForChannel(channel), channel.key): channel, + }; + + return [for (final favorite in favorites) ?channelMap[favorite.stableKey]]; +} + @JsonSerializable(createToJson: false) class LiveTvChannel with MultiServerFields { @JsonKey(readValue: _readChannelKey) diff --git a/lib/screens/livetv/live_tv_screen.dart b/lib/screens/livetv/live_tv_screen.dart index 32fd395f..4a5cce41 100644 --- a/lib/screens/livetv/live_tv_screen.dart +++ b/lib/screens/livetv/live_tv_screen.dart @@ -71,15 +71,12 @@ class _LiveTvScreenState extends State final Map _favoriteStoreBySource = {}; final Map _favoriteModeByStore = {}; - List get _filteredChannels { - if (!_showFavoritesOnly) return _channels; - if (_favoriteKeys.isEmpty) return const []; - final channelMap = {for (final c in _channels) _favoriteKeyForChannel(c): c}; - return [ - for (final fav in _favoriteChannels) - if (channelMap.containsKey(fav.stableKey)) channelMap[fav.stableKey]!, - ]; - } + List get _filteredChannels => filterLiveTvChannelsForFavorites( + channels: _channels, + favoritesOnly: _showFavoritesOnly, + favorites: _favoriteChannels, + sourceForChannel: _sourceForChannel, + ); String _liveServerScopeKey(LiveTvServerInfo serverInfo) => '${serverInfo.serverId}\u0000${serverInfo.dvrKey}'; diff --git a/test/models/livetv_channel_test.dart b/test/models/livetv_channel_test.dart index 97396288..ef8d0b82 100644 --- a/test/models/livetv_channel_test.dart +++ b/test/models/livetv_channel_test.dart @@ -16,4 +16,35 @@ void main() { expect(liveTvChannelScopeKey(a), isNot(liveTvChannelScopeKey(b))); }); + + test('favorite filtering falls back to all channels when no favorites are loaded', () { + final channels = [LiveTvChannel(key: '101'), LiveTvChannel(key: '102')]; + + final filtered = filterLiveTvChannelsForFavorites( + channels: channels, + favoritesOnly: true, + favorites: const [], + sourceForChannel: (_) => 'server://server-1/provider-a', + ); + + expect(filtered, same(channels)); + }); + + test('favorite filtering preserves favorite order and source scope', () { + final channels = [LiveTvChannel(key: '101'), LiveTvChannel(key: '102'), LiveTvChannel(key: '101')]; + const sourceA = 'server://server-1/provider-a'; + const sourceB = 'server://server-2/provider-a'; + + final filtered = filterLiveTvChannelsForFavorites( + channels: channels, + favoritesOnly: true, + favorites: [ + FavoriteChannel(source: sourceB, id: '101'), + FavoriteChannel(source: sourceA, id: '102'), + ], + sourceForChannel: (channel) => identical(channel, channels[2]) ? sourceB : sourceA, + ); + + expect(filtered, [channels[2], channels[1]]); + }); }