From d90c2fa31c6fe18977dbc22c597cbb8df7e66ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr> Date: Thu, 30 Nov 2023 11:04:06 +0100 Subject: [PATCH] Add settings page (username, security token) --- android/gradle.properties | 4 +- assets/translations/en.json | 6 ++ assets/translations/fr.json | 6 ++ .../metadata/android/en-US/changelogs/35.txt | 1 + .../metadata/android/fr-FR/changelogs/35.txt | 1 + lib/cubit/bottom_nav_cubit.dart | 2 +- lib/cubit/settings_cubit.dart | 46 +++++++++++ lib/cubit/settings_state.dart | 23 ++++++ lib/main.dart | 2 + lib/ui/screens/settings.dart | 23 ++++++ lib/ui/skeleton.dart | 2 + lib/ui/widgets/bottom_nav_bar.dart | 4 + lib/ui/widgets/settings_form.dart | 76 +++++++++++++++++++ pubspec.yaml | 2 +- 14 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/35.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/35.txt create mode 100644 lib/cubit/settings_cubit.dart create mode 100644 lib/cubit/settings_state.dart create mode 100644 lib/ui/screens/settings.dart create mode 100644 lib/ui/widgets/settings_form.dart diff --git a/android/gradle.properties b/android/gradle.properties index 4617f96..32638a3 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.34 -app.versionCode=34 +app.versionName=0.0.35 +app.versionCode=35 diff --git a/assets/translations/en.json b/assets/translations/en.json index 6b82e28..6b7214c 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -5,6 +5,7 @@ "bottom_nav_home": "Home", "bottom_nav_discoveries": "Discoveries", "bottom_nav_repartition": "Statistics", + "bottom_nav_settings": "Settings", "global_statistics": "Global statistics", "statistics_total_scrobbles_count": "Total scrobbles count: {count}", @@ -23,6 +24,11 @@ "top_artists_title": "Top artists ({daysCount} days)", + "settings_title": "Settings", + "settings_label_username": "Username: ", + "settings_label_security_token": "Security token: ", + "settings_button_save": "Save", + "MON": "MON", "TUE": "TUE", "WED": "WED", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 84b4236..b495f44 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -5,6 +5,7 @@ "bottom_nav_home": "Accueil", "bottom_nav_discoveries": "Découvertes", "bottom_nav_repartition": "Statistiques", + "bottom_nav_settings": "Paramètres", "global_statistics": "Statistiques globales d'écoutes", "statistics_total_scrobbles_count": "Nombre total d'écoutes : {count}", @@ -23,6 +24,11 @@ "top_artists_title": "Top artistes ({daysCount} jours)", + "settings_title": "Paramètres", + "settings_label_username": "Utilisateur : ", + "settings_label_security_token": "Jeton de sécurité : ", + "settings_button_save": "Enregistrer", + "MON": "LUN", "TUE": "MAR", "WED": "MER", diff --git a/fastlane/metadata/android/en-US/changelogs/35.txt b/fastlane/metadata/android/en-US/changelogs/35.txt new file mode 100644 index 0000000..5a6ab6b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/35.txt @@ -0,0 +1 @@ +Add settings page (username, security token). diff --git a/fastlane/metadata/android/fr-FR/changelogs/35.txt b/fastlane/metadata/android/fr-FR/changelogs/35.txt new file mode 100644 index 0000000..84037e3 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/35.txt @@ -0,0 +1 @@ +Ajout d'une page de paramètres (nom d'utilisateur, jeton de sécurité). diff --git a/lib/cubit/bottom_nav_cubit.dart b/lib/cubit/bottom_nav_cubit.dart index 8753255..1592a43 100644 --- a/lib/cubit/bottom_nav_cubit.dart +++ b/lib/cubit/bottom_nav_cubit.dart @@ -3,7 +3,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; class BottomNavCubit extends HydratedCubit<int> { BottomNavCubit() : super(0); - int pagesCount = 3; + int pagesCount = 4; void updateIndex(int index) { if (isIndexAllowed(index)) { diff --git a/lib/cubit/settings_cubit.dart b/lib/cubit/settings_cubit.dart new file mode 100644 index 0000000..45df233 --- /dev/null +++ b/lib/cubit/settings_cubit.dart @@ -0,0 +1,46 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +part 'settings_state.dart'; + +class SettingsCubit extends HydratedCubit<SettingsState> { + SettingsCubit() : super(const SettingsState()); + + String getUsername() { + return state.username ?? ''; + } + + String getSecurityToken() { + return state.securityToken ?? ''; + } + + void setValues({ + String? username, + String? securityToken, + }) { + emit(SettingsState( + username: username != null ? username : state.username, + securityToken: securityToken != null ? securityToken : state.securityToken, + )); + } + + @override + SettingsState? fromJson(Map<String, dynamic> json) { + String username = json['username'] as String; + String securityToken = json['securityToken'] as String; + + return SettingsState( + username: username, + securityToken: securityToken, + ); + } + + @override + Map<String, String>? toJson(SettingsState state) { + return <String, String>{ + 'username': state.username ?? '', + 'securityToken': state.securityToken ?? '', + }; + } +} diff --git a/lib/cubit/settings_state.dart b/lib/cubit/settings_state.dart new file mode 100644 index 0000000..c32e6bb --- /dev/null +++ b/lib/cubit/settings_state.dart @@ -0,0 +1,23 @@ +part of 'settings_cubit.dart'; + +@immutable +class SettingsState extends Equatable { + const SettingsState({ + this.username, + this.securityToken, + }); + + final String? username; + final String? securityToken; + + @override + List<String?> get props => <String?>[ + username, + securityToken, + ]; + + Map<String, String?> get values => <String, String?>{ + 'username': username, + 'securityToken': securityToken, + }; +} diff --git a/lib/main.dart b/lib/main.dart index 52ae6ef..d4ca87c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,6 +16,7 @@ import 'package:scrobbles/cubit/data_statistics_global_cubit.dart'; import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart'; import 'package:scrobbles/cubit/data_timeline_cubit.dart'; import 'package:scrobbles/cubit/data_top_artists_cubit.dart'; +import 'package:scrobbles/cubit/settings_cubit.dart'; import 'package:scrobbles/ui/skeleton.dart'; void main() async { @@ -49,6 +50,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MultiBlocProvider( providers: [ + BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()), BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()), BlocProvider<DataCountsByDayCubit>(create: (context) => DataCountsByDayCubit()), BlocProvider<DataCountsByHourCubit>(create: (context) => DataCountsByHourCubit()), diff --git a/lib/ui/screens/settings.dart b/lib/ui/screens/settings.dart new file mode 100644 index 0000000..2dc9cec --- /dev/null +++ b/lib/ui/screens/settings.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +import 'package:scrobbles/ui/widgets/header_app.dart'; +import 'package:scrobbles/ui/widgets/settings_form.dart'; + +class ScreenSettings extends StatelessWidget { + const ScreenSettings({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: <Widget>[ + SizedBox(height: 8), + AppHeader(text: 'settings_title'), + SizedBox(height: 8), + SettingsForm(), + ], + ); + } +} diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart index fe99383..46c449c 100644 --- a/lib/ui/skeleton.dart +++ b/lib/ui/skeleton.dart @@ -5,6 +5,7 @@ import 'package:flutter_swipe/flutter_swipe.dart'; import 'package:scrobbles/cubit/bottom_nav_cubit.dart'; import 'package:scrobbles/ui/screens/discoveries.dart'; import 'package:scrobbles/ui/screens/home.dart'; +import 'package:scrobbles/ui/screens/settings.dart'; import 'package:scrobbles/ui/screens/statistics.dart'; import 'package:scrobbles/ui/widgets/app_bar.dart'; import 'package:scrobbles/ui/widgets/bottom_nav_bar.dart'; @@ -23,6 +24,7 @@ class _SkeletonScreenState extends State<SkeletonScreen> { const ScreenHome(), const ScreenDiscoveries(), const ScreenStatistics(), + const ScreenSettings(), ]; return Scaffold( diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart index daaed37..c775a6a 100644 --- a/lib/ui/widgets/bottom_nav_bar.dart +++ b/lib/ui/widgets/bottom_nav_bar.dart @@ -54,6 +54,10 @@ class BottomNavBar extends StatelessWidget { icon: const Icon(Ionicons.bar_chart_outline), label: tr('bottom_nav_repartition'), ), + BottomNavigationBarItem( + icon: const Icon(Ionicons.settings_outline), + label: tr('bottom_nav_settings'), + ), ], ); }, diff --git a/lib/ui/widgets/settings_form.dart b/lib/ui/widgets/settings_form.dart new file mode 100644 index 0000000..5af5f14 --- /dev/null +++ b/lib/ui/widgets/settings_form.dart @@ -0,0 +1,76 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unicons/unicons.dart'; + +import 'package:scrobbles/cubit/settings_cubit.dart'; + +class SettingsForm extends StatefulWidget { + const SettingsForm({super.key}); + + @override + State<SettingsForm> createState() => _SettingsFormState(); +} + +class _SettingsFormState extends State<SettingsForm> { + final usernameController = TextEditingController(); + final securityTokenController = TextEditingController(); + + @override + void dispose() { + usernameController.dispose(); + securityTokenController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + SettingsCubit settings = BlocProvider.of<SettingsCubit>(context); + + usernameController.text = settings.getUsername(); + securityTokenController.text = settings.getSecurityToken(); + + void saveSettings() { + BlocProvider.of<SettingsCubit>(context).setValues( + username: usernameController.text, + securityToken: securityTokenController.text, + ); + } + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: <Widget>[ + Text('settings_label_username').tr(), + TextFormField( + controller: usernameController, + decoration: InputDecoration( + border: UnderlineInputBorder(), + ), + ), + SizedBox(height: 16), + Text('settings_label_security_token').tr(), + TextFormField( + controller: securityTokenController, + decoration: InputDecoration( + border: UnderlineInputBorder(), + ), + ), + SizedBox(height: 20), + ElevatedButton( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(UniconsLine.save), + SizedBox(width: 8), + Text('settings_button_save').tr(), + ], + ), + onPressed: () => saveSettings(), + ), + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 8c8dc91..8a873de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Display scrobbles data and charts publish_to: 'none' -version: 0.0.34+34 +version: 0.0.35+35 environment: sdk: '^3.0.0' -- GitLab