fix: update watch state properly
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../client/plex_client.dart';
|
||||||
|
import '../models/plex_metadata.dart';
|
||||||
|
|
||||||
|
/// Mixin for screens that need to update individual items after watch state changes
|
||||||
|
///
|
||||||
|
/// This provides a standard implementation for fetching updated metadata
|
||||||
|
/// and replacing items in lists, while allowing each screen to customize
|
||||||
|
/// which lists should be updated.
|
||||||
|
mixin ItemUpdatable<T extends StatefulWidget> on State<T> {
|
||||||
|
/// The Plex client to use for fetching updated metadata
|
||||||
|
/// Each screen must provide access to their client
|
||||||
|
PlexClient get client;
|
||||||
|
|
||||||
|
/// Updates a single item in the screen's list(s) after watch state changes
|
||||||
|
///
|
||||||
|
/// Fetches the latest metadata with images (including clearLogo) and
|
||||||
|
/// calls [updateItemInLists] to update the appropriate list(s).
|
||||||
|
///
|
||||||
|
/// If the fetch fails, the error is silently caught and the item will
|
||||||
|
/// be updated on the next full refresh.
|
||||||
|
Future<void> updateItem(String ratingKey) async {
|
||||||
|
try {
|
||||||
|
final updatedMetadata = await client.getMetadataWithImages(ratingKey);
|
||||||
|
if (updatedMetadata != null) {
|
||||||
|
setState(() {
|
||||||
|
updateItemInLists(ratingKey, updatedMetadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Silently fail - the item will update on next full refresh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Override this method to specify which list(s) should be updated
|
||||||
|
///
|
||||||
|
/// This method is called within [setState], so you should directly
|
||||||
|
/// modify your list(s) without calling setState again.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// @override
|
||||||
|
/// void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata) {
|
||||||
|
/// final index = _items.indexWhere((item) => item.ratingKey == ratingKey);
|
||||||
|
/// if (index != -1) {
|
||||||
|
/// _items[index] = updatedMetadata;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata);
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import '../widgets/media_card.dart';
|
|||||||
import '../widgets/desktop_app_bar.dart';
|
import '../widgets/desktop_app_bar.dart';
|
||||||
import '../widgets/server_list_tile.dart';
|
import '../widgets/server_list_tile.dart';
|
||||||
import '../mixins/refreshable.dart';
|
import '../mixins/refreshable.dart';
|
||||||
|
import '../mixins/item_updatable.dart';
|
||||||
import '../utils/app_logger.dart';
|
import '../utils/app_logger.dart';
|
||||||
import 'video_player_screen.dart';
|
import 'video_player_screen.dart';
|
||||||
import 'main_screen.dart';
|
import 'main_screen.dart';
|
||||||
@@ -32,7 +33,10 @@ class DiscoverScreen extends StatefulWidget {
|
|||||||
State<DiscoverScreen> createState() => _DiscoverScreenState();
|
State<DiscoverScreen> createState() => _DiscoverScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DiscoverScreenState extends State<DiscoverScreen> with Refreshable {
|
class _DiscoverScreenState extends State<DiscoverScreen> with Refreshable, ItemUpdatable {
|
||||||
|
@override
|
||||||
|
PlexClient get client => widget.client;
|
||||||
|
|
||||||
List<PlexMetadata> _onDeck = [];
|
List<PlexMetadata> _onDeck = [];
|
||||||
List<PlexMetadata> _recentlyAdded = [];
|
List<PlexMetadata> _recentlyAdded = [];
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
@@ -110,6 +114,21 @@ class _DiscoverScreenState extends State<DiscoverScreen> with Refreshable {
|
|||||||
_loadContent();
|
_loadContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata) {
|
||||||
|
// Check and update in _onDeck list
|
||||||
|
final onDeckIndex = _onDeck.indexWhere((item) => item.ratingKey == ratingKey);
|
||||||
|
if (onDeckIndex != -1) {
|
||||||
|
_onDeck[onDeckIndex] = updatedMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and update in _recentlyAdded list
|
||||||
|
final recentlyAddedIndex = _recentlyAdded.indexWhere((item) => item.ratingKey == ratingKey);
|
||||||
|
if (recentlyAddedIndex != -1) {
|
||||||
|
_recentlyAdded[recentlyAddedIndex] = updatedMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _handleSwitchServer() async {
|
Future<void> _handleSwitchServer() async {
|
||||||
final storage = await StorageService.getInstance();
|
final storage = await StorageService.getInstance();
|
||||||
final plexToken = storage.getPlexToken();
|
final plexToken = storage.getPlexToken();
|
||||||
@@ -808,11 +827,12 @@ class _DiscoverScreenState extends State<DiscoverScreen> with Refreshable {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
child: MediaCard(
|
child: MediaCard(
|
||||||
|
key: Key(item.ratingKey),
|
||||||
client: widget.client,
|
client: widget.client,
|
||||||
item: item,
|
item: item,
|
||||||
width: cardWidth,
|
width: cardWidth,
|
||||||
height: cardHeight,
|
height: cardHeight,
|
||||||
onRefresh: _loadContent,
|
onRefresh: updateItem,
|
||||||
userProfile: widget.userProfile,
|
userProfile: widget.userProfile,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import '../widgets/desktop_app_bar.dart';
|
|||||||
import '../widgets/app_bar_back_button.dart';
|
import '../widgets/app_bar_back_button.dart';
|
||||||
import '../services/storage_service.dart';
|
import '../services/storage_service.dart';
|
||||||
import '../mixins/refreshable.dart';
|
import '../mixins/refreshable.dart';
|
||||||
|
import '../mixins/item_updatable.dart';
|
||||||
|
|
||||||
class LibrariesScreen extends StatefulWidget {
|
class LibrariesScreen extends StatefulWidget {
|
||||||
final PlexClient client;
|
final PlexClient client;
|
||||||
@@ -20,7 +21,10 @@ class LibrariesScreen extends StatefulWidget {
|
|||||||
State<LibrariesScreen> createState() => _LibrariesScreenState();
|
State<LibrariesScreen> createState() => _LibrariesScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LibrariesScreenState extends State<LibrariesScreen> with Refreshable {
|
class _LibrariesScreenState extends State<LibrariesScreen> with Refreshable, ItemUpdatable {
|
||||||
|
@override
|
||||||
|
PlexClient get client => widget.client;
|
||||||
|
|
||||||
List<PlexLibrary> _libraries = [];
|
List<PlexLibrary> _libraries = [];
|
||||||
List<PlexMetadata> _items = [];
|
List<PlexMetadata> _items = [];
|
||||||
List<PlexFilter> _filters = [];
|
List<PlexFilter> _filters = [];
|
||||||
@@ -167,6 +171,14 @@ class _LibrariesScreenState extends State<LibrariesScreen> with Refreshable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata) {
|
||||||
|
final index = _items.indexWhere((item) => item.ratingKey == ratingKey);
|
||||||
|
if (index != -1) {
|
||||||
|
_items[index] = updatedMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Public method to refresh content
|
// Public method to refresh content
|
||||||
@override
|
@override
|
||||||
void refresh() {
|
void refresh() {
|
||||||
@@ -374,9 +386,10 @@ class _LibrariesScreenState extends State<LibrariesScreen> with Refreshable {
|
|||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
final item = _items[index];
|
final item = _items[index];
|
||||||
return MediaCard(
|
return MediaCard(
|
||||||
|
key: Key(item.ratingKey),
|
||||||
client: widget.client,
|
client: widget.client,
|
||||||
item: item,
|
item: item,
|
||||||
onRefresh: _applyFilters,
|
onRefresh: updateItem,
|
||||||
userProfile: widget.userProfile,
|
userProfile: widget.userProfile,
|
||||||
);
|
);
|
||||||
}, childCount: _items.length),
|
}, childCount: _items.length),
|
||||||
|
|||||||
@@ -32,13 +32,22 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
|
|||||||
PlexMetadata? _fullMetadata;
|
PlexMetadata? _fullMetadata;
|
||||||
PlexMetadata? _onDeckEpisode;
|
PlexMetadata? _onDeckEpisode;
|
||||||
bool _isLoadingMetadata = true;
|
bool _isLoadingMetadata = true;
|
||||||
|
late final ScrollController _scrollController;
|
||||||
|
bool _watchStateChanged = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_scrollController = ScrollController();
|
||||||
_loadFullMetadata();
|
_loadFullMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadFullMetadata() async {
|
Future<void> _loadFullMetadata() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingMetadata = true;
|
_isLoadingMetadata = true;
|
||||||
@@ -108,6 +117,33 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update watch state without full screen rebuild
|
||||||
|
/// This preserves scroll position and only updates watch-related data
|
||||||
|
Future<void> _updateWatchState() async {
|
||||||
|
try {
|
||||||
|
final metadata = await widget.client.getMetadataWithImages(widget.metadata.ratingKey);
|
||||||
|
|
||||||
|
if (metadata != null) {
|
||||||
|
// For shows, also refetch seasons to update their watch counts
|
||||||
|
List<PlexMetadata>? updatedSeasons;
|
||||||
|
if (metadata.type.toLowerCase() == 'show') {
|
||||||
|
updatedSeasons = await widget.client.getChildren(widget.metadata.ratingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single setState to minimize rebuilds - scroll position is preserved by controller
|
||||||
|
setState(() {
|
||||||
|
_fullMetadata = metadata;
|
||||||
|
if (updatedSeasons != null) {
|
||||||
|
_seasons = updatedSeasons;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
appLogger.e('Failed to update watch state', error: e);
|
||||||
|
// Silently fail - user can manually refresh if needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _playFirstEpisode() async {
|
Future<void> _playFirstEpisode() async {
|
||||||
try {
|
try {
|
||||||
// If seasons aren't loaded yet, wait for them or load them
|
// If seasons aren't loaded yet, wait for them or load them
|
||||||
@@ -192,13 +228,15 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
// Hero header with background art
|
// Hero header with background art
|
||||||
DesktopSliverAppBar(
|
DesktopSliverAppBar(
|
||||||
expandedHeight: headerHeight,
|
expandedHeight: headerHeight,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
leading: const AppBarBackButton(
|
leading: AppBarBackButton(
|
||||||
style: BackButtonStyle.circular,
|
style: BackButtonStyle.circular,
|
||||||
|
onPressed: () => Navigator.pop(context, _watchStateChanged),
|
||||||
),
|
),
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background: Stack(
|
background: Stack(
|
||||||
@@ -495,13 +533,14 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
|
|||||||
metadata.ratingKey,
|
metadata.ratingKey,
|
||||||
);
|
);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
_watchStateChanged = true;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('Marked as watched'),
|
content: Text('Marked as watched'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
// Refresh metadata to update UI
|
// Update watch state without full rebuild
|
||||||
_loadFullMetadata();
|
_updateWatchState();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
@@ -527,13 +566,14 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
|
|||||||
metadata.ratingKey,
|
metadata.ratingKey,
|
||||||
);
|
);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
_watchStateChanged = true;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('Marked as unwatched'),
|
content: Text('Marked as unwatched'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
// Refresh metadata to update UI
|
// Update watch state without full rebuild
|
||||||
_loadFullMetadata();
|
_updateWatchState();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
@@ -642,15 +682,22 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
|
|||||||
child: MediaContextMenu(
|
child: MediaContextMenu(
|
||||||
client: widget.client,
|
client: widget.client,
|
||||||
metadata: season,
|
metadata: season,
|
||||||
onRefresh: _loadFullMetadata,
|
onRefresh: (ratingKey) {
|
||||||
onTap: () {
|
_watchStateChanged = true;
|
||||||
Navigator.push(
|
_updateWatchState();
|
||||||
|
},
|
||||||
|
onTap: () async {
|
||||||
|
final watchStateChanged = await Navigator.push<bool>(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
SeasonDetailScreen(client: widget.client, season: season),
|
SeasonDetailScreen(client: widget.client, season: season),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
if (watchStateChanged == true) {
|
||||||
|
_watchStateChanged = true;
|
||||||
|
_updateWatchState();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../models/plex_user_profile.dart';
|
|||||||
import '../widgets/media_card.dart';
|
import '../widgets/media_card.dart';
|
||||||
import '../widgets/desktop_app_bar.dart';
|
import '../widgets/desktop_app_bar.dart';
|
||||||
import '../mixins/refreshable.dart';
|
import '../mixins/refreshable.dart';
|
||||||
|
import '../mixins/item_updatable.dart';
|
||||||
|
|
||||||
class SearchScreen extends StatefulWidget {
|
class SearchScreen extends StatefulWidget {
|
||||||
final PlexClient client;
|
final PlexClient client;
|
||||||
@@ -17,7 +18,10 @@ class SearchScreen extends StatefulWidget {
|
|||||||
State<SearchScreen> createState() => _SearchScreenState();
|
State<SearchScreen> createState() => _SearchScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SearchScreenState extends State<SearchScreen> with Refreshable {
|
class _SearchScreenState extends State<SearchScreen> with Refreshable, ItemUpdatable {
|
||||||
|
@override
|
||||||
|
PlexClient get client => widget.client;
|
||||||
|
|
||||||
final _searchController = TextEditingController();
|
final _searchController = TextEditingController();
|
||||||
List<PlexMetadata> _searchResults = [];
|
List<PlexMetadata> _searchResults = [];
|
||||||
bool _isSearching = false;
|
bool _isSearching = false;
|
||||||
@@ -101,6 +105,14 @@ class _SearchScreenState extends State<SearchScreen> with Refreshable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata) {
|
||||||
|
final index = _searchResults.indexWhere((item) => item.ratingKey == ratingKey);
|
||||||
|
if (index != -1) {
|
||||||
|
_searchResults[index] = updatedMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -196,9 +208,10 @@ class _SearchScreenState extends State<SearchScreen> with Refreshable {
|
|||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
final item = _searchResults[index];
|
final item = _searchResults[index];
|
||||||
return MediaCard(
|
return MediaCard(
|
||||||
|
key: Key(item.ratingKey),
|
||||||
client: widget.client,
|
client: widget.client,
|
||||||
item: item,
|
item: item,
|
||||||
onRefresh: refresh,
|
onRefresh: updateItem,
|
||||||
userProfile: widget.userProfile,
|
userProfile: widget.userProfile,
|
||||||
);
|
);
|
||||||
}, childCount: _searchResults.length),
|
}, childCount: _searchResults.length),
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import '../models/plex_metadata.dart';
|
|||||||
import '../models/plex_user_profile.dart';
|
import '../models/plex_user_profile.dart';
|
||||||
import '../widgets/desktop_app_bar.dart';
|
import '../widgets/desktop_app_bar.dart';
|
||||||
import '../widgets/app_bar_back_button.dart';
|
import '../widgets/app_bar_back_button.dart';
|
||||||
|
import '../widgets/media_context_menu.dart';
|
||||||
|
import '../mixins/item_updatable.dart';
|
||||||
import 'video_player_screen.dart';
|
import 'video_player_screen.dart';
|
||||||
|
|
||||||
class SeasonDetailScreen extends StatefulWidget {
|
class SeasonDetailScreen extends StatefulWidget {
|
||||||
@@ -23,9 +25,13 @@ class SeasonDetailScreen extends StatefulWidget {
|
|||||||
State<SeasonDetailScreen> createState() => _SeasonDetailScreenState();
|
State<SeasonDetailScreen> createState() => _SeasonDetailScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SeasonDetailScreenState extends State<SeasonDetailScreen> {
|
class _SeasonDetailScreenState extends State<SeasonDetailScreen> with ItemUpdatable {
|
||||||
|
@override
|
||||||
|
PlexClient get client => widget.client;
|
||||||
|
|
||||||
List<PlexMetadata> _episodes = [];
|
List<PlexMetadata> _episodes = [];
|
||||||
bool _isLoadingEpisodes = false;
|
bool _isLoadingEpisodes = false;
|
||||||
|
bool _watchStateChanged = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -51,6 +57,20 @@ class _SeasonDetailScreenState extends State<SeasonDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateItem(String ratingKey) async {
|
||||||
|
_watchStateChanged = true;
|
||||||
|
await super.updateItem(ratingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata) {
|
||||||
|
final index = _episodes.indexWhere((item) => item.ratingKey == ratingKey);
|
||||||
|
if (index != -1) {
|
||||||
|
_episodes[index] = updatedMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -59,8 +79,9 @@ class _SeasonDetailScreenState extends State<SeasonDetailScreen> {
|
|||||||
DesktopSliverAppBar(
|
DesktopSliverAppBar(
|
||||||
title: Text(widget.season.title),
|
title: Text(widget.season.title),
|
||||||
pinned: true,
|
pinned: true,
|
||||||
leading: const AppBarBackButton(
|
leading: AppBarBackButton(
|
||||||
style: BackButtonStyle.circular,
|
style: BackButtonStyle.circular,
|
||||||
|
onPressed: () => Navigator.pop(context, _watchStateChanged),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_isLoadingEpisodes)
|
if (_isLoadingEpisodes)
|
||||||
@@ -117,23 +138,27 @@ class _SeasonDetailScreenState extends State<SeasonDetailScreen> {
|
|||||||
? episode.viewOffset! / episode.duration!
|
? episode.viewOffset! / episode.duration!
|
||||||
: 0.0;
|
: 0.0;
|
||||||
|
|
||||||
return Card(
|
return MediaContextMenu(
|
||||||
clipBehavior: Clip.antiAlias,
|
client: widget.client,
|
||||||
child: InkWell(
|
metadata: episode,
|
||||||
onTap: () async {
|
onRefresh: updateItem,
|
||||||
await Navigator.push(
|
onTap: () async {
|
||||||
context,
|
await Navigator.push(
|
||||||
MaterialPageRoute(
|
context,
|
||||||
builder: (context) => VideoPlayerScreen(
|
MaterialPageRoute(
|
||||||
client: widget.client,
|
builder: (context) => VideoPlayerScreen(
|
||||||
metadata: episode,
|
client: widget.client,
|
||||||
userProfile: widget.userProfile,
|
metadata: episode,
|
||||||
),
|
userProfile: widget.userProfile,
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
// Refresh episodes when returning from video player
|
);
|
||||||
_loadEpisodes();
|
// Refresh episodes when returning from video player
|
||||||
},
|
_loadEpisodes();
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
key: Key(episode.ratingKey),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class MediaCard extends StatefulWidget {
|
|||||||
final PlexMetadata item;
|
final PlexMetadata item;
|
||||||
final double? width;
|
final double? width;
|
||||||
final double? height;
|
final double? height;
|
||||||
final VoidCallback? onRefresh;
|
final void Function(String ratingKey)? onRefresh;
|
||||||
final PlexUserProfile? userProfile;
|
final PlexUserProfile? userProfile;
|
||||||
|
|
||||||
const MediaCard({
|
const MediaCard({
|
||||||
@@ -48,7 +48,7 @@ class _MediaCardState extends State<MediaCard> {
|
|||||||
);
|
);
|
||||||
// Refresh parent screen if result indicates it's needed
|
// Refresh parent screen if result indicates it's needed
|
||||||
if (result == true) {
|
if (result == true) {
|
||||||
widget.onRefresh?.call();
|
widget.onRefresh?.call(widget.item.ratingKey);
|
||||||
}
|
}
|
||||||
} else if (itemType == 'season') {
|
} else if (itemType == 'season') {
|
||||||
// For seasons, show season detail screen
|
// For seasons, show season detail screen
|
||||||
@@ -63,7 +63,7 @@ class _MediaCardState extends State<MediaCard> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
// Season screen doesn't return a refresh flag, but we can refresh anyway
|
// Season screen doesn't return a refresh flag, but we can refresh anyway
|
||||||
widget.onRefresh?.call();
|
widget.onRefresh?.call(widget.item.ratingKey);
|
||||||
} else {
|
} else {
|
||||||
// For all other types (shows, movies), show detail screen
|
// For all other types (shows, movies), show detail screen
|
||||||
final result = await Navigator.push<bool>(
|
final result = await Navigator.push<bool>(
|
||||||
@@ -78,7 +78,7 @@ class _MediaCardState extends State<MediaCard> {
|
|||||||
);
|
);
|
||||||
// Refresh parent screen if result indicates it's needed
|
// Refresh parent screen if result indicates it's needed
|
||||||
if (result == true) {
|
if (result == true) {
|
||||||
widget.onRefresh?.call();
|
widget.onRefresh?.call(widget.item.ratingKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class _MenuAction {
|
|||||||
class MediaContextMenu extends StatefulWidget {
|
class MediaContextMenu extends StatefulWidget {
|
||||||
final PlexClient client;
|
final PlexClient client;
|
||||||
final PlexMetadata metadata;
|
final PlexMetadata metadata;
|
||||||
final VoidCallback? onRefresh;
|
final void Function(String ratingKey)? onRefresh;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ class _MediaContextMenuState extends State<MediaContextMenu> {
|
|||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(successMessage)),
|
SnackBar(content: Text(successMessage)),
|
||||||
);
|
);
|
||||||
widget.onRefresh?.call();
|
widget.onRefresh?.call(widget.metadata.ratingKey);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
@@ -255,7 +255,7 @@ class _MediaContextMenuState extends State<MediaContextMenu> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) => screenBuilder(metadata)),
|
MaterialPageRoute(builder: (context) => screenBuilder(metadata)),
|
||||||
);
|
);
|
||||||
widget.onRefresh?.call();
|
widget.onRefresh?.call(widget.metadata.ratingKey);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
|||||||
Reference in New Issue
Block a user