refactor: estrai showUpdateDialog e parseTagName in file dedicati
Sposta la logica del dialog in lib/shared/widgets/update_dialog.dart per renderla importabile nei test senza duplicare il codice. Estrae parseTagName come metodo @visibleForTesting in UpdateService isolando il parsing JSON dalla chiamata HTTP. app.dart semplificato di conseguenza. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-31
@@ -5,7 +5,7 @@ import 'features/converter/presentation/pages/converter_page.dart';
|
|||||||
import 'features/shopping_list/presentation/pages/shopping_list_page.dart';
|
import 'features/shopping_list/presentation/pages/shopping_list_page.dart';
|
||||||
import 'features/guide/presentation/widgets/info_bottom_sheet.dart';
|
import 'features/guide/presentation/widgets/info_bottom_sheet.dart';
|
||||||
import 'shared/services/update_service.dart';
|
import 'shared/services/update_service.dart';
|
||||||
import 'shared/services/url_launcher_service.dart';
|
import 'shared/widgets/update_dialog.dart';
|
||||||
|
|
||||||
class BitePlanApp extends StatelessWidget {
|
class BitePlanApp extends StatelessWidget {
|
||||||
const BitePlanApp({super.key});
|
const BitePlanApp({super.key});
|
||||||
@@ -33,8 +33,6 @@ class _MainScaffoldState extends State<_MainScaffold> {
|
|||||||
late final List<Widget> _pages;
|
late final List<Widget> _pages;
|
||||||
|
|
||||||
static const _titles = ['Piano Pasti', 'Convertitore', 'Lista della spesa'];
|
static const _titles = ['Piano Pasti', 'Convertitore', 'Lista della spesa'];
|
||||||
static const _releasesUrl =
|
|
||||||
'https://github.com/davide3011/biteplan/releases/latest';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -48,38 +46,11 @@ class _MainScaffoldState extends State<_MainScaffold> {
|
|||||||
];
|
];
|
||||||
UpdateService.checkUpdate().then((newVersion) {
|
UpdateService.checkUpdate().then((newVersion) {
|
||||||
if (newVersion != null && mounted) {
|
if (newVersion != null && mounted) {
|
||||||
_showUpdateDialog(newVersion);
|
showUpdateDialog(context, newVersion);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showUpdateDialog(String newVersion) {
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (ctx) => AlertDialog(
|
|
||||||
icon: const Icon(Icons.system_update_outlined, size: 32),
|
|
||||||
title: const Text('Aggiornamento disponibile'),
|
|
||||||
content: Text(
|
|
||||||
'È disponibile la versione $newVersion di BitePlan.\n'
|
|
||||||
'Vuoi scaricare il nuovo APK?',
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(ctx).pop(),
|
|
||||||
child: const Text('Più tardi'),
|
|
||||||
),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(ctx).pop();
|
|
||||||
UrlLauncherService.launch(_releasesUrl);
|
|
||||||
},
|
|
||||||
child: const Text('Scarica'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|||||||
@@ -27,14 +27,24 @@ class UpdateService {
|
|||||||
final body = await response.transform(utf8.decoder).join();
|
final body = await response.transform(utf8.decoder).join();
|
||||||
client.close();
|
client.close();
|
||||||
if (response.statusCode != 200) return null;
|
if (response.statusCode != 200) return null;
|
||||||
final data = jsonDecode(body) as Map<String, dynamic>;
|
final tag = parseTagName(body);
|
||||||
final tag = (data['tag_name'] as String).replaceFirst(RegExp(r'^v'), '');
|
if (tag == null) return null;
|
||||||
return isNewer(tag, kAppVersion) ? tag : null;
|
return isNewer(tag, kAppVersion) ? tag : null;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
static String? parseTagName(String body) {
|
||||||
|
try {
|
||||||
|
final data = jsonDecode(body) as Map<String, dynamic>;
|
||||||
|
return (data['tag_name'] as String).replaceFirst(RegExp(r'^v'), '');
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static bool isNewer(String remote, String local) {
|
static bool isNewer(String remote, String local) {
|
||||||
final r = parseVersion(remote);
|
final r = parseVersion(remote);
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../services/url_launcher_service.dart';
|
||||||
|
|
||||||
|
const _releasesUrl = 'https://github.com/davide3011/biteplan/releases/latest';
|
||||||
|
|
||||||
|
void showUpdateDialog(BuildContext context, String newVersion) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
icon: const Icon(Icons.system_update_outlined, size: 32),
|
||||||
|
title: const Text('Aggiornamento disponibile'),
|
||||||
|
content: Text(
|
||||||
|
'È disponibile la versione $newVersion di BitePlan.\n'
|
||||||
|
'Vuoi scaricare il nuovo APK?',
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(ctx).pop(),
|
||||||
|
child: const Text('Più tardi'),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(ctx).pop();
|
||||||
|
UrlLauncherService.launch(_releasesUrl);
|
||||||
|
},
|
||||||
|
child: const Text('Scarica'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user