diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8bff852..534377d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -50,124 +50,155 @@ PODS: - Flutter - file_saver (0.0.1): - Flutter - - Firebase/CoreOnly (12.0.0): - - FirebaseCore (~> 12.0.0) - - Firebase/Crashlytics (12.0.0): + - Firebase/Auth (12.2.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 12.0.0) - - Firebase/Performance (12.0.0): + - FirebaseAuth (~> 12.2.0) + - Firebase/CoreOnly (12.2.0): + - FirebaseCore (~> 12.2.0) + - Firebase/Crashlytics (12.2.0): - Firebase/CoreOnly - - FirebasePerformance (~> 12.0.0) - - firebase_analytics (12.0.0): + - FirebaseCrashlytics (~> 12.2.0) + - Firebase/Database (12.2.0): + - Firebase/CoreOnly + - FirebaseDatabase (~> 12.2.0) + - Firebase/Performance (12.2.0): + - Firebase/CoreOnly + - FirebasePerformance (~> 12.2.0) + - firebase_analytics (12.0.1): - firebase_core - - FirebaseAnalytics (= 12.0.0) + - FirebaseAnalytics (= 12.2.0) - Flutter - - firebase_core (4.0.0): - - Firebase/CoreOnly (= 12.0.0) + - firebase_auth (6.0.2): + - Firebase/Auth (= 12.2.0) + - firebase_core - Flutter - - firebase_crashlytics (5.0.0): - - Firebase/Crashlytics (= 12.0.0) + - firebase_core (4.1.0): + - Firebase/CoreOnly (= 12.2.0) + - Flutter + - firebase_crashlytics (5.0.1): + - Firebase/Crashlytics (= 12.2.0) - firebase_core - Flutter - - firebase_performance (0.11.0): - - Firebase/Performance (= 12.0.0) + - firebase_database (12.0.1): + - Firebase/Database (= 12.2.0) - firebase_core - Flutter - - FirebaseABTesting (12.0.0): - - FirebaseCore (~> 12.0.0) - - FirebaseAnalytics (12.0.0): - - FirebaseAnalytics/Default (= 12.0.0) - - FirebaseCore (~> 12.0.0) - - FirebaseInstallations (~> 12.0.0) + - firebase_performance (0.11.0-1): + - Firebase/Performance (= 12.2.0) + - firebase_core + - Flutter + - FirebaseABTesting (12.2.0): + - FirebaseCore (~> 12.2.0) + - FirebaseAnalytics (12.2.0): + - FirebaseAnalytics/Default (= 12.2.0) + - FirebaseCore (~> 12.2.0) + - FirebaseInstallations (~> 12.2.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - FirebaseAnalytics/Default (12.0.0): - - FirebaseCore (~> 12.0.0) - - FirebaseInstallations (~> 12.0.0) - - GoogleAppMeasurement/Default (= 12.0.0) + - FirebaseAnalytics/Default (12.2.0): + - FirebaseCore (~> 12.2.0) + - FirebaseInstallations (~> 12.2.0) + - GoogleAppMeasurement/Default (= 12.2.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - FirebaseCore (12.0.0): - - FirebaseCoreInternal (~> 12.0.0) + - FirebaseAppCheckInterop (12.2.0) + - FirebaseAuth (12.2.0): + - FirebaseAppCheckInterop (~> 12.2.0) + - FirebaseAuthInterop (~> 12.2.0) + - FirebaseCore (~> 12.2.0) + - FirebaseCoreExtension (~> 12.2.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GTMSessionFetcher/Core (< 6.0, >= 3.4) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (12.2.0) + - FirebaseCore (12.2.0): + - FirebaseCoreInternal (~> 12.2.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Logger (~> 8.1) - - FirebaseCoreExtension (12.0.0): - - FirebaseCore (~> 12.0.0) - - FirebaseCoreInternal (12.0.0): + - FirebaseCoreExtension (12.2.0): + - FirebaseCore (~> 12.2.0) + - FirebaseCoreInternal (12.2.0): - "GoogleUtilities/NSData+zlib (~> 8.1)" - - FirebaseCrashlytics (12.0.0): - - FirebaseCore (~> 12.0.0) - - FirebaseInstallations (~> 12.0.0) - - FirebaseRemoteConfigInterop (~> 12.0.0) - - FirebaseSessions (~> 12.0.0) + - FirebaseCrashlytics (12.2.0): + - FirebaseCore (~> 12.2.0) + - FirebaseInstallations (~> 12.2.0) + - FirebaseRemoteConfigInterop (~> 12.2.0) + - FirebaseSessions (~> 12.2.0) - GoogleDataTransport (~> 10.1) - GoogleUtilities/Environment (~> 8.1) - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) - - FirebaseInstallations (12.0.0): - - FirebaseCore (~> 12.0.0) + - FirebaseDatabase (12.2.0): + - FirebaseAppCheckInterop (~> 12.2.0) + - FirebaseCore (~> 12.2.0) + - FirebaseSharedSwift (~> 12.2.0) + - GoogleUtilities/UserDefaults (~> 8.1) + - leveldb-library (~> 1.22) + - FirebaseInstallations (12.2.0): + - FirebaseCore (~> 12.2.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) - PromisesObjC (~> 2.4) - - FirebasePerformance (12.0.0): - - FirebaseCore (~> 12.0.0) - - FirebaseInstallations (~> 12.0.0) - - FirebaseRemoteConfig (~> 12.0.0) - - FirebaseSessions (~> 12.0.0) + - FirebasePerformance (12.2.0): + - FirebaseCore (~> 12.2.0) + - FirebaseInstallations (~> 12.2.0) + - FirebaseRemoteConfig (~> 12.2.0) + - FirebaseSessions (~> 12.2.0) - GoogleDataTransport (~> 10.1) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) - nanopb (~> 3.30910.0) - - FirebaseRemoteConfig (12.0.0): - - FirebaseABTesting (~> 12.0.0) - - FirebaseCore (~> 12.0.0) - - FirebaseInstallations (~> 12.0.0) - - FirebaseRemoteConfigInterop (~> 12.0.0) - - FirebaseSharedSwift (~> 12.0.0) + - FirebaseRemoteConfig (12.2.0): + - FirebaseABTesting (~> 12.2.0) + - FirebaseCore (~> 12.2.0) + - FirebaseInstallations (~> 12.2.0) + - FirebaseRemoteConfigInterop (~> 12.2.0) + - FirebaseSharedSwift (~> 12.2.0) - GoogleUtilities/Environment (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - - FirebaseRemoteConfigInterop (12.0.0) - - FirebaseSessions (12.0.0): - - FirebaseCore (~> 12.0.0) - - FirebaseCoreExtension (~> 12.0.0) - - FirebaseInstallations (~> 12.0.0) + - FirebaseRemoteConfigInterop (12.2.0) + - FirebaseSessions (12.2.0): + - FirebaseCore (~> 12.2.0) + - FirebaseCoreExtension (~> 12.2.0) + - FirebaseInstallations (~> 12.2.0) - GoogleDataTransport (~> 10.1) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) - nanopb (~> 3.30910.0) - PromisesSwift (~> 2.1) - - FirebaseSharedSwift (12.0.0) + - FirebaseSharedSwift (12.2.0) - Flutter (1.0.0) - flutter_native_splash (2.4.3): - Flutter - - GoogleAdsOnDeviceConversion (2.1.0): + - GoogleAdsOnDeviceConversion (2.3.0): - GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Network (~> 8.1) - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/Core (12.0.0): + - GoogleAppMeasurement/Core (12.2.0): - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/Default (12.0.0): - - GoogleAdsOnDeviceConversion (= 2.1.0) - - GoogleAppMeasurement/Core (= 12.0.0) - - GoogleAppMeasurement/IdentitySupport (= 12.0.0) + - GoogleAppMeasurement/Default (12.2.0): + - GoogleAdsOnDeviceConversion (= 2.3.0) + - GoogleAppMeasurement/Core (= 12.2.0) + - GoogleAppMeasurement/IdentitySupport (= 12.2.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/IdentitySupport (12.0.0): - - GoogleAppMeasurement/Core (= 12.0.0) + - GoogleAppMeasurement/IdentitySupport (12.2.0): + - GoogleAppMeasurement/Core (= 12.2.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) @@ -203,11 +234,13 @@ PODS: - GoogleUtilities/UserDefaults (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy + - GTMSessionFetcher/Core (5.0.0) - image_pickers (0.0.1): - AFNetworking - Flutter - SDWebImage - ZLPhotoBrowser (= 4.5.4) + - leveldb-library (1.22.6) - nanopb (3.30910.0): - nanopb/decode (= 3.30910.0) - nanopb/encode (= 3.30910.0) @@ -223,9 +256,10 @@ PODS: - PromisesObjC (2.4.0) - PromisesSwift (2.4.0): - PromisesObjC (= 2.4.0) - - SDWebImage (5.21.1): - - SDWebImage/Core (= 5.21.1) - - SDWebImage/Core (5.21.1) + - RecaptchaInterop (101.0.0) + - SDWebImage (5.21.2): + - SDWebImage/Core (= 5.21.2) + - SDWebImage/Core (5.21.2) - share_plus (0.0.1): - Flutter - shared_preferences_foundation (0.0.1): @@ -248,8 +282,10 @@ DEPENDENCIES: - file_picker (from `.symlinks/plugins/file_picker/ios`) - file_saver (from `.symlinks/plugins/file_saver/ios`) - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) + - firebase_database (from `.symlinks/plugins/firebase_database/ios`) - firebase_performance (from `.symlinks/plugins/firebase_performance/ios`) - Flutter (from `Flutter`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) @@ -271,10 +307,14 @@ SPEC REPOS: - Firebase - FirebaseABTesting - FirebaseAnalytics + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop - FirebaseCore - FirebaseCoreExtension - FirebaseCoreInternal - FirebaseCrashlytics + - FirebaseDatabase - FirebaseInstallations - FirebasePerformance - FirebaseRemoteConfig @@ -285,9 +325,12 @@ SPEC REPOS: - GoogleAppMeasurement - GoogleDataTransport - GoogleUtilities + - GTMSessionFetcher + - leveldb-library - nanopb - PromisesObjC - PromisesSwift + - RecaptchaInterop - SDWebImage - SwiftyGif - ZLPhotoBrowser @@ -299,10 +342,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/file_saver/ios" firebase_analytics: :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" firebase_crashlytics: :path: ".symlinks/plugins/firebase_crashlytics/ios" + firebase_database: + :path: ".symlinks/plugins/firebase_database/ios" firebase_performance: :path: ".symlinks/plugins/firebase_performance/ios" Flutter: @@ -334,37 +381,46 @@ SPEC CHECKSUMS: DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808 - Firebase: 800d487043c0557d9faed71477a38d9aafb08a41 - firebase_analytics: 2d63841132d444fad3d3e71682dc0072ae2aaf08 - firebase_core: c70f903d70ec229c08c2f49aba5a8c1338546d40 - firebase_crashlytics: f5df4d26483758138b386e0db8a704e5b7ba6543 - firebase_performance: 8430265f50cc21b002deca0d872a20a2edbc629a - FirebaseABTesting: 2cad22e464cd7ef4589ae29f897bc71ff83ce83b - FirebaseAnalytics: 6d790cd1b159b4eb61a99948df0934ce505a34f7 - FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a - FirebaseCoreExtension: 639afb3de6abd611952be78a794c54a47fa0f361 - FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55 - FirebaseCrashlytics: db75aa0cab8d00f68406fa247c32fe17ade884d7 - FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988 - FirebasePerformance: 58e48464297c539c0a75c4c3411a5fba442b4553 - FirebaseRemoteConfig: 4cbbe0083474359025e7bb334b9d0cff16b78d3a - FirebaseRemoteConfigInterop: bfa0ea72ba3dc5af739777296424e46bd6f42613 - FirebaseSessions: 4e784acda213108aafef536535cdfc03504acc42 - FirebaseSharedSwift: 59266c22ccfcef604d725c034c568fa666ea9bda + Firebase: 26f6f8d460603af3df970ad505b16b15f5e2e9a1 + firebase_analytics: cad607594fc56b3743691f37000d81cde0a86237 + firebase_auth: 9270662fd9824ff308da6b16a118f70a7f91fc81 + firebase_core: 3b9065a1037f7f2e360077d2aa5fb6ec9adb3005 + firebase_crashlytics: 0d84e61860fcc67faf4d3a59664ce13a5622dbc0 + firebase_database: 03879c7e072c92a921e7bdc11b4f8e60e04c3502 + firebase_performance: 0e63765dc819d5f4b2331000c4f42382fc92a739 + FirebaseABTesting: 32f3fc079d72c9b93e000b60877c4e4f62ef7031 + FirebaseAnalytics: e04e23bc070e3014aa5cf4980f9df7ce5cd79ec8 + FirebaseAppCheckInterop: a1b2598c64c5a8c42fd6f6a1c3d0938ae4324678 + FirebaseAuth: 059c11702bdb759bb49b6c7ec6ff67abf21f39c4 + FirebaseAuthInterop: 217702acd4cc6baa98ba9d6c054532e0de0b8a16 + FirebaseCore: 311c48a147ad4a0ab7febbaed89e8025c67510cd + FirebaseCoreExtension: 73af080c22a2f7b44cefa391dc08f7e4ee162cb5 + FirebaseCoreInternal: 56ea29f3dad2894f81b060f706f9d53509b6ed3b + FirebaseCrashlytics: f83cbf176d5c637ade108c0aacf1ccbd5ec499bf + FirebaseDatabase: d7f6b8dc187fe7e72db1c1e2b03a7611d8ec2813 + FirebaseInstallations: 3e884b01feabdf67582a80f3250425a00979b4ed + FirebasePerformance: 9071eb99f3ed135defc1c145391a82edb98e0b93 + FirebaseRemoteConfig: e3ae5500b92e813f3a0b12fa09701a26441dfc95 + FirebaseRemoteConfigInterop: 0896fd52ab72586a355c8f389ff85aaa9e5375e1 + FirebaseSessions: f4692789e770bec66ce17d772c0e9561c4f11737 + FirebaseSharedSwift: 52d868d4c269fcb4e4e1310c548435a9c1f46e25 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 - GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64 - GoogleAppMeasurement: 8f6ab04ad6ae493b53fcf56bd26323fb2f1384f3 + GoogleAdsOnDeviceConversion: 9090c435cde08903e8dd1ba2c77fbec9e46d9afe + GoogleAppMeasurement: 09f341dfa8527d1612a09cbfe809a242c0b737af GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 + GTMSessionFetcher: 02d6e866e90bc236f48a703a041dfe43e6221a29 image_pickers: 414d35c2efe796a0ce86dedc756548458e4c1500 + leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 - SDWebImage: f29024626962457f3470184232766516dee8dfea + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba + SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d diff --git a/lib/main.dart b/lib/main.dart index a75c008..b6cc1f5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,32 +4,20 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:shoot_report/firebase_options.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:shoot_report/main_app.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/type_dao.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; -import 'package:shoot_report/utilities/app_migration.dart'; -import 'package:shoot_report/utilities/database.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; +import 'package:shoot_report/services/migration_service.dart'; import 'package:flutter/material.dart'; -late FlutterDatabase database; - Future main() async { // Flutter initialization WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + // Splash initialization FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + // Language initialization await EasyLocalization.ensureInitialized(); - // Database initialization - database = await $FloorFlutterDatabase - .databaseBuilder('flutter_shoot_report.db') - .build(); - TypeDao typeDao = database.typeDao; - WeaponDao weaponDao = database.weaponDao; - TrainingDao trainingDao = database.trainingDao; - CompetitionDao competitionDao = database.competitionDao; + // General initialization _initialization(); @@ -38,12 +26,7 @@ Future main() async { supportedLocales: const [Locale("en"), Locale("de")], path: 'assets/translations', fallbackLocale: const Locale('en'), - child: ShootReport( - typeDao: typeDao, - weaponDao: weaponDao, - trainingDao: trainingDao, - competitionDao: competitionDao, - ))); + child: const ShootReport())); } void _initialization() async { @@ -55,11 +38,8 @@ void _initialization() async { // Firebase configuration FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; - // Check if we have to load default weapons - await AppMigration.loadDefaultWeapons(database.weaponDao); - - // Check if we have to load default types - await AppMigration.loadDefaultTypes(database.typeDao); + // Perform Firebase migration + await MigrationService.performMigration(); // Log App opened FirebaseLog().logAppStart(); diff --git a/lib/main_app.dart b/lib/main_app.dart index fe121a4..9e9972b 100644 --- a/lib/main_app.dart +++ b/lib/main_app.dart @@ -1,25 +1,10 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/type_dao.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; import 'package:shoot_report/utilities/theme.dart'; import 'package:shoot_report/views/weapon/weapon.dart'; class ShootReport extends StatelessWidget { - final TypeDao typeDao; - final WeaponDao weaponDao; - final TrainingDao trainingDao; - final CompetitionDao competitionDao; - - const ShootReport({ - super.key, - required this.typeDao, - required this.weaponDao, - required this.trainingDao, - required this.competitionDao, - }); + const ShootReport({super.key}); @override Widget build(BuildContext context) { @@ -37,11 +22,6 @@ class ShootReport extends StatelessWidget { localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, - home: WeaponWidget( - typeDao: typeDao, - weaponDao: weaponDao, - trainingDao: trainingDao, - competitionDao: competitionDao, - )); + home: const WeaponWidget()); } } diff --git a/lib/models/competition.dart b/lib/models/competition.dart index 7ce9c37..2196ac1 100644 --- a/lib/models/competition.dart +++ b/lib/models/competition.dart @@ -1,37 +1,17 @@ -import 'package:floor/floor.dart'; -import 'package:shoot_report/models/weapon.dart'; - -@Entity( - primaryKeys: ['id'], - foreignKeys: [ - ForeignKey( - childColumns: ['weapon_id'], - parentColumns: ['id'], - entity: Weapon, - ) - ], -) class Competition { - @PrimaryKey(autoGenerate: true) final int? id; - DateTime date; - String image; - String place; - String kind; - int shotCount; - List shots; - String comment; - - @ColumnInfo(name: 'weapon_id') final int weaponId; + // Firebase key for deletion/update + String? firebaseKey; + Competition(this.id, this.date, this.image, this.place, this.kind, this.shotCount, this.shots, this.comment, this.weaponId); } diff --git a/lib/models/training.dart b/lib/models/training.dart index df46f25..5d5dc04 100644 --- a/lib/models/training.dart +++ b/lib/models/training.dart @@ -1,39 +1,18 @@ -import 'package:floor/floor.dart'; -import 'package:shoot_report/models/weapon.dart'; - -@Entity( - primaryKeys: ['id'], - foreignKeys: [ - ForeignKey( - childColumns: ['weapon_id'], - parentColumns: ['id'], - entity: Weapon, - ) - ], -) class Training { - @PrimaryKey(autoGenerate: true) final int? id; - DateTime date; - String image; - int indicator; - String place; - String kind; - int shotCount; - List shots; - String comment; - - @ColumnInfo(name: 'weapon_id') final int weaponId; + // Firebase key for deletion + String? firebaseKey; + Training(this.id, this.date, this.image, this.indicator, this.place, this.kind, this.shotCount, this.shots, this.comment, this.weaponId); } diff --git a/lib/models/type.dart b/lib/models/type.dart index ebf7fb7..45b5819 100644 --- a/lib/models/type.dart +++ b/lib/models/type.dart @@ -1,12 +1,6 @@ -import 'package:floor/floor.dart'; - -@entity class Type { - @PrimaryKey(autoGenerate: true) final int? id; - final String name; - final int order; Type(this.id, this.name, this.order); diff --git a/lib/models/weapon.dart b/lib/models/weapon.dart index c7890b1..6b13a43 100644 --- a/lib/models/weapon.dart +++ b/lib/models/weapon.dart @@ -1,19 +1,10 @@ -import 'package:floor/floor.dart'; - -@entity class Weapon { - @PrimaryKey(autoGenerate: true) final int? id; - final String name; - final int order; - final String prefFile; - final int typeId; + final bool show; - bool show; - - Weapon(this.id, this.name, this.order, this.prefFile, this.typeId, this.show); + const Weapon(this.id, this.name, this.order, this.prefFile, this.typeId, this.show); } diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart new file mode 100644 index 0000000..85b1b85 --- /dev/null +++ b/lib/services/auth_service.dart @@ -0,0 +1,29 @@ +import 'package:firebase_auth/firebase_auth.dart'; + +class AuthService { + static final FirebaseAuth _auth = FirebaseAuth.instance; + + static User? get currentUser => _auth.currentUser; + static String? get userId => _auth.currentUser?.uid; + + static Future signInAnonymously() async { + if (_auth.currentUser == null) { + await _auth.signInAnonymously(); + } + } + + static Future createAccountAndMigrateData(String email, String password) async { + final currentUser = _auth.currentUser; + if (currentUser == null || !currentUser.isAnonymous) { + throw Exception('No anonymous user to upgrade'); + } + + // Create email/password credential + final credential = EmailAuthProvider.credential(email: email, password: password); + + // Link anonymous account with email/password + await currentUser.linkWithCredential(credential); + + // Data is automatically preserved since we're upgrading the same user + } +} \ No newline at end of file diff --git a/lib/services/competition_dao.dart b/lib/services/competition_dao.dart deleted file mode 100644 index 713f7a3..0000000 --- a/lib/services/competition_dao.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:shoot_report/models/competition.dart'; -import 'package:floor/floor.dart'; - -@dao -abstract class CompetitionDao { - @Query('SELECT * FROM competition') - Stream> findAllCompetitions(); - - @Query('SELECT * FROM competition WHERE weapon_id = :wid ORDER by date DESC') - Stream> findAllCompetitionForWeapon(int wid); - - @insert - Future insertCompetition(Competition competition); - - @update - Future updateCompetition(Competition competition); - - @delete - Future deleteCompetition(Competition competition); -} diff --git a/lib/services/firebase_data_service.dart b/lib/services/firebase_data_service.dart new file mode 100644 index 0000000..6cb89d6 --- /dev/null +++ b/lib/services/firebase_data_service.dart @@ -0,0 +1,278 @@ +import 'dart:async'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:shoot_report/services/auth_service.dart'; + +class FirebaseDataService { + static final StreamController>> _weaponsController = + StreamController>>.broadcast(); + static final DatabaseReference _database = FirebaseDatabase.instanceFor( + app: Firebase.app(), + databaseURL: + 'https://shoot-report-default-rtdb.europe-west1.firebasedatabase.app') + .ref(); + + // User preferences + static Future setPreference(String key, String value) async { + final userId = AuthService.userId; + if (userId != null) { + await _database.child('users/$userId/preferences/$key').set(value); + } + } + + static Future getPreference(String key) async { + final userId = AuthService.userId; + if (userId != null) { + final snapshot = + await _database.child('users/$userId/preferences/$key').get(); + return snapshot.value as String?; + } + return null; + } + + // Global weapons + static Future?> getGlobalWeapons() async { + final snapshot = await _database.child('global/weapons').get(); + final data = snapshot.value; + + if (data == null) return null; + + if (data is Map) { + return data; + } else if (data is Map) { + return Map.from(data); + } else if (data is List) { + // Convert list to map with index as key + final Map result = {}; + for (int i = 0; i < data.length; i++) { + if (data[i] != null) { + result[i.toString()] = data[i]; + } + } + return result; + } + + return null; + } + + // Global types + static Future?> getGlobalTypes() async { + final snapshot = await _database.child('global/types').get(); + final data = snapshot.value; + + if (data == null) return null; + + if (data is Map) { + return data; + } else if (data is Map) { + return Map.from(data); + } else if (data is List) { + // Convert list to map with index as key + final Map result = {}; + for (int i = 0; i < data.length; i++) { + if (data[i] != null) { + result[i.toString()] = data[i]; + } + } + return result; + } + + return null; + } + + // User visibility + static Future setWeaponVisibility(String weaponId, bool visible) async { + final userId = AuthService.userId; + if (userId != null) { + await _database + .child('users/$userId/visibility/weapons/$weaponId') + .set(visible); + + // Manually refresh the weapons stream + _refreshWeaponsStream(); + } + } + + static void _refreshWeaponsStream() async { + try { + final weapons = await getVisibleWeapons(); + _weaponsController.add(weapons); + } catch (e) { + // Silently handle errors + } + } + + static Future getWeaponVisibility(String weaponId) async { + final userId = AuthService.userId; + if (userId != null) { + final snapshot = await _database + .child('users/$userId/visibility/weapons/$weaponId') + .get(); + final value = snapshot.value; + + if (value == null) return true; + if (value is bool) return value; + if (value is String) return value.toLowerCase() == 'true'; + if (value is int) return value != 0; + + return true; // Default to visible + } + return true; + } + + // Get visible weapons (combines global weapons with user visibility) + static Future>> getVisibleWeapons() async { + try { + final globalWeapons = await getGlobalWeapons(); + + if (globalWeapons == null || globalWeapons.isEmpty) { + return []; + } + + final visibleWeapons = >[]; + for (final entry in globalWeapons.entries) { + final weaponData = entry.value; + final weaponId = entry.key; + + // Convert to proper Map + Map weapon; + if (weaponData is Map) { + weapon = weaponData; + } else if (weaponData is Map) { + weapon = Map.from(weaponData); + } else { + continue; + } + + final isVisible = await getWeaponVisibility(weaponId); + + if (isVisible) { + visibleWeapons.add(weapon); + } + } + + visibleWeapons.sort((a, b) => (a['order'] as int).compareTo(b['order'] as int)); + return visibleWeapons; + } catch (e) { + return []; + } + } + + // Stream for weapon visibility changes + static Stream>> getVisibleWeaponsStream() { + // Initialize stream with current data + _refreshWeaponsStream(); + + return _weaponsController.stream; + } + + + + // User trainings + static Future saveTraining(Map training) async { + final userId = AuthService.userId; + if (userId != null) { + await _database.child('users/$userId/trainings').push().set(training); + } + } + + static Future deleteTraining(String trainingKey) async { + final userId = AuthService.userId; + if (userId != null) { + await _database.child('users/$userId/trainings/$trainingKey').remove(); + } + } + + static Future updateTraining( + String trainingKey, Map training) async { + final userId = AuthService.userId; + if (userId != null) { + await _database + .child('users/$userId/trainings/$trainingKey') + .update(training); + } + } + + static Future?> getUserTrainings() async { + final userId = AuthService.userId; + if (userId != null) { + final snapshot = await _database.child('users/$userId/trainings').get(); + return snapshot.value as Map?; + } + return null; + } + + static Stream?> getUserTrainingsStream() { + final userId = AuthService.userId; + if (userId != null) { + return _database.child('users/$userId/trainings').onValue.map((event) { + if (event.snapshot.exists) { + final data = event.snapshot.value; + if (data is Map) { + return data; + } else if (data is Map) { + return Map.from(data); + } + } + return {}; + }); + } + return Stream.value({}); + } + + // User competitions + static Future saveCompetition(Map competition) async { + final userId = AuthService.userId; + if (userId != null) { + await _database + .child('users/$userId/competitions') + .push() + .set(competition); + } + } + + static Future deleteCompetition(String competitionKey) async { + final userId = AuthService.userId; + if (userId != null) { + await _database + .child('users/$userId/competitions/$competitionKey') + .remove(); + } + } + + static Future updateCompetition( + String competitionKey, Map competition) async { + final userId = AuthService.userId; + if (userId != null) { + await _database + .child('users/$userId/competitions/$competitionKey') + .update(competition); + } + } + + static Future?> getUserCompetitions() async { + final userId = AuthService.userId; + if (userId != null) { + final snapshot = + await _database.child('users/$userId/competitions').get(); + return snapshot.value as Map?; + } + return null; + } + + static Stream?> getUserCompetitionsStream() { + final userId = AuthService.userId; + if (userId != null) { + return _database.child('users/$userId/competitions').onValue.map((event) { + final data = event.snapshot.value; + if (data is Map) { + return data; + } else if (data is Map) { + return Map.from(data); + } + return {}; + }); + } + return Stream.value({}); + } +} diff --git a/lib/services/migration_service.dart b/lib/services/migration_service.dart new file mode 100644 index 0000000..89015e1 --- /dev/null +++ b/lib/services/migration_service.dart @@ -0,0 +1,105 @@ +import 'dart:io'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:path/path.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:shoot_report/services/auth_service.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; +import 'package:sqflite/sqflite.dart'; + +class MigrationService { + static const String _migrationKey = 'firebase_migration_completed'; + + static Future isMigrationCompleted() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getBool(_migrationKey) ?? false; + } + + static Future performMigration() async { + if (await isMigrationCompleted()) return; + + await AuthService.signInAnonymously(); + + // Try to migrate from SQLite if database exists + await _migrateFromSQLite(); + + // Migrate SharedPreferences + await _migrateSharedPreferences(); + + // Mark migration as completed + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_migrationKey, true); + } + + static Future _migrateFromSQLite() async { + try { + final databasesPath = await getDatabasesPath(); + final path = join(databasesPath, 'flutter_shoot_report.db'); + + if (!await File(path).exists()) { + return; + } + + final database = await openDatabase(path); + final userId = AuthService.userId; + if (userId == null) return; + + final DatabaseReference userRef = FirebaseDatabase.instance.ref('users/$userId'); + + try { + final trainings = await database.query('Training'); + for (final training in trainings) { + await userRef.child('trainings').push().set({ + 'date': training['date'], + 'image': training['image'] ?? '', + 'indicator': training['indicator'] ?? 2, + 'place': training['place'] ?? '', + 'kind': training['kind'] ?? '', + 'shotCount': training['shotCount'] ?? 0, + 'shots': training['shots'] ?? '[]', + 'comment': training['comment'] ?? '', + 'weaponId': training['weapon_id'], + }); + } + } catch (e) {} + + try { + final competitions = await database.query('Competition'); + for (final competition in competitions) { + await userRef.child('competitions').push().set({ + 'date': competition['date'], + 'image': competition['image'] ?? '', + 'place': competition['place'] ?? '', + 'kind': competition['kind'] ?? '', + 'shotCount': competition['shotCount'] ?? 0, + 'shots': competition['shots'] ?? '[]', + 'comment': competition['comment'] ?? '', + 'weaponId': competition['weapon_id'], + }); + } + } catch (e) {} + + try { + final weapons = await database.query('Weapon'); + for (final weapon in weapons) { + await userRef.child('visibility/weapons/${weapon['id']}').set(weapon['show'] == 1); + } + } catch (e) {} + + await database.close(); + } catch (e) {} + } + + static Future _migrateSharedPreferences() async { + final prefs = await SharedPreferences.getInstance(); + final keys = prefs.getKeys(); + + for (final key in keys) { + if (key.contains('_goal') || key.contains('_pref')) { + final value = prefs.getString(key); + if (value != null) { + await FirebaseDataService.setPreference(key, value); + } + } + } + } +} diff --git a/lib/services/training_dao.dart b/lib/services/training_dao.dart deleted file mode 100644 index 40d2ca8..0000000 --- a/lib/services/training_dao.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:shoot_report/models/training.dart'; -import 'package:floor/floor.dart'; - -@dao -abstract class TrainingDao { - @Query('SELECT * FROM training') - Stream> findAllTrainings(); - - @Query('SELECT * FROM training WHERE weapon_id = :wid ORDER by date DESC') - Stream> findAllTrainingsForWeapon(int wid); - - @insert - Future insertTraining(Training training); - - @update - Future updateTraining(Training training); - - @delete - Future deleteTraining(Training training); -} diff --git a/lib/services/type_dao.dart b/lib/services/type_dao.dart deleted file mode 100644 index 70c9040..0000000 --- a/lib/services/type_dao.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:floor/floor.dart'; -import 'package:shoot_report/models/type.dart'; - -@dao -abstract class TypeDao { - @Query("SELECT * FROM type ORDER by \"order\" ASC;") - Stream> findAllTypes(); - - @insert - Future insertGroup(Type type); - - @update - Future updateGroup(Type type); - - @delete - Future deleteGroup(Type type); -} diff --git a/lib/services/weapon_dao.dart b/lib/services/weapon_dao.dart deleted file mode 100644 index 145ce0c..0000000 --- a/lib/services/weapon_dao.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:floor/floor.dart'; -import 'package:shoot_report/models/weapon.dart'; - -@dao -abstract class WeaponDao { - @Query("SELECT * FROM weapon ORDER by \"order\" ASC;") - Stream> findAllWeapons(); - - @Query("SELECT * FROM weapon WHERE typeId = :id ORDER by \"order\" ASC;") - Stream> findAllWeaponsForType(int id); - - @Query("SELECT * FROM weapon WHERE show = :show ORDER by \"order\" ASC;") - Stream> findAllWeaponsDistinction(bool show); - - @insert - Future insertWeapon(Weapon weapon); - - @update - Future updateWeapon(Weapon weapon); - - @delete - Future deleteWeapon(Weapon weapon); -} diff --git a/lib/utilities/app_migration.dart b/lib/utilities/app_migration.dart deleted file mode 100644 index d5f2bd8..0000000 --- a/lib/utilities/app_migration.dart +++ /dev/null @@ -1,153 +0,0 @@ -import 'dart:developer'; -import 'package:shoot_report/models/type.dart'; -import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/type_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; - -class AppMigration { - static Future loadDefaultWeapons(WeaponDao weaponDao) async { - log("Loading initial weapons.", name: "Migration"); - try { - await weaponDao - .insertWeapon(Weapon(1, "weapon_00", 0, "prefWeapon00", 1, false)); - await weaponDao - .insertWeapon(Weapon(2, "weapon_01", 1, "prefWeapon01", 1, false)); - await weaponDao - .insertWeapon(Weapon(3, "weapon_02", 2, "prefWeapon02", 1, false)); - await weaponDao - .insertWeapon(Weapon(4, "weapon_03", 3, "prefWeapon03", 1, false)); - await weaponDao - .insertWeapon(Weapon(5, "weapon_04", 4, "prefWeapon04", 1, false)); - await weaponDao - .insertWeapon(Weapon(6, "weapon_05", 5, "prefWeapon05", 2, false)); - await weaponDao - .insertWeapon(Weapon(7, "weapon_06", 6, "prefWeapon06", 2, false)); - await weaponDao - .insertWeapon(Weapon(8, "weapon_07", 7, "prefWeapon07", 1, false)); - await weaponDao - .insertWeapon(Weapon(9, "weapon_08", 8, "prefWeapon08", 1, false)); - await weaponDao - .insertWeapon(Weapon(10, "weapon_09", 9, "prefWeapon09", 1, false)); - await weaponDao - .insertWeapon(Weapon(11, "weapon_10", 10, "prefWeapon10", 2, false)); - await weaponDao - .insertWeapon(Weapon(12, "weapon_11", 11, "prefWeapon11", 5, false)); - await weaponDao - .insertWeapon(Weapon(13, "weapon_12", 12, "prefWeapon12", 2, false)); - await weaponDao - .insertWeapon(Weapon(14, "weapon_13", 13, "prefWeapon13", 1, false)); - await weaponDao - .insertWeapon(Weapon(15, "weapon_14", 14, "prefWeapon14", 1, false)); - await weaponDao - .insertWeapon(Weapon(16, "weapon_15", 15, "prefWeapon15", 1, false)); - await weaponDao - .insertWeapon(Weapon(17, "weapon_16", 16, "prefWeapon16", 1, false)); - await weaponDao - .insertWeapon(Weapon(18, "weapon_17", 17, "prefWeapon17", 1, false)); - await weaponDao - .insertWeapon(Weapon(19, "weapon_18", 18, "prefWeapon18", 1, false)); - await weaponDao - .insertWeapon(Weapon(20, "weapon_19", 19, "prefWeapon19", 1, false)); - await weaponDao - .insertWeapon(Weapon(21, "weapon_20", 20, "prefWeapon20", 1, false)); - await weaponDao - .insertWeapon(Weapon(22, "weapon_21", 21, "prefWeapon21", 1, false)); - await weaponDao - .insertWeapon(Weapon(23, "weapon_22", 22, "prefWeapon22", 1, false)); - await weaponDao - .insertWeapon(Weapon(24, "weapon_23", 23, "prefWeapon23", 1, false)); - await weaponDao - .insertWeapon(Weapon(25, "weapon_24", 24, "prefWeapon24", 1, false)); - await weaponDao - .insertWeapon(Weapon(26, "weapon_25", 25, "prefWeapon25", 2, false)); - await weaponDao - .insertWeapon(Weapon(27, "weapon_26", 26, "prefWeapon26", 2, false)); - await weaponDao - .insertWeapon(Weapon(28, "weapon_27", 27, "prefWeapon27", 2, false)); - await weaponDao - .insertWeapon(Weapon(29, "weapon_28", 28, "prefWeapon28", 2, false)); - await weaponDao - .insertWeapon(Weapon(30, "weapon_29", 29, "prefWeapon29", 2, false)); - await weaponDao - .insertWeapon(Weapon(31, "weapon_30", 30, "prefWeapon30", 2, false)); - await weaponDao - .insertWeapon(Weapon(32, "weapon_31", 31, "prefWeapon31", 2, false)); - await weaponDao - .insertWeapon(Weapon(33, "weapon_32", 32, "prefWeapon32", 2, false)); - await weaponDao - .insertWeapon(Weapon(34, "weapon_33", 33, "prefWeapon33", 2, false)); - await weaponDao - .insertWeapon(Weapon(35, "weapon_34", 34, "prefWeapon34", 2, false)); - await weaponDao - .insertWeapon(Weapon(36, "weapon_35", 35, "prefWeapon35", 3, false)); - await weaponDao - .insertWeapon(Weapon(37, "weapon_36", 36, "prefWeapon36", 3, false)); - await weaponDao - .insertWeapon(Weapon(38, "weapon_37", 37, "prefWeapon37", 4, false)); - await weaponDao - .insertWeapon(Weapon(39, "weapon_38", 38, "prefWeapon38", 4, false)); - await weaponDao - .insertWeapon(Weapon(40, "weapon_39", 39, "prefWeapon39", 4, false)); - await weaponDao - .insertWeapon(Weapon(41, "weapon_40", 40, "prefWeapon40", 4, false)); - await weaponDao - .insertWeapon(Weapon(42, "weapon_41", 41, "prefWeapon41", 4, false)); - await weaponDao - .insertWeapon(Weapon(43, "weapon_42", 42, "prefWeapon42", 4, false)); - await weaponDao - .insertWeapon(Weapon(44, "weapon_43", 43, "prefWeapon43", 4, false)); - await weaponDao - .insertWeapon(Weapon(45, "weapon_44", 44, "prefWeapon44", 4, false)); - await weaponDao - .insertWeapon(Weapon(46, "weapon_45", 45, "prefWeapon45", 4, false)); - await weaponDao - .insertWeapon(Weapon(47, "weapon_46", 46, "prefWeapon46", 4, false)); - await weaponDao - .insertWeapon(Weapon(48, "weapon_47", 47, "prefWeapon47", 4, false)); - await weaponDao - .insertWeapon(Weapon(49, "weapon_48", 48, "prefWeapon48", 4, false)); - await weaponDao - .insertWeapon(Weapon(50, "weapon_49", 49, "prefWeapon49", 4, false)); - await weaponDao - .insertWeapon(Weapon(51, "weapon_50", 50, "prefWeapon50", 5, false)); - await weaponDao - .insertWeapon(Weapon(52, "weapon_51", 51, "prefWeapon51", 5, false)); - await weaponDao - .insertWeapon(Weapon(53, "weapon_52", 52, "prefWeapon52", 5, false)); - await weaponDao - .insertWeapon(Weapon(54, "weapon_53", 53, "prefWeapon53", 5, false)); - await weaponDao - .insertWeapon(Weapon(55, "weapon_54", 54, "prefWeapon54", 5, false)); - await weaponDao - .insertWeapon(Weapon(56, "weapon_55", 55, "prefWeapon55", 5, false)); - await weaponDao - .insertWeapon(Weapon(57, "weapon_56", 56, "prefWeapon56", 5, false)); - await weaponDao - .insertWeapon(Weapon(58, "weapon_57", 57, "prefWeapon57", 5, false)); - - log("Loading initial weapons finished.", name: "Migration"); - return 0; - } on Exception catch (_) { - log("Default weapons already there."); - return 1; - } - } - - static Future loadDefaultTypes(TypeDao typeDao) async { - log("Loading initial types.", name: "Migration"); - - try { - await typeDao.insertGroup(Type(1, "type_00", 0)); - await typeDao.insertGroup(Type(2, "type_01", 1)); - await typeDao.insertGroup(Type(3, "type_02", 2)); - await typeDao.insertGroup(Type(4, "type_03", 3)); - await typeDao.insertGroup(Type(5, "type_04", 4)); - - log("Loading initial types finished.", name: "Migration"); - return 0; - } on Exception catch (_) { - log("Default types already there."); - return 1; - } - } -} diff --git a/lib/utilities/array_converter.dart b/lib/utilities/array_converter.dart deleted file mode 100644 index 24317dc..0000000 --- a/lib/utilities/array_converter.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:floor/floor.dart'; -import 'dart:convert'; - -class ArrayConverter extends TypeConverter { - @override - List decode(String databaseValue) { - return jsonDecode(databaseValue); - } - - @override - String encode(List value) { - return jsonEncode(value); - } -} diff --git a/lib/utilities/database.dart b/lib/utilities/database.dart deleted file mode 100644 index 95fda95..0000000 --- a/lib/utilities/database.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'dart:async'; -import 'package:shoot_report/models/competition.dart'; -import 'package:shoot_report/models/type.dart'; -import 'package:shoot_report/models/training.dart'; -import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/type_dao.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:floor/floor.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; -import 'package:shoot_report/utilities/array_converter.dart'; -import 'package:shoot_report/utilities/date_time_converter.dart'; -// ignore: depend_on_referenced_packages -import 'package:sqflite/sqflite.dart' as sqflite; -part 'database.g.dart'; - -@TypeConverters([DateTimeConverter, ArrayConverter]) -@Database(version: 1, entities: [Type, Weapon, Training, Competition]) -abstract class FlutterDatabase extends FloorDatabase { - TypeDao get typeDao; - WeaponDao get weaponDao; - TrainingDao get trainingDao; - CompetitionDao get competitionDao; -} diff --git a/lib/utilities/date_time_converter.dart b/lib/utilities/date_time_converter.dart deleted file mode 100644 index e66e614..0000000 --- a/lib/utilities/date_time_converter.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:floor/floor.dart'; - -class DateTimeConverter extends TypeConverter { - @override - DateTime decode(int databaseValue) { - return DateTime.fromMillisecondsSinceEpoch(databaseValue); - } - - @override - int encode(DateTime value) { - return value.millisecondsSinceEpoch; - } -} diff --git a/lib/views/competition/competition.dart b/lib/views/competition/competition.dart index f38aaa3..8aacd1b 100644 --- a/lib/views/competition/competition.dart +++ b/lib/views/competition/competition.dart @@ -1,22 +1,15 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; + import 'package:shoot_report/utilities/theme.dart'; import 'package:shoot_report/views/competition/competition_list.dart'; import 'package:shoot_report/views/competition/competition_statistics.dart'; class CompetitionWidget extends StatelessWidget { final Weapon weapon; - final WeaponDao weaponDao; - final CompetitionDao competitionDao; - const CompetitionWidget( - {super.key, - required this.weapon, - required this.weaponDao, - required this.competitionDao}); + const CompetitionWidget({super.key, required this.weapon}); @override Widget build(BuildContext context) { @@ -35,15 +28,8 @@ class CompetitionWidget extends StatelessWidget { ), Flexible( child: TabBarView(children: [ - CompetitionListWidget( - weapon: weapon, - weaponDao: weaponDao, - competitionDao: competitionDao, - ), - CompetitionStatisticWidget( - weapon: weapon, - competitionDao: competitionDao, - ) + CompetitionListWidget(weapon: weapon), + CompetitionStatisticWidget(weapon: weapon) ])) ])); } diff --git a/lib/views/competition/competition_add.dart b/lib/views/competition/competition_add.dart index 039b698..e765dca 100644 --- a/lib/views/competition/competition_add.dart +++ b/lib/views/competition/competition_add.dart @@ -6,19 +6,17 @@ import 'package:flutter/services.dart'; import 'package:image_pickers/image_pickers.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:quickalert/quickalert.dart'; -import 'package:shoot_report/models/competition.dart'; + import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/utilities/kind_list.dart'; import 'package:shoot_report/utilities/theme.dart'; class CompetitionAddWidget extends StatefulWidget { final Weapon weapon; - final CompetitionDao competitionDao; - const CompetitionAddWidget( - {super.key, required this.weapon, required this.competitionDao}); + const CompetitionAddWidget({super.key, required this.weapon}); @override State createState() => _CompetitionAddWidgetState(); @@ -333,19 +331,18 @@ class _CompetitionAddWidgetState extends State { ])))))); } - void _addCompetition() { - var competition = Competition( - null, - date, - Platform.isIOS ? imagePath.split("/").last : imagePath, - place, - kind, - shotCount, - shots, - comment, - widget.weapon.id!); - - widget.competitionDao.insertCompetition(competition); + void _addCompetition() async { + await FirebaseDataService.saveCompetition({ + 'date': date.millisecondsSinceEpoch, + 'image': Platform.isIOS ? imagePath.split("/").last : imagePath, + 'place': place, + 'kind': kind, + 'shotCount': shotCount, + 'shots': shots, + 'comment': comment, + 'weaponId': widget.weapon.id!, + }); + HapticFeedback.heavyImpact(); QuickAlert.show( context: context, diff --git a/lib/views/competition/competition_edit.dart b/lib/views/competition/competition_edit.dart index 74f2ae3..8e08ca8 100644 --- a/lib/views/competition/competition_edit.dart +++ b/lib/views/competition/competition_edit.dart @@ -10,7 +10,7 @@ import 'package:quickalert/quickalert.dart'; import 'package:share_plus/share_plus.dart'; import 'package:shoot_report/models/competition.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/csv_converter.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/utilities/kind_list.dart'; @@ -18,13 +18,11 @@ import 'package:shoot_report/utilities/theme.dart'; class CompetitionEditWidget extends StatefulWidget { final Weapon weapon; - final CompetitionDao competitionDao; final Competition competition; const CompetitionEditWidget( {super.key, required this.weapon, - required this.competitionDao, required this.competition}); @override @@ -381,19 +379,19 @@ class _CompetitionEditWidgetState extends State { ])))))); } - void _editCompetition() { - widget.competition.date = date; - if (Platform.isIOS) { - widget.competition.image = imagePath.split("/").last; - } else { - widget.competition.image = imagePath; + void _editCompetition() async { + if (widget.competition.firebaseKey != null) { + await FirebaseDataService.updateCompetition(widget.competition.firebaseKey!, { + 'date': date.millisecondsSinceEpoch, + 'image': Platform.isIOS ? imagePath.split("/").last : imagePath, + 'place': place, + 'kind': kind, + 'shotCount': shotCount, + 'shots': shots, + 'comment': comment, + 'weaponId': widget.weapon.id!, + }); } - widget.competition.place = place; - widget.competition.kind = kind; - widget.competition.shotCount = shotCount; - widget.competition.shots = shots; - widget.competition.comment = comment; - widget.competitionDao.updateCompetition(widget.competition); HapticFeedback.heavyImpact(); QuickAlert.show( diff --git a/lib/views/competition/competition_list.dart b/lib/views/competition/competition_list.dart index d61f533..4f34495 100644 --- a/lib/views/competition/competition_list.dart +++ b/lib/views/competition/competition_list.dart @@ -3,33 +3,27 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:shoot_report/models/competition.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; import 'package:shoot_report/views/competition/competition_add.dart'; import 'package:shoot_report/views/competition/competition_row.dart'; class CompetitionListWidget extends StatelessWidget { final Weapon weapon; - final WeaponDao weaponDao; - final CompetitionDao competitionDao; - const CompetitionListWidget( - {super.key, - required this.weapon, - required this.weaponDao, - required this.competitionDao}); + const CompetitionListWidget({super.key, required this.weapon}); @override Widget build(BuildContext context) { return Scaffold( - body: StreamBuilder>( - stream: competitionDao.findAllCompetitionForWeapon(weapon.id!), + body: StreamBuilder?>( + stream: FirebaseDataService.getUserCompetitionsStream(), builder: (_, snapshot) { - if (!snapshot.hasData) { - return const SizedBox(); + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); } - if (snapshot.data.toString() == "[]") { + + if (!snapshot.hasData || snapshot.data == null || snapshot.data!.isEmpty) { final ThemeData mode = Theme.of(context); return Center( child: Column( @@ -47,14 +41,55 @@ class CompetitionListWidget extends StatelessWidget { ])); } - final competitions = snapshot.requireData; + final competitionsData = snapshot.data!; + final competitions = competitionsData.entries + .where((entry) { + final data = Map.from(entry.value as Map); + return data['weaponId'] == weapon.id!; + }) + .map((entry) { + final data = Map.from(entry.value as Map); + final competition = Competition( + null, + DateTime.fromMillisecondsSinceEpoch(data['date']), + data['image'] ?? '', + data['place'] ?? '', + data['kind'] ?? '', + data['shotCount'] ?? 0, + data['shots'] ?? [], + data['comment'] ?? '', + data['weaponId'] ?? weapon.id!, + ); + // Store Firebase key for deletion/update + competition.firebaseKey = entry.key; + return competition; + }).toList(); + + competitions.sort((a, b) => b.date.compareTo(a.date)); + + if (competitions.isEmpty) { + final ThemeData mode = Theme.of(context); + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.emoji_events, + color: (mode.brightness == Brightness.light) + ? const Color(AppTheme.primaryColor) + : const Color(AppTheme.backgroundLight), + size: 120, + ), + Text(tr("competition_data_no"), + textAlign: TextAlign.center) + ])); + } return ListView.separated( itemCount: competitions.length, itemBuilder: (context, index) { return CompetitionListRow( weapon: weapon, - competitionDao: competitionDao, competition: competitions[index]); }, separatorBuilder: (context, index) { @@ -66,8 +101,7 @@ class CompetitionListWidget extends StatelessWidget { showBarModalBottomSheet( context: context, expand: true, - builder: (context) => CompetitionAddWidget( - weapon: weapon, competitionDao: competitionDao), + builder: (context) => CompetitionAddWidget(weapon: weapon), ); }, backgroundColor: const Color(AppTheme.accentColor), diff --git a/lib/views/competition/competition_row.dart b/lib/views/competition/competition_row.dart index 1bacc1d..d333324 100644 --- a/lib/views/competition/competition_row.dart +++ b/lib/views/competition/competition_row.dart @@ -3,18 +3,16 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:shoot_report/models/competition.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/views/competition/competition_edit.dart'; class CompetitionListRow extends StatefulWidget { final Weapon weapon; - final CompetitionDao competitionDao; final Competition competition; const CompetitionListRow({ super.key, required this.weapon, - required this.competitionDao, required this.competition, }); @@ -49,7 +47,6 @@ class _CompetitionListRowState extends State { expand: true, builder: (context) => CompetitionEditWidget( weapon: widget.weapon, - competitionDao: widget.competitionDao, competition: widget.competition)); }); } @@ -63,19 +60,21 @@ class _CompetitionListRowState extends State { content: Text(tr("competition_alert_message")), actions: [ TextButton( - onPressed: () { - widget.competitionDao - .deleteCompetition(widget.competition); - - final scaffoldMessengerState = - ScaffoldMessenger.of(context); - scaffoldMessengerState.hideCurrentSnackBar(); - scaffoldMessengerState.showSnackBar( - SnackBar( - content: Text(tr("competition_deleted")), - behavior: SnackBarBehavior.floating), - ); + onPressed: () async { Navigator.of(context).pop(); + if (widget.competition.firebaseKey != null) { + await FirebaseDataService.deleteCompetition(widget.competition.firebaseKey!); + } + if (mounted) { + final scaffoldMessengerState = + ScaffoldMessenger.of(context); + scaffoldMessengerState.hideCurrentSnackBar(); + scaffoldMessengerState.showSnackBar( + SnackBar( + content: Text(tr("competition_deleted")), + behavior: SnackBarBehavior.floating), + ); + } }, child: Text(tr("general_yes"))), TextButton( diff --git a/lib/views/competition/competition_statistics.dart b/lib/views/competition/competition_statistics.dart index decc514..e29573c 100644 --- a/lib/views/competition/competition_statistics.dart +++ b/lib/views/competition/competition_statistics.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/competition.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/chart_data.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/utilities/theme.dart'; @@ -10,13 +10,8 @@ import 'package:shoot_report/widgets/statistic.dart'; class CompetitionStatisticWidget extends StatefulWidget { final Weapon weapon; - final CompetitionDao competitionDao; - const CompetitionStatisticWidget({ - super.key, - required this.weapon, - required this.competitionDao, - }); + const CompetitionStatisticWidget({super.key, required this.weapon}); @override State createState() => @@ -35,14 +30,10 @@ class _CompetitionStatisticWidgetState @override Widget build(BuildContext context) { return Scaffold( - body: StreamBuilder>( - stream: widget.competitionDao - .findAllCompetitionForWeapon(widget.weapon.id!), + body: StreamBuilder?>( + stream: FirebaseDataService.getUserCompetitionsStream(), builder: (_, snapshot) { - if (!snapshot.hasData) { - return const SizedBox(); - } - if (snapshot.data.toString() == "[]") { + if (!snapshot.hasData || snapshot.data == null || snapshot.data!.isEmpty) { final ThemeData mode = Theme.of(context); return Center( child: Column( @@ -62,7 +53,27 @@ class _CompetitionStatisticWidgetState List dataWhole = []; List dataTenth = []; - final competitions = snapshot.requireData; + final competitionsData = snapshot.data!; + final competitions = competitionsData.entries + .where((entry) { + final data = Map.from(entry.value as Map); + return data['weaponId'] == widget.weapon.id!; + }) + .map((entry) { + final data = Map.from(entry.value as Map); + return Competition( + null, + DateTime.fromMillisecondsSinceEpoch(data['date']), + data['image'] ?? '', + data['place'] ?? '', + data['kind'] ?? '', + data['shotCount'] ?? 0, + data['shots'] ?? [], + data['comment'] ?? '', + data['weaponId'] ?? widget.weapon.id!, + ); + }).toList(); + for (var competition in competitions) { if (competition.shots.isNotEmpty) { var rings = competition.shots.reduce((value, next) => diff --git a/lib/views/data/data_device.dart b/lib/views/data/data_device.dart index faeede2..e2d7ecb 100644 --- a/lib/views/data/data_device.dart +++ b/lib/views/data/data_device.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; class DataDeviceWidget extends StatefulWidget { @@ -53,18 +53,14 @@ class _DataDeviceWidgetState extends State { : const Color(AppTheme.textColorDark), ), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString("data_device", value); + await FirebaseDataService.setPreference("data_device", value); }) ]) ]))))); } void _loadData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - setState(() { - _textDataDeviceController.text = prefs.getString("data_device") ?? ""; - }); + _textDataDeviceController.text = await FirebaseDataService.getPreference("data_device") ?? ""; + setState(() {}); } } diff --git a/lib/views/data/data_person.dart b/lib/views/data/data_person.dart index 3f8bf3d..2f21d17 100644 --- a/lib/views/data/data_person.dart +++ b/lib/views/data/data_person.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'package:image_pickers/image_pickers.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; class DataPersonWidget extends StatefulWidget { @@ -136,9 +136,7 @@ class _DataPersonWidgetState extends State { contentPadding: const EdgeInsets.all(10.0), labelText: tr("data_person_name")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString("data_person_name", value); + await FirebaseDataService.setPreference("data_person_name", value); }), TextFormField( controller: _textDataPersonAgeController, @@ -149,9 +147,7 @@ class _DataPersonWidgetState extends State { labelText: tr("data_person_age")), keyboardType: TextInputType.number, onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString("data_person_age", value); + await FirebaseDataService.setPreference("data_person_age", value); }), TextFormField( controller: _textDataPersonHightController, @@ -164,9 +160,7 @@ class _DataPersonWidgetState extends State { const TextInputType.numberWithOptions( decimal: true), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString("data_person_height", value); + await FirebaseDataService.setPreference("data_person_height", value); }) ]), CupertinoFormSection.insetGrouped( @@ -184,9 +178,7 @@ class _DataPersonWidgetState extends State { contentPadding: const EdgeInsets.all(10.0), labelText: tr("data_person_club_1")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString("data_person_club_1", value); + await FirebaseDataService.setPreference("data_person_club_1", value); }), TextFormField( controller: _textDataPersonClub2Controller, @@ -196,9 +188,7 @@ class _DataPersonWidgetState extends State { contentPadding: const EdgeInsets.all(10.0), labelText: tr("data_person_club_2")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString("data_person_club_2", value); + await FirebaseDataService.setPreference("data_person_club_2", value); }) ]), CupertinoFormSection.insetGrouped( @@ -216,9 +206,7 @@ class _DataPersonWidgetState extends State { contentPadding: const EdgeInsets.all(10.0), labelText: tr("data_person_trainer")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString("data_person_trainer", value); + await FirebaseDataService.setPreference("data_person_trainer", value); }), TextFormField( controller: @@ -230,10 +218,7 @@ class _DataPersonWidgetState extends State { labelText: tr("data_person_trainer_mail")), keyboardType: TextInputType.emailAddress, onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( - "data_person_trainer_mail", value); + await FirebaseDataService.setPreference("data_person_trainer_mail", value); }), TextFormField( controller: @@ -244,10 +229,7 @@ class _DataPersonWidgetState extends State { contentPadding: const EdgeInsets.all(10.0), labelText: tr("data_person_squadtrainer")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( - "data_person_squadtrainer", value); + await FirebaseDataService.setPreference("data_person_squadtrainer", value); }), TextFormField( controller: @@ -260,10 +242,7 @@ class _DataPersonWidgetState extends State { tr("data_person_squadtrainer_mail")), keyboardType: TextInputType.emailAddress, onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( - "data_person_squadtrainer_mail", value); + await FirebaseDataService.setPreference("data_person_squadtrainer_mail", value); }) ]) ]))))); @@ -273,11 +252,10 @@ class _DataPersonWidgetState extends State { Navigator.of(context).pop(null); ImagePickers.openCamera().then((Media? media) async { if (media != null) { - SharedPreferences prefs = await SharedPreferences.getInstance(); if (Platform.isIOS) { - prefs.setString("data_person_photo", media.path!.split("/").last); + await FirebaseDataService.setPreference("data_person_photo", media.path!.split("/").last); } else if (Platform.isAndroid) { - prefs.setString("data_person_photo", media.path!); + await FirebaseDataService.setPreference("data_person_photo", media.path!); } setState(() { imagePath = media.path; @@ -297,13 +275,10 @@ class _DataPersonWidgetState extends State { UIConfig(uiThemeColor: const Color(AppTheme.primaryColor)), cropConfig: CropConfig(enableCrop: false, width: 2, height: 1)) .then((List medias) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - if (Platform.isIOS) { - prefs.setString( - "data_person_photo", medias.first.path!.split("/").last); + await FirebaseDataService.setPreference("data_person_photo", medias.first.path!.split("/").last); } else if (Platform.isAndroid) { - prefs.setString("data_person_photo", medias.first.path); + await FirebaseDataService.setPreference("data_person_photo", medias.first.path!); } setState(() { imagePath = medias.first.path; @@ -312,47 +287,35 @@ class _DataPersonWidgetState extends State { } void _deleteImage() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.remove("data_person_photo"); + await FirebaseDataService.setPreference("data_person_photo", ""); setState(() { imagePath = ""; }); } void _loadData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); String directory = (await getApplicationDocumentsDirectory()).parent.path; - setState(() { - _textDataPersonNameController.text = - prefs.getString("data_person_name") ?? ""; - _textDataPersonAgeController.text = - prefs.getString("data_person_age") ?? ""; - _textDataPersonHightController.text = - prefs.getString("data_person_height") ?? ""; - _textDataPersonClub1Controller.text = - prefs.getString("data_person_club_1") ?? ""; - _textDataPersonClub2Controller.text = - prefs.getString("data_person_club_2") ?? ""; - _textDataPersonTrainerontroller.text = - prefs.getString("data_person_trainer") ?? ""; - _textDataPersonTrainerMailController.text = - prefs.getString("data_person_trainer_mail") ?? ""; - _textDataPersonSquadTrainerController.text = - prefs.getString("data_person_squadtrainer") ?? ""; - _textDataPersonSquadTrainerMailController.text = - prefs.getString("data_person_squadtrainer_mail") ?? ""; + _textDataPersonNameController.text = await FirebaseDataService.getPreference("data_person_name") ?? ""; + _textDataPersonAgeController.text = await FirebaseDataService.getPreference("data_person_age") ?? ""; + _textDataPersonHightController.text = await FirebaseDataService.getPreference("data_person_height") ?? ""; + _textDataPersonClub1Controller.text = await FirebaseDataService.getPreference("data_person_club_1") ?? ""; + _textDataPersonClub2Controller.text = await FirebaseDataService.getPreference("data_person_club_2") ?? ""; + _textDataPersonTrainerontroller.text = await FirebaseDataService.getPreference("data_person_trainer") ?? ""; + _textDataPersonTrainerMailController.text = await FirebaseDataService.getPreference("data_person_trainer_mail") ?? ""; + _textDataPersonSquadTrainerController.text = await FirebaseDataService.getPreference("data_person_squadtrainer") ?? ""; + _textDataPersonSquadTrainerMailController.text = await FirebaseDataService.getPreference("data_person_squadtrainer_mail") ?? ""; - // Get image path if there is one - if (prefs.getString("data_person_photo") != null && - prefs.getString("data_person_photo") != "") { - if (Platform.isIOS) { - imagePath = - "$directory/Documents/${prefs.getString("data_person_photo")}"; - } else if (Platform.isAndroid) { - imagePath = prefs.getString("data_person_photo"); - } + // Get image path if there is one + final photoPath = await FirebaseDataService.getPreference("data_person_photo"); + if (photoPath != null && photoPath.isNotEmpty) { + if (Platform.isIOS) { + imagePath = "$directory/Documents/$photoPath"; + } else if (Platform.isAndroid) { + imagePath = photoPath; } - }); + } + + setState(() {}); } } diff --git a/lib/views/discipline/discipine_type.dart b/lib/views/discipline/discipine_type.dart index fe9db1e..3dd4d0c 100644 --- a/lib/views/discipline/discipine_type.dart +++ b/lib/views/discipline/discipine_type.dart @@ -1,16 +1,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:shoot_report/models/type.dart'; -import 'package:shoot_report/services/type_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/views/discipline/discipline_type_row.dart'; class DisciplineTypeListView extends StatelessWidget { - final TypeDao typeDao; - final WeaponDao weaponDao; - - const DisciplineTypeListView( - {super.key, required this.typeDao, required this.weaponDao}); + const DisciplineTypeListView({super.key}); @override Widget build(BuildContext context) { @@ -29,21 +24,41 @@ class DisciplineTypeListView extends StatelessWidget { icon: const Icon(Icons.close, color: Colors.white)) ]), - body: StreamBuilder>( - stream: typeDao.findAllTypes(), + body: FutureBuilder?>( + future: FirebaseDataService.getGlobalTypes(), builder: (_, snapshot) { if (!snapshot.hasData) { - return const SizedBox(); + return const Center(child: CircularProgressIndicator()); + } + + final typesData = snapshot.data; + if (typesData == null || typesData.isEmpty) { + return const Center(child: Text('No types available')); } - final groups = snapshot.requireData; + + final types = typesData.entries.map((entry) { + final data = entry.value; + Map typeData; + if (data is Map) { + typeData = data; + } else if (data is Map) { + typeData = Map.from(data); + } else { + return null; + } + return Type(typeData['id'], typeData['name'], typeData['order']); + }).where((type) => type != null).cast().toList(); + + types.sort((a, b) => a.order.compareTo(b.order)); + return ListView.separated( - itemCount: groups.length, + itemCount: types.length, itemBuilder: (context, index) { - return DisciplineTypeListCell( - type: groups[index], weaponDao: weaponDao); + return DisciplineTypeListCell(type: types[index]); }, separatorBuilder: (context, index) => const Divider(height: 5)); - }))))); + }) + )))); } } diff --git a/lib/views/discipline/discipline_type_row.dart b/lib/views/discipline/discipline_type_row.dart index bb92347..c32a628 100644 --- a/lib/views/discipline/discipline_type_row.dart +++ b/lib/views/discipline/discipline_type_row.dart @@ -1,18 +1,12 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/type.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; import 'package:shoot_report/views/discipline/discipline_weapon.dart'; class DisciplineTypeListCell extends StatelessWidget { final Type type; - final WeaponDao weaponDao; - const DisciplineTypeListCell({ - super.key, - required this.type, - required this.weaponDao, - }); + const DisciplineTypeListCell({super.key, required this.type}); @override Widget build(BuildContext context) { @@ -24,8 +18,7 @@ class DisciplineTypeListCell extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => DisciplineWeaponListView( - type: type, weaponDao: weaponDao))); + builder: (context) => DisciplineWeaponListView(type: type))); }); } -} +} \ No newline at end of file diff --git a/lib/views/discipline/discipline_weapon.dart b/lib/views/discipline/discipline_weapon.dart index e4d361e..de67f27 100644 --- a/lib/views/discipline/discipline_weapon.dart +++ b/lib/views/discipline/discipline_weapon.dart @@ -1,46 +1,78 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:shoot_report/models/type.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/views/discipline/discipline_weapon_row.dart'; -import 'package:shoot_report/models/type.dart'; class DisciplineWeaponListView extends StatelessWidget { final Type type; - final WeaponDao weaponDao; - const DisciplineWeaponListView( - {super.key, required this.type, required this.weaponDao}); + const DisciplineWeaponListView({super.key, required this.type}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - automaticallyImplyLeading: true, - title: Text(tr(type.name)), - actions: [ - IconButton( - onPressed: () => - Navigator.of(context, rootNavigator: true).pop(), - icon: const Icon(Icons.close, color: Colors.white)) - ], - ), - body: Material( - child: StreamBuilder>( - stream: weaponDao.findAllWeaponsForType(type.id!), - builder: (_, snapshot) { - if (!snapshot.hasData) { - return const SizedBox(); - } - final weapons = snapshot.requireData; - return ListView.separated( - itemCount: weapons.length, - itemBuilder: (context, index) { - return DisciplineWeaponListCell( - weapon: weapons[index], weaponDao: weaponDao); - }, - separatorBuilder: (context, index) => - const Divider(height: 5)); - }))); + title: Text(tr(type.name)), + leading: const BackButton(color: Colors.white)), + body: FutureBuilder?>( + future: FirebaseDataService.getGlobalWeapons(), + builder: (_, snapshot) { + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + + final weaponsData = snapshot.data; + if (weaponsData == null || weaponsData.isEmpty) { + return const Center(child: Text('No weapons available')); + } + + final weapons = weaponsData.entries + .where((entry) { + final data = entry.value; + Map weaponData; + if (data is Map) { + weaponData = data; + } else if (data is Map) { + weaponData = Map.from(data); + } else { + return false; + } + return weaponData['typeId'] == type.id; + }) + .map((entry) { + final data = entry.value; + Map weaponData; + if (data is Map) { + weaponData = data; + } else if (data is Map) { + weaponData = Map.from(data); + } else { + return null; + } + return Weapon( + weaponData['id'], + weaponData['name'], + weaponData['order'], + weaponData['prefFile'], + weaponData['typeId'], + weaponData['show'] ?? true, + ); + }) + .where((weapon) => weapon != null) + .cast() + .toList(); + + weapons.sort((a, b) => a.order.compareTo(b.order)); + + return ListView.separated( + itemCount: weapons.length, + itemBuilder: (context, index) { + return DisciplineWeaponListCell(weapon: weapons[index]); + }, + separatorBuilder: (context, index) => + const Divider(height: 5)); + })); } -} +} \ No newline at end of file diff --git a/lib/views/discipline/discipline_weapon_row.dart b/lib/views/discipline/discipline_weapon_row.dart index a5167fa..a774641 100644 --- a/lib/views/discipline/discipline_weapon_row.dart +++ b/lib/views/discipline/discipline_weapon_row.dart @@ -1,33 +1,58 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; -class DisciplineWeaponListCell extends StatelessWidget { +class DisciplineWeaponListCell extends StatefulWidget { final Weapon weapon; - final WeaponDao weaponDao; - const DisciplineWeaponListCell({ - super.key, - required this.weapon, - required this.weaponDao, - }); + const DisciplineWeaponListCell({super.key, required this.weapon}); + + @override + State createState() => _DisciplineWeaponListCellState(); +} + +class _DisciplineWeaponListCellState extends State { + bool _isVisible = true; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadVisibility(); + } + + void _loadVisibility() async { + final visibility = await FirebaseDataService.getWeaponVisibility(widget.weapon.id.toString()); + if (mounted) { + setState(() { + _isVisible = visibility; + _isLoading = false; + }); + } + } @override Widget build(BuildContext context) { return ListTile( - leading: - Image.asset("assets/images/disciplines_weapon.png", height: 16), - title: Text(tr(weapon.name)), - trailing: IconButton( - onPressed: () { - _updateFavorite(weapon); - }, - icon: Icon(weapon.show ? Icons.star : Icons.star_outline))); + leading: Image.asset("assets/images/disciplines_weapon.png", height: 16), + title: Text(tr(widget.weapon.name)), + trailing: _isLoading + ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) + : IconButton( + onPressed: () { + _updateFavorite(); + }, + icon: Icon(_isVisible ? Icons.star : Icons.star_outline))); } - void _updateFavorite(Weapon weapon) { - weapon.show = !weapon.show; - weaponDao.updateWeapon(weapon); + void _updateFavorite() async { + final newVisibility = !_isVisible; + await FirebaseDataService.setWeaponVisibility(widget.weapon.id.toString(), newVisibility); + if (mounted) { + setState(() { + _isVisible = newVisibility; + }); + } } } diff --git a/lib/views/goals/goals_tenth.dart b/lib/views/goals/goals_tenth.dart index c5409e9..4494b7a 100644 --- a/lib/views/goals/goals_tenth.dart +++ b/lib/views/goals/goals_tenth.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:shoot_report/models/weapon.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; class GoalsTenthWidget extends StatefulWidget { @@ -73,9 +73,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_jackpot")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_40_jackpot", value); }), @@ -95,9 +93,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_optimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_40_optimal", value); }), @@ -117,9 +113,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_real")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_40_real", value); }), @@ -139,9 +133,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_minimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_40_minimal", value); }), @@ -161,9 +153,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_chaos")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_40_chaos", value); }) @@ -191,9 +181,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_jackpot")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_60_jackpot", value); }), @@ -213,9 +201,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_optimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_60_optimal", value); }), @@ -235,9 +221,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_real")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_60_real", value); }), @@ -257,9 +241,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_minimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_60_minimal", value); }), @@ -279,9 +261,7 @@ class _GoalsTenthWidgetState extends State { ), labelText: tr("goals_tenth_chaos")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalTenth_60_chaos", value); }) @@ -290,34 +270,26 @@ class _GoalsTenthWidgetState extends State { } void _loadData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - setState(() { - _text40JackpotController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_40_jackpot") ?? - ""; - _text40OptimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_40_optimal") ?? - ""; - _text40RealController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_40_real") ?? ""; - _text40MinimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_40_minimal") ?? - ""; - _text40ChaosController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_40_chaos") ?? ""; - _text60JackpotController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_60_jackpot") ?? - ""; - _text60OptimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_60_optimal") ?? - ""; - _text60RealController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_60_real") ?? ""; - _text60MinimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_60_minimal") ?? - ""; - _text60ChaosController.text = - prefs.getString("${widget.weapon.prefFile}_goalTenth_60_chaos") ?? ""; - }); + _text40JackpotController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_40_jackpot") ?? ""; + _text40OptimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_40_optimal") ?? ""; + _text40RealController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_40_real") ?? ""; + _text40MinimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_40_minimal") ?? ""; + _text40ChaosController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_40_chaos") ?? ""; + _text60JackpotController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_60_jackpot") ?? ""; + _text60OptimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_60_optimal") ?? ""; + _text60RealController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_60_real") ?? ""; + _text60MinimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_60_minimal") ?? ""; + _text60ChaosController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalTenth_60_chaos") ?? ""; + setState(() {}); } } diff --git a/lib/views/goals/goals_whole.dart b/lib/views/goals/goals_whole.dart index 54eea7c..d1df72d 100644 --- a/lib/views/goals/goals_whole.dart +++ b/lib/views/goals/goals_whole.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:shoot_report/models/weapon.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; class GoalsWholeWidget extends StatefulWidget { @@ -78,9 +78,7 @@ class _GoalsWholeWidgetState extends State { labelText: tr("goals_whole_jackpot"), ), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_40_jackpot", value); }), @@ -100,9 +98,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_optimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_40_optimal", value); }), @@ -122,9 +118,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_real")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_40_real", value); }), @@ -144,9 +138,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_minimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_40_minimal", value); }), @@ -166,9 +158,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_chaos")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_40_chaos", value); }) @@ -196,9 +186,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_jackpot")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_60_jackpot", value); }), @@ -218,9 +206,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_optimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_60_optimal", value); }), @@ -240,9 +226,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_real")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_60_real", value); }), @@ -262,9 +246,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_minimal")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_60_minimal", value); }), @@ -284,9 +266,7 @@ class _GoalsWholeWidgetState extends State { ), labelText: tr("goals_whole_chaos")), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_goalWhole_60_chaos", value); }) @@ -295,34 +275,26 @@ class _GoalsWholeWidgetState extends State { } void _loadData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - setState(() { - _textWhole40JackpotController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_40_jackpot") ?? - ""; - _textWhole40OptimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_40_optimal") ?? - ""; - _textWhole40RealController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_40_real") ?? ""; - _textWhole40MinimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_40_minimal") ?? - ""; - _textWhole40ChaosController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_40_chaos") ?? ""; - _textWhole60JackpotController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_60_jackpot") ?? - ""; - _textWhole60OptimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_60_optimal") ?? - ""; - _textWhole60RealController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_60_real") ?? ""; - _textWhole60MinimalController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_60_minimal") ?? - ""; - _textWhole60ChaosController.text = - prefs.getString("${widget.weapon.prefFile}_goalWhole_60_chaos") ?? ""; - }); + _textWhole40JackpotController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_40_jackpot") ?? ""; + _textWhole40OptimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_40_optimal") ?? ""; + _textWhole40RealController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_40_real") ?? ""; + _textWhole40MinimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_40_minimal") ?? ""; + _textWhole40ChaosController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_40_chaos") ?? ""; + _textWhole60JackpotController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_60_jackpot") ?? ""; + _textWhole60OptimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_60_optimal") ?? ""; + _textWhole60RealController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_60_real") ?? ""; + _textWhole60MinimalController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_60_minimal") ?? ""; + _textWhole60ChaosController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_goalWhole_60_chaos") ?? ""; + setState(() {}); } } diff --git a/lib/views/home.dart b/lib/views/home.dart index 3f8c074..418a55d 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -1,9 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; + import 'package:shoot_report/views/competition/competition.dart'; import 'package:shoot_report/views/data/data.dart'; import 'package:shoot_report/views/goals/goals.dart'; @@ -15,17 +13,8 @@ import 'package:shoot_report/widgets/popup_menu.dart'; class HomeWidget extends StatefulWidget { final Weapon weapon; - final WeaponDao weaponDao; - final TrainingDao trainingDao; - final CompetitionDao competitionDao; - const HomeWidget({ - super.key, - required this.weapon, - required this.weaponDao, - required this.trainingDao, - required this.competitionDao, - }); + const HomeWidget({super.key, required this.weapon}); @override State createState() => _HomeWidgetState(); @@ -43,15 +32,8 @@ class _HomeWidgetState extends State { @override Widget build(BuildContext context) { List widgetOptions = [ - TrainingWidget( - weapon: widget.weapon, - weaponDao: widget.weaponDao, - trainingDao: widget.trainingDao), - CompetitionWidget( - weapon: widget.weapon, - weaponDao: widget.weaponDao, - competitionDao: widget.competitionDao, - ), + TrainingWidget(weapon: widget.weapon), + CompetitionWidget(weapon: widget.weapon), ProcedureWidget(weapon: widget.weapon), GoalsWidget(weapon: widget.weapon) ]; diff --git a/lib/views/procedure/procedure_preparation.dart b/lib/views/procedure/procedure_preparation.dart index b5c4a82..8c64882 100644 --- a/lib/views/procedure/procedure_preparation.dart +++ b/lib/views/procedure/procedure_preparation.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:shoot_report/models/weapon.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; class ProcedurePreparationWidget extends StatefulWidget { @@ -58,9 +58,7 @@ class _ProcedurePreparationWidgetState : const Color(AppTheme.textColorDark), ), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_procedure_before", value); }) @@ -69,10 +67,8 @@ class _ProcedurePreparationWidgetState } void _loadData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - setState(() { - _textPreparationController.text = - prefs.getString("${widget.weapon.prefFile}_procedure_before") ?? ""; - }); + _textPreparationController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_procedure_before") ?? ""; + setState(() {}); } } diff --git a/lib/views/procedure/procedure_shot.dart b/lib/views/procedure/procedure_shot.dart index 41fecdf..49dc9d3 100644 --- a/lib/views/procedure/procedure_shot.dart +++ b/lib/views/procedure/procedure_shot.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:shoot_report/models/weapon.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; class ProcedureShotWidget extends StatefulWidget { @@ -55,9 +55,7 @@ class _ProcedureShotWidgetState extends State { : const Color(AppTheme.textColorDark), ), onChanged: (value) async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setString( + await FirebaseDataService.setPreference( "${widget.weapon.prefFile}_procedure_shot", value); }) @@ -66,10 +64,8 @@ class _ProcedureShotWidgetState extends State { } void _loadData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - setState(() { - _textShotController.text = - prefs.getString("${widget.weapon.prefFile}_procedure_shot") ?? ""; - }); + _textShotController.text = + await FirebaseDataService.getPreference("${widget.weapon.prefFile}_procedure_shot") ?? ""; + setState(() {}); } } diff --git a/lib/views/training/training.dart b/lib/views/training/training.dart index dc3411c..4163c90 100644 --- a/lib/views/training/training.dart +++ b/lib/views/training/training.dart @@ -1,22 +1,15 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; + import 'package:shoot_report/utilities/theme.dart'; import 'package:shoot_report/views/training/training_list.dart'; import 'package:shoot_report/views/training/training_statistics.dart'; class TrainingWidget extends StatelessWidget { final Weapon weapon; - final WeaponDao weaponDao; - final TrainingDao trainingDao; - const TrainingWidget( - {super.key, - required this.weapon, - required this.weaponDao, - required this.trainingDao}); + const TrainingWidget({super.key, required this.weapon}); @override Widget build(BuildContext context) { @@ -34,9 +27,8 @@ class TrainingWidget extends StatelessWidget { ])), Flexible( child: TabBarView(children: [ - TrainingListWidget( - weapon: weapon, weaponDao: weaponDao, trainingDao: trainingDao), - TrainingStatisticWidget(weapon: weapon, trainingDao: trainingDao) + TrainingListWidget(weapon: weapon), + TrainingStatisticWidget(weapon: weapon) ])) ])); } diff --git a/lib/views/training/training_add.dart b/lib/views/training/training_add.dart index 6e1fee8..b05f5e7 100644 --- a/lib/views/training/training_add.dart +++ b/lib/views/training/training_add.dart @@ -6,9 +6,9 @@ import 'package:flutter/material.dart'; import 'package:image_pickers/image_pickers.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:quickalert/quickalert.dart'; -import 'package:shoot_report/models/training.dart'; + import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/training_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/utilities/indicator_to_image.dart'; import 'package:shoot_report/utilities/kind_list.dart'; @@ -16,10 +16,8 @@ import 'package:shoot_report/utilities/theme.dart'; class TrainingAddWidget extends StatefulWidget { final Weapon weapon; - final TrainingDao trainingDao; - const TrainingAddWidget( - {super.key, required this.weapon, required this.trainingDao}); + const TrainingAddWidget({super.key, required this.weapon}); @override State createState() => _TrainingAddWidgetState(); @@ -360,20 +358,19 @@ class _TrainingAddWidgetState extends State { )); } - void _addTraining() { - var training = Training( - null, - date, - Platform.isIOS ? imagePath.split("/").last : imagePath, - indicator, - place, - kind, - shotCount, - shots, - comment, - widget.weapon.id!); - - widget.trainingDao.insertTraining(training); + void _addTraining() async { + await FirebaseDataService.saveTraining({ + 'date': date.millisecondsSinceEpoch, + 'image': Platform.isIOS ? imagePath.split("/").last : imagePath, + 'indicator': indicator, + 'place': place, + 'kind': kind, + 'shotCount': shotCount, + 'shots': shots, + 'comment': comment, + 'weaponId': widget.weapon.id!, + }); + HapticFeedback.heavyImpact(); QuickAlert.show( context: context, diff --git a/lib/views/training/training_edit.dart b/lib/views/training/training_edit.dart index c978b19..307fad8 100644 --- a/lib/views/training/training_edit.dart +++ b/lib/views/training/training_edit.dart @@ -10,7 +10,7 @@ import 'package:quickalert/quickalert.dart'; import 'package:share_plus/share_plus.dart'; import 'package:shoot_report/models/training.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/training_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/csv_converter.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/utilities/indicator_to_image.dart'; @@ -19,13 +19,11 @@ import 'package:shoot_report/utilities/theme.dart'; class TrainingEditWidget extends StatefulWidget { final Weapon weapon; - final TrainingDao trainingDao; final Training training; const TrainingEditWidget( {super.key, required this.weapon, - required this.trainingDao, required this.training}); @override @@ -414,20 +412,20 @@ class _TrainingEditWidgetState extends State { )); } - void _editTraining() { - widget.training.date = date; - if (Platform.isIOS) { - widget.training.image = imagePath.split("/").last; - } else { - widget.training.image = imagePath; + void _editTraining() async { + if (widget.training.firebaseKey != null) { + await FirebaseDataService.updateTraining(widget.training.firebaseKey!, { + 'date': date.millisecondsSinceEpoch, + 'image': Platform.isIOS ? imagePath.split("/").last : imagePath, + 'indicator': indicator, + 'place': place, + 'kind': kind, + 'shotCount': shotCount, + 'shots': shots, + 'comment': comment, + 'weaponId': widget.weapon.id!, + }); } - widget.training.indicator = indicator; - widget.training.place = place; - widget.training.kind = kind; - widget.training.shotCount = shotCount; - widget.training.shots = shots; - widget.training.comment = comment; - widget.trainingDao.updateTraining(widget.training); HapticFeedback.heavyImpact(); QuickAlert.show( diff --git a/lib/views/training/training_list.dart b/lib/views/training/training_list.dart index 9f05110..623c64c 100644 --- a/lib/views/training/training_list.dart +++ b/lib/views/training/training_list.dart @@ -3,33 +3,31 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:shoot_report/models/training.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; import 'package:shoot_report/views/training/training_add.dart'; import 'package:shoot_report/views/training/training_row.dart'; class TrainingListWidget extends StatelessWidget { final Weapon weapon; - final WeaponDao weaponDao; - final TrainingDao trainingDao; - const TrainingListWidget( - {super.key, - required this.weapon, - required this.weaponDao, - required this.trainingDao}); + const TrainingListWidget({super.key, required this.weapon}); @override Widget build(BuildContext context) { return Scaffold( - body: StreamBuilder>( - stream: trainingDao.findAllTrainingsForWeapon(weapon.id!), + body: StreamBuilder?>( + stream: FirebaseDataService.getUserTrainingsStream(), builder: (_, snapshot) { - if (!snapshot.hasData) { - return const SizedBox(); + + + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); } - if (snapshot.data.toString() == "[]") { + + if (!snapshot.hasData || + snapshot.data == null || + snapshot.data!.isEmpty) { final ThemeData mode = Theme.of(context); return Center( child: Column( @@ -49,15 +47,56 @@ class TrainingListWidget extends StatelessWidget { ])); } - final trainings = snapshot.requireData; + final trainingsData = snapshot.data!; + final trainings = trainingsData.entries.where((entry) { + final data = Map.from(entry.value as Map); + return data['weaponId'] == weapon.id!; + }).map((entry) { + final data = Map.from(entry.value as Map); + final training = Training( + null, + DateTime.fromMillisecondsSinceEpoch(data['date']), + data['image'] ?? '', + data['indicator'] ?? 2, + data['place'] ?? '', + data['kind'] ?? '', + data['shotCount'] ?? 0, + data['shots'] ?? [], + data['comment'] ?? '', + data['weaponId'] ?? weapon.id!, + ); + // Store Firebase key for deletion + training.firebaseKey = entry.key; + return training; + }).toList(); + + trainings.sort((a, b) => b.date.compareTo(a.date)); + + if (trainings.isEmpty) { + final ThemeData mode = Theme.of(context); + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.fitness_center, + color: (mode.brightness == Brightness.light) + ? const Color(AppTheme.primaryColor) + : const Color(AppTheme.backgroundLight), + size: 120, + ), + Text( + tr("training_data_no"), + textAlign: TextAlign.center, + ) + ])); + } return ListView.separated( itemCount: trainings.length, itemBuilder: (context, index) { return TrainingListRow( - weapon: weapon, - trainingDao: trainingDao, - training: trainings[index]); + weapon: weapon, training: trainings[index]); }, separatorBuilder: (context, index) { return const Divider(height: 0); @@ -68,8 +107,7 @@ class TrainingListWidget extends StatelessWidget { showBarModalBottomSheet( context: context, expand: true, - builder: (context) => TrainingAddWidget( - weapon: weapon, trainingDao: trainingDao)); + builder: (context) => TrainingAddWidget(weapon: weapon)); }, backgroundColor: const Color(AppTheme.accentColor), child: const Icon(Icons.add))); diff --git a/lib/views/training/training_row.dart b/lib/views/training/training_row.dart index a8a36cc..ad66306 100644 --- a/lib/views/training/training_row.dart +++ b/lib/views/training/training_row.dart @@ -3,19 +3,17 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:shoot_report/models/training.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/training_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/indicator_to_image.dart'; import 'package:shoot_report/views/training/training_edit.dart'; class TrainingListRow extends StatefulWidget { final Weapon weapon; - final TrainingDao trainingDao; final Training training; const TrainingListRow({ super.key, required this.weapon, - required this.trainingDao, required this.training, }); @@ -50,7 +48,6 @@ class _TrainingListRowState extends State { expand: true, builder: (context) => TrainingEditWidget( weapon: widget.weapon, - trainingDao: widget.trainingDao, training: widget.training), ); }); @@ -65,17 +62,21 @@ class _TrainingListRowState extends State { content: Text(tr("training_alert_message")), actions: [ TextButton( - onPressed: () { - widget.trainingDao.deleteTraining(widget.training); - final scaffoldMessengerState = - ScaffoldMessenger.of(context); - scaffoldMessengerState.hideCurrentSnackBar(); - scaffoldMessengerState.showSnackBar( - SnackBar( - content: Text(tr("training_removed")), - behavior: SnackBarBehavior.floating), - ); + onPressed: () async { Navigator.of(context).pop(); + if (widget.training.firebaseKey != null) { + await FirebaseDataService.deleteTraining(widget.training.firebaseKey!); + } + if (mounted) { + final scaffoldMessengerState = + ScaffoldMessenger.of(context); + scaffoldMessengerState.hideCurrentSnackBar(); + scaffoldMessengerState.showSnackBar( + SnackBar( + content: Text(tr("training_removed")), + behavior: SnackBarBehavior.floating), + ); + } }, child: Text(tr("general_yes"))), TextButton( diff --git a/lib/views/training/training_statistics.dart b/lib/views/training/training_statistics.dart index 0c66184..2bb3a0b 100644 --- a/lib/views/training/training_statistics.dart +++ b/lib/views/training/training_statistics.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/training.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/training_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/chart_data.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/utilities/theme.dart'; @@ -10,13 +10,8 @@ import 'package:shoot_report/widgets/statistic.dart'; class TrainingStatisticWidget extends StatefulWidget { final Weapon weapon; - final TrainingDao trainingDao; - const TrainingStatisticWidget({ - super.key, - required this.weapon, - required this.trainingDao, - }); + const TrainingStatisticWidget({super.key, required this.weapon}); @override State createState() => @@ -34,14 +29,10 @@ class _TrainingStatisticWidgetState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: StreamBuilder>( - stream: - widget.trainingDao.findAllTrainingsForWeapon(widget.weapon.id!), + body: StreamBuilder?>( + stream: FirebaseDataService.getUserTrainingsStream(), builder: (_, snapshot) { - if (!snapshot.hasData) { - return const SizedBox(); - } - if (snapshot.data.toString() == "[]") { + if (!snapshot.hasData || snapshot.data == null || snapshot.data!.isEmpty) { final ThemeData mode = Theme.of(context); return Center( child: Column( @@ -63,7 +54,28 @@ class _TrainingStatisticWidgetState extends State { List dataWhole = []; List dataTenth = []; - final trainings = snapshot.requireData; + final trainingsData = snapshot.data!; + final trainings = trainingsData.entries + .where((entry) { + final data = Map.from(entry.value as Map); + return data['weaponId'] == widget.weapon.id!; + }) + .map((entry) { + final data = Map.from(entry.value as Map); + return Training( + null, + DateTime.fromMillisecondsSinceEpoch(data['date']), + data['image'] ?? '', + data['indicator'] ?? 2, + data['place'] ?? '', + data['kind'] ?? '', + data['shotCount'] ?? 0, + data['shots'] ?? [], + data['comment'] ?? '', + data['weaponId'] ?? widget.weapon.id!, + ); + }).toList(); + for (var training in trainings) { if (training.shots.isNotEmpty) { var rings = training.shots.reduce((value, next) => diff --git a/lib/views/weapon/weapon.dart b/lib/views/weapon/weapon.dart index 50f5014..76cf369 100644 --- a/lib/views/weapon/weapon.dart +++ b/lib/views/weapon/weapon.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/type_dao.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; + import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/views/discipline/discipine_type.dart'; import 'package:shoot_report/views/weapon/weapon_list.dart'; @@ -12,18 +9,7 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:shoot_report/widgets/popup_menu.dart'; class WeaponWidget extends StatefulWidget { - final TypeDao typeDao; - final WeaponDao weaponDao; - final TrainingDao trainingDao; - final CompetitionDao competitionDao; - - const WeaponWidget({ - super.key, - required this.typeDao, - required this.weaponDao, - required this.trainingDao, - required this.competitionDao, - }); + const WeaponWidget({super.key}); @override State createState() => _WeaponWidgetState(); @@ -52,18 +38,12 @@ class _WeaponWidgetState extends State { context: context, expand: true, enableDrag: true, - builder: (context) => DisciplineTypeListView( - typeDao: widget.typeDao, - weaponDao: widget.weaponDao)); + builder: (context) => const DisciplineTypeListView()); }), const PopupMenuWidget() ]), body: Column(children: [ - WeaponListView( - weaponDao: widget.weaponDao, - trainingDao: widget.trainingDao, - competitionDao: widget.competitionDao, - ) + const WeaponListView() ]), bottomNavigationBar: const AdsWidget()); } diff --git a/lib/views/weapon/weapon_list.dart b/lib/views/weapon/weapon_list.dart index 89b2ded..1abfeee 100644 --- a/lib/views/weapon/weapon_list.dart +++ b/lib/views/weapon/weapon_list.dart @@ -1,34 +1,42 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; +import 'package:shoot_report/services/firebase_data_service.dart'; import 'package:shoot_report/utilities/theme.dart'; import 'package:shoot_report/views/weapon/weapon_row.dart'; class WeaponListView extends StatelessWidget { - final WeaponDao weaponDao; - final TrainingDao trainingDao; - final CompetitionDao competitionDao; - - const WeaponListView({ - super.key, - required this.weaponDao, - required this.trainingDao, - required this.competitionDao, - }); + const WeaponListView({super.key}); @override Widget build(BuildContext context) { return Expanded( - child: StreamBuilder>( - stream: weaponDao.findAllWeaponsDistinction(true), + child: StreamBuilder>>( + stream: FirebaseDataService.getVisibleWeaponsStream(), builder: (_, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.error, size: 64, color: Colors.red), + const SizedBox(height: 16), + const Text('Connection Error'), + ], + ), + ); + } + if (!snapshot.hasData) { - return const SizedBox(); + return const Center(child: CircularProgressIndicator()); } - if (snapshot.data.toString() == "[]") { + + final weaponsData = snapshot.data!; + if (weaponsData.isEmpty) { final ThemeData mode = Theme.of(context); return Center( child: Column( @@ -47,18 +55,24 @@ class WeaponListView extends StatelessWidget { ) ])); } - final weapons = snapshot.requireData; + + final weapons = weaponsData.map((data) { + return Weapon( + data['id'], + data['name'], + data['order'], + data['prefFile'], + data['typeId'], + true, // Always true for visible weapons + ); + }).toList(); + return ListView.separated( itemCount: weapons.length, itemBuilder: (context, index) { - return WeaponListCell( - weapon: weapons[index], - weaponDao: weaponDao, - trainingDao: trainingDao, - competitionDao: competitionDao); + return WeaponListCell(weapon: weapons[index]); }, - separatorBuilder: (context, index) => - const Divider(height: 5)); + separatorBuilder: (context, index) => const Divider(height: 5)); })); } } diff --git a/lib/views/weapon/weapon_row.dart b/lib/views/weapon/weapon_row.dart index 9c07ca7..1a63c56 100644 --- a/lib/views/weapon/weapon_row.dart +++ b/lib/views/weapon/weapon_row.dart @@ -1,24 +1,13 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:shoot_report/models/weapon.dart'; -import 'package:shoot_report/services/competition_dao.dart'; -import 'package:shoot_report/services/training_dao.dart'; -import 'package:shoot_report/services/weapon_dao.dart'; + import 'package:shoot_report/views/home.dart'; class WeaponListCell extends StatelessWidget { final Weapon weapon; - final WeaponDao weaponDao; - final TrainingDao trainingDao; - final CompetitionDao competitionDao; - const WeaponListCell({ - super.key, - required this.weapon, - required this.weaponDao, - required this.trainingDao, - required this.competitionDao, - }); + const WeaponListCell({super.key, required this.weapon}); @override Widget build(BuildContext context) { @@ -30,12 +19,7 @@ class WeaponListCell extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => HomeWidget( - weapon: weapon, - weaponDao: weaponDao, - trainingDao: trainingDao, - competitionDao: competitionDao, - ))); + builder: (context) => HomeWidget(weapon: weapon))); }); } } diff --git a/lib/widgets/popup_menu.dart b/lib/widgets/popup_menu.dart index 4b3290f..5c0d9be 100644 --- a/lib/widgets/popup_menu.dart +++ b/lib/widgets/popup_menu.dart @@ -1,14 +1,7 @@ -import 'dart:io'; - import 'package:easy_localization/easy_localization.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:file_saver/file_saver.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; -import 'package:quickalert/quickalert.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:shoot_report/main.dart'; +import 'package:shoot_report/services/auth_service.dart'; import 'package:shoot_report/utilities/firebase_log.dart'; import 'package:shoot_report/widgets/cooperation.dart'; import 'package:shoot_report/widgets/information.dart'; @@ -52,13 +45,9 @@ class _PopupMenuWidget extends State { child: Text(tr("menu_instagram")), ), /*PopupMenuItem( - value: 5, - child: Text(tr("menu_import")), - ),*/ - PopupMenuItem( - value: 6, - child: Text(tr("menu_export")), - ) + value: 5, + child: Text(tr("menu_create_account")), + ),*/ ], onSelected: (item) { switch (item) { @@ -98,53 +87,127 @@ class _PopupMenuWidget extends State { ); break; case 5: - FirebaseLog().logEvent("Import Database"); - importDatabase(); - break; - case 6: - FirebaseLog().logEvent("Export Database"); - Share.shareXFiles([XFile(database.database.database.path)], - text: tr("training_share_text")); + FirebaseLog().logEvent("Create Account"); + _showCreateAccountDialog(context); break; } }); } - void importDatabase() async { - // Get the new database - FilePickerResult? result = - await FilePicker.platform.pickFiles(withData: true); + void _showCreateAccountDialog(BuildContext context) { + final emailController = TextEditingController(); + final passwordController = TextEditingController(); - if (result != null) { - PlatformFile file = result.files.first; - if (file.extension == "db") { - Uint8List? fileBytes = result.files.first.bytes; + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(tr("create_account_title")), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: emailController, + decoration: InputDecoration( + labelText: tr("email"), + hintText: "user@example.com", + ), + keyboardType: TextInputType.emailAddress, + ), + const SizedBox(height: 16), + TextField( + controller: passwordController, + decoration: InputDecoration( + labelText: tr("password"), + ), + obscureText: true, + ), + const SizedBox(height: 16), + Text( + tr("create_account_info"), + style: const TextStyle(fontSize: 12, color: Colors.grey), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(tr("general_cancel")), + ), + TextButton( + onPressed: () async { + final email = emailController.text.trim(); + final password = passwordController.text; - String path = await FileSaver.instance.saveFile( - name: "flutter_shoot_report.db", - bytes: fileBytes, - ); + if (email.isEmpty || password.isEmpty) { + return; + } + + Navigator.of(context).pop(); + await _createAccount(context, email, password); + }, + child: Text(tr("create_account_button")), + ), + ], + ), + ); + } - // Move file - await File(path).copy(database.database.database.path); + Future _createAccount( + BuildContext context, String email, String password) async { + try { + // Show loading + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const AlertDialog( + content: Row( + children: [ + CircularProgressIndicator(), + SizedBox(width: 16), + Text("Creating account..."), + ], + ), + ), + ); - if (mounted) { - QuickAlert.show( - context: context, - type: QuickAlertType.success, - title: tr("import_database_alert_title"), - text: tr("import_database_alert_message"), - ); - } - } else { - if (mounted) { - QuickAlert.show( - context: context, - type: QuickAlertType.success, - title: tr("import_database_alert_error_title"), - text: tr("import_database_alert_error_message"), - ); - } + await AuthService.createAccountAndMigrateData(email, password); + + if (context.mounted) { + Navigator.of(context).pop(); // Close loading + + // Show success + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(tr("success")), + content: Text(tr("account_created_success")), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(tr("general_ok")), + ), + ], + ), + ); + } + } catch (e) { + if (context.mounted) { + Navigator.of(context).pop(); // Close loading + + // Show error + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(tr("error")), + content: Text(tr("account_creation_failed")), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(tr("general_ok")), + ), + ], + ), + ); } } } diff --git a/pubspec.lock b/pubspec.lock index 6b73834..4b52a14 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,30 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" - url: "https://pub.dev" - source: hosted - version: "67.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: bb84ee51e527053dd8e25ecc9f97a6abfdc19130fb4d883e4e8585e23e7e6dd8 - url: "https://pub.dev" - source: hosted - version: "1.3.60" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: "948f7d74f41dd6f2d563ea9f4c21d7ea764f8e047d2b24138974c19c24d37eb6" url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "1.3.61" ansicolor: dependency: transitive description: @@ -65,70 +49,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" - url: "https://pub.dev" - source: hosted - version: "1.1.2" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" - url: "https://pub.dev" - source: hosted - version: "2.4.13" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 - url: "https://pub.dev" - source: hosted - version: "7.3.2" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: ba95c961bafcd8686d1cf63be864eb59447e795e124d98d6a27d91fcd13602fb - url: "https://pub.dev" - source: hosted - version: "8.11.1" characters: dependency: transitive description: @@ -137,22 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a - url: "https://pub.dev" - source: hosted - version: "1.4.0" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" - source: hosted - version: "2.0.4" clock: dependency: transitive description: @@ -161,14 +65,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" - url: "https://pub.dev" - source: hosted - version: "4.10.1" collection: dependency: transitive description: @@ -177,14 +73,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" cross_file: dependency: transitive description: @@ -225,14 +113,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" - url: "https://pub.dev" - source: hosted - version: "2.3.6" dbus: dependency: transitive description: @@ -241,14 +121,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.11" - dev_build: - dependency: transitive - description: - name: dev_build - sha256: fda8a54458b2a873a84e0cd1513f4323a1fb0599ed5455245359bc0398bad9ee - url: "https://pub.dev" - source: hosted - version: "1.1.2+11" dio: dependency: transitive description: @@ -325,34 +197,58 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: "07146e89e11302c6b07e3465c2c556ebcdd0053a3c5b1aa9bfd3203b778e5b4c" + sha256: dde9d6a7b69b07551a77cfb913c81c64804f7602b07541328322c321e73f2a0e url: "https://pub.dev" source: hosted - version: "12.0.0" + version: "12.0.1" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "27e81a0efc821bec6cba64abc1083b91c8ddbad28eeb4c6f6b7c78a59d06f259" + sha256: "4008d82a58edcbedec34a7b39f457eed24181cb9c89782c104828c42e4c859b2" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.0.1" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "7d87f47462042a7d9125e3123db2783bc72917d85e2719d4cb6aeaec209605e1" + sha256: db2a2e8803f5471a5f89b4abacae95ae27e0644f77526879fb81a2c1abc12b5f + url: "https://pub.dev" + source: hosted + version: "0.6.0+1" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + sha256: b843f8b6897654899bfc437e385f710f79fdf1fe872ce629cbdf6017772d5803 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + sha256: c7acba5e95dc8095149c4534e968d61099896f1a09421d46d7c3fc3069082a37 + url: "https://pub.dev" + source: hosted + version: "8.1.1" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + sha256: "083c57b761b33f766824d7835c2b47abacbc4d82c7193e91442d77b983675b28" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "6.0.2" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "6b343e6f7b72a4f32d7ce8df8c9a28d8f54b4ac20d7c6500f3e8b3969afca457" + sha256: "967dae9a65f69377beb9f4ab292ea63ce5befa1ce24682cab1b69ca4b7a46927" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.1.0" firebase_core_platform_interface: dependency: transitive description: @@ -365,90 +261,82 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "5d28b14dd32282fb7ce2b22b897362453755b6b8541d491127dc72b755bb7b16" + sha256: f7ee08febc1c4451588ce58ffcf28edaee857e9a196fee88b85deb889990094a url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.1.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "95b6871850b1a7e3b09c284c59a0c71fafcad3eee8ac1b6f06aaf8979290cbb8" + sha256: f2e175a967712ee1f616ab8843390891a315428ba497ce3d256d4c46f32db6f8 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.0.1" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: ba5b7a916f1ebedc6db35b33abdc618f202fc25e0792088dfba698e19fec9c09 + sha256: b49b90af4a1fd8f30b58abd90af88371969bea51b62838a4f4e737c2098b725e url: "https://pub.dev" source: hosted - version: "3.8.11" - firebase_performance: + version: "3.8.12" + firebase_database: dependency: "direct main" description: - name: firebase_performance - sha256: "3db95272fe954e8c6b1ff8a1b0ef38f35093da7f3ee909a6608eee3f067d4b99" + name: firebase_database + sha256: adc9e1c79528e9ee9c8503b2f0c7344e90d79a0f4853cde93a2a5ee8a4f5d35a url: "https://pub.dev" source: hosted - version: "0.11.0" - firebase_performance_platform_interface: - dependency: transitive - description: - name: firebase_performance_platform_interface - sha256: b9650cbe9b1c8c8511bb052c3d1e5753449ef6ea7a15ea33670340fcfa5821fe - url: "https://pub.dev" - source: hosted - version: "0.1.5+11" - firebase_performance_web: + version: "12.0.1" + firebase_database_platform_interface: dependency: transitive description: - name: firebase_performance_web - sha256: bf7de20205793ec52fe8b0ec7cf76e811041e6584676e4e9938f69676ed854e1 + name: firebase_database_platform_interface + sha256: "2ef66d5fa062a6999a93e2a6428684b51c99aec726e7a57a1cee8db46fb451d3" url: "https://pub.dev" source: hosted - version: "0.1.7+17" - fixnum: + version: "0.2.6+12" + firebase_database_web: dependency: transitive description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + name: firebase_database_web + sha256: "40564fd2ef33bf3bdb93e098c017af694620cc0126a567b183414bc6aeee5186" url: "https://pub.dev" source: hosted - version: "1.1.1" - floor: + version: "0.2.6+18" + firebase_performance: dependency: "direct main" description: - name: floor - sha256: c1b06023912b5b8e49deb6a9d867863c535ae1a232d991c3582bba3ee8687867 + name: firebase_performance + sha256: caedb1b05f1d3e754ae02bacb99e42ac983beb3d8c656f20af47a35652289631 url: "https://pub.dev" source: hosted - version: "1.5.0" - floor_annotation: + version: "0.11.0+1" + firebase_performance_platform_interface: dependency: transitive description: - name: floor_annotation - sha256: a40949580a7ab0eee572686e2d3b1638fd6bd6a753e661d792ab4236b365b23b + name: firebase_performance_platform_interface + sha256: "7e410575add10c68d2bbdbf0ef5afce4dbae5b809d7d4a23845c2421d39085d2" url: "https://pub.dev" source: hosted - version: "1.5.0" - floor_common: + version: "0.1.5+12" + firebase_performance_web: dependency: transitive description: - name: floor_common - sha256: "41c9914862f83a821815e1b1ffd47a1e1ae2130c35ff882ba2d000a67713ba64" + name: firebase_performance_web + sha256: "2f53cc56343c18c01d824a2a1113c55060cde50ac8341cc3fafa9edc13cf8035" url: "https://pub.dev" source: hosted - version: "1.5.0" - floor_generator: - dependency: "direct dev" + version: "0.1.7+18" + fixnum: + dependency: transitive description: - name: floor_generator - sha256: "1499b3ab878a807e6fbe6f140dc014124845cd1df3090a113aae5fa7577a1e77" + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -479,10 +367,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab" + sha256: b0694b7fb1689b0e6cc193b3f1fcac6423c4f93c74fb20b806c6b6f196db0c31 url: "https://pub.dev" source: hosted - version: "2.0.29" + version: "2.0.30" flutter_test: dependency: "direct dev" description: flutter @@ -493,30 +381,6 @@ packages: description: flutter source: sdk version: "0.0.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" html: dependency: transitive description: @@ -533,14 +397,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" - source: hosted - version: "3.2.2" http_parser: dependency: transitive description: @@ -573,30 +429,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.20.2" - io: - dependency: transitive - description: - name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" - source: hosted - version: "1.0.5" - js: - dependency: transitive - description: - name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.dev" - source: hosted - version: "0.7.2" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" leak_tracker: dependency: transitive description: @@ -629,14 +461,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" matcher: dependency: transitive description: @@ -677,14 +501,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" - source: hosted - version: "2.2.0" package_info_plus: dependency: "direct main" description: @@ -702,7 +518,7 @@ packages: source: hosted version: "3.2.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" @@ -721,10 +537,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.18" path_provider_foundation: dependency: transitive description: @@ -829,14 +645,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" posix: dependency: transitive description: @@ -845,30 +653,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.3" - process_run: - dependency: transitive - description: - name: process_run - sha256: "6ec839cdd3e6de4685318e7686cd4abb523c3d3a55af0e8d32a12ae19bc66622" - url: "https://pub.dev" - source: hosted - version: "1.2.4" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" - source: hosted - version: "1.5.0" quickalert: dependency: "direct main" description: @@ -905,10 +689,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e" + sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74 url: "https://pub.dev" source: hosted - version: "2.4.11" + version: "2.4.12" shared_preferences_foundation: dependency: transitive description: @@ -949,43 +733,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" - shelf: - dependency: transitive - description: - name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" - source: hosted - version: "1.4.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 - url: "https://pub.dev" - source: hosted - version: "2.0.1" - simple_sparse_list: - dependency: transitive - description: - name: simple_sparse_list - sha256: aa648fd240fa39b49dcd11c19c266990006006de6699a412de485695910fbc1f - url: "https://pub.dev" - source: hosted - version: "0.1.4" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.0" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://pub.dev" - source: hosted - version: "1.5.0" source_span: dependency: transitive description: @@ -1003,7 +755,7 @@ packages: source: hosted version: "7.0.0" sqflite: - dependency: transitive + dependency: "direct main" description: name: sqflite sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 @@ -1026,22 +778,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.6" - sqflite_common_ffi: - dependency: transitive - description: - name: sqflite_common_ffi - sha256: "9faa2fedc5385ef238ce772589f7718c24cdddd27419b609bb9c6f703ea27988" - url: "https://pub.dev" - source: hosted - version: "2.3.6" - sqflite_common_ffi_web: - dependency: transitive - description: - name: sqflite_common_ffi_web - sha256: "61ea702e7aba727f28be7ead00b84c19c745cd4a4934d0c41473303df11ac9ea" - url: "https://pub.dev" - source: hosted - version: "0.4.5+4" sqflite_darwin: dependency: transitive description: @@ -1058,22 +794,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" - sqlite3: - dependency: transitive - description: - name: sqlite3 - sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924 - url: "https://pub.dev" - source: hosted - version: "2.9.0" - sqlparser: - dependency: transitive - description: - name: sqlparser - sha256: "7b20045d1ccfb7bc1df7e8f9fee5ae58673fce6ff62cefbb0e0fd7214e90e5a0" - url: "https://pub.dev" - source: hosted - version: "0.34.1" stack_trace: dependency: transitive description: @@ -1090,14 +810,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" - source: hosted - version: "2.1.1" string_scanner: dependency: transitive description: @@ -1106,30 +818,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - strings: - dependency: transitive - description: - name: strings - sha256: "482f1511d2cd8ab9f2c4cc148e6837be8a00c03b9a8eaedbc981a84d9c6305d8" - url: "https://pub.dev" - source: hosted - version: "3.2.0" syncfusion_flutter_charts: dependency: "direct main" description: name: syncfusion_flutter_charts - sha256: "68fdb029dad34a46e4c9cfad8ad66fe29db7b303bd96849261ab2b23a168d0e8" + sha256: e78e3bf638bbb49011ec5300556fc076bc1df36aae2262c1e5a5c25cf7baee30 url: "https://pub.dev" source: hosted - version: "30.2.7" + version: "31.1.17" syncfusion_flutter_core: dependency: transitive description: name: syncfusion_flutter_core - sha256: bfd026c0f9822b49ff26fed11cd3334519acb6a6ad4b0c81d9cd18df6af1c4c0 + sha256: "917022225a57ddf1287f9cd840acdcfab3197b92e84964476f6a2f9e87a78ace" url: "https://pub.dev" source: hosted - version: "30.2.7" + version: "31.1.17" synchronized: dependency: transitive description: @@ -1154,14 +858,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.6" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" - url: "https://pub.dev" - source: hosted - version: "1.0.2" typed_data: dependency: transitive description: @@ -1170,14 +866,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" - unicode: - dependency: transitive - description: - name: unicode - sha256: "0d99edbd2e74726bed2e4989713c8bec02e5581628e334d8c88c0271593fb402" - url: "https://pub.dev" - source: hosted - version: "1.1.8" universal_io: dependency: transitive description: @@ -1198,10 +886,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656" + sha256: "69ee86740f2847b9a4ba6cffa74ed12ce500bbe2b07f3dc1e643439da60637b7" url: "https://pub.dev" source: hosted - version: "6.3.17" + version: "6.3.18" url_launcher_ios: dependency: transitive description: @@ -1274,14 +962,6 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" - watcher: - dependency: transitive - description: - name: watcher - sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c" - url: "https://pub.dev" - source: hosted - version: "1.1.3" web: dependency: transitive description: @@ -1290,22 +970,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" - source: hosted - version: "3.0.3" webview_flutter: dependency: "direct main" description: @@ -1318,10 +982,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: "0a42444056b24ed832bdf3442d65c5194f6416f7e782152384944053c2ecc9a3" + sha256: "9a25f6b4313978ba1c2cda03a242eea17848174912cfb4d2d8ee84a556f248e3" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.10.1" webview_flutter_platform_interface: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 99cdd95..0e5608b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.6.4+22 +version: 1.7.0+23 environment: sdk: '>=3.3.0' @@ -30,8 +30,9 @@ environment: # versions available, run `flutter pub outdated`. dependencies: csv: any - floor: any quickalert: any + sqflite: any + path: any share_plus: any file_saver: any file_picker: any @@ -54,13 +55,13 @@ dependencies: # Firebase firebase_core: any + firebase_auth: any + firebase_database: any firebase_analytics: any firebase_crashlytics: any firebase_performance: any dev_dependencies: - build_runner: any - floor_generator: any flutter_test: sdk: flutter