mirror of
https://github.com/R0m1k3/xtremobile.git
synced 2026-10-11 17:29:26 +02:00
feat(live): refresh the channel list on demand and on resume
Live categories and channels were fetched once per app session and kept in memory until the process died, so a TV box left running never picked up provider changes. - Live tab: pull-to-refresh plus a remote-friendly refresh button, and an automatic refetch when the app returns to the foreground after 4 hours. - Settings: the "refresh cache" entry was a no-op delay; it now forces a reload of live channels, movies, series and EPG. - The Xtream service keeps the last non-empty live lists and serves them when a refetch fails, so a refresh on a not-yet-connected network does not blank the screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9ce707a702
commit
60c7f4cece
6 files changed
+396
-241
No files matched your search
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
// import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
|
||||
import 'package:xtremobile/mobile/providers/mobile_settings_providers.dart';
|
||||
import 'package:xtremobile/mobile/providers/mobile_xtream_providers.dart';
|
||||
import 'package:xtremobile/mobile/widgets/tv_focusable.dart';
|
||||
import 'package:xtremobile/core/theme/app_decorations.dart';
|
||||
import 'package:xtremobile/core/theme/app_theme.dart';
|
||||
@@ -106,7 +107,7 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Version 1.6.1',
|
||||
'Version 1.7.2',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 13,
|
||||
color: AppDecorations.textSecondary(context),
|
||||
@@ -179,8 +180,7 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
_buildSettingItem(
|
||||
icon: Icons.public,
|
||||
title: 'EPG communautaire',
|
||||
subtitle:
|
||||
'Repli quand le fournisseur ne renvoie aucun programme',
|
||||
subtitle: 'Repli quand le fournisseur ne renvoie aucun programme',
|
||||
value: settings.useCommunityEpg ? 'Activé' : 'Désactivé',
|
||||
onTap: () => ref
|
||||
.read(mobileSettingsProvider.notifier)
|
||||
@@ -197,9 +197,8 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
onTap: () => _showKeyboardDialog(
|
||||
'URL XMLTV',
|
||||
_xmltvController,
|
||||
(val) => ref
|
||||
.read(mobileSettingsProvider.notifier)
|
||||
.setXmltvUrl(val),
|
||||
(val) =>
|
||||
ref.read(mobileSettingsProvider.notifier).setXmltvUrl(val),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -254,8 +253,8 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
|
||||
_buildSettingItem(
|
||||
icon: _isRefreshingCache ? Icons.hourglass_empty : Icons.refresh,
|
||||
title: 'Actualiser le cache',
|
||||
subtitle: 'Recharge films, séries et EPG',
|
||||
title: 'Forcer l\'actualisation',
|
||||
subtitle: 'Recharge chaînes, films, séries et EPG',
|
||||
value: _isRefreshingCache ? '...' : 'Appuyer',
|
||||
onTap: _isRefreshingCache ? null : _refreshCache,
|
||||
),
|
||||
@@ -319,15 +318,15 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
setState(() => _isRefreshingCache = true);
|
||||
|
||||
try {
|
||||
// final dir = await getApplicationDocumentsDirectory();
|
||||
// final cacheStore = HiveCacheStore(dir.path);
|
||||
// await cacheStore.clean();
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
refreshFullCatalog(ref);
|
||||
// Brief pause so the tap visibly registers on a TV remote.
|
||||
await Future.delayed(const Duration(milliseconds: 600));
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Cache actualisé ! Relancez l\'app pour recharger.'),
|
||||
content:
|
||||
Text('Chaînes, films, séries et EPG en cours de rechargement'),
|
||||
backgroundColor: AppColors.success,
|
||||
duration: Duration(seconds: 3),
|
||||
),
|
||||
@@ -336,7 +335,8 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Erreur: $e'), backgroundColor: AppColors.error),
|
||||
SnackBar(
|
||||
content: Text('Erreur: $e'), backgroundColor: AppColors.error),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@@ -390,54 +390,60 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
builder: (ctx, constraints) {
|
||||
return Row(
|
||||
children: segments.map((seg) {
|
||||
final (mode, icon, label) = seg;
|
||||
final selected = themeState.appThemeMode == mode;
|
||||
final accentColor = Theme.of(context).colorScheme.primary;
|
||||
return Expanded(
|
||||
child: TVFocusable(
|
||||
onPressed: () => notifier.setThemeMode(mode),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 7,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: selected
|
||||
? accentColor
|
||||
: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: selected
|
||||
? AppColors.onSurface
|
||||
: AppDecorations.textSecondary(context),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: selected
|
||||
? FontWeight.w700
|
||||
: FontWeight.w500,
|
||||
final (mode, icon, label) = seg;
|
||||
final selected = themeState.appThemeMode == mode;
|
||||
final accentColor =
|
||||
Theme.of(context).colorScheme.primary;
|
||||
return Expanded(
|
||||
child: TVFocusable(
|
||||
onPressed: () => notifier.setThemeMode(mode),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
margin:
|
||||
const EdgeInsets.symmetric(horizontal: 2),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 7,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: selected
|
||||
? accentColor
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: selected
|
||||
? AppColors.onSurface
|
||||
: AppDecorations.textSecondary(context),
|
||||
: AppDecorations.textSecondary(
|
||||
context),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: selected
|
||||
? FontWeight.w700
|
||||
: FontWeight.w500,
|
||||
color: selected
|
||||
? AppColors.onSurface
|
||||
: AppDecorations.textSecondary(
|
||||
context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -483,7 +489,8 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: Theme.of(context).colorScheme.primary, size: 24),
|
||||
Icon(icon,
|
||||
color: Theme.of(context).colorScheme.primary, size: 24),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -514,7 +521,10 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.15),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
@@ -562,7 +572,8 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: Theme.of(context).colorScheme.primary, size: 24),
|
||||
Icon(icon,
|
||||
color: Theme.of(context).colorScheme.primary, size: 24),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -592,7 +603,8 @@ class _MobileSettingsTabState extends ConsumerState<MobileSettingsTab> {
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(Icons.edit, color: AppDecorations.textSecondary(context), size: 20),
|
||||
Icon(Icons.edit,
|
||||
color: AppDecorations.textSecondary(context), size: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user