From 846f91d44079d252a95c06cad1c594c20aab6caa Mon Sep 17 00:00:00 2001 From: alexdivadi Date: Thu, 13 Feb 2025 18:18:49 -0800 Subject: [PATCH 1/7] feat: add emotions repo --- lib/src/app.dart | 1 + .../data/repositories/emotion_repository.dart | 90 ++++++++++++++ .../repositories/emotion_repository.g.dart | 29 +++++ .../emotion_repository_local.dart | 89 ++++++++++++++ .../emotion_repository_local.g.dart | 30 +++++ .../emotions/domain/entities/emotion_log.dart | 30 +++++ .../emotions/domain/models/category.dart | 31 +++++ .../emotions/domain/models/emotion.dart | 110 ++++++++++++++++++ .../domain/models/emotion_log_model.dart | 82 +++++++++++++ .../data/onboarding_repository.g.dart | 2 +- lib/src/utils/capitalize.dart | 8 ++ .../utils/shared_preferences_provider.dart | 4 +- .../utils/shared_preferences_provider.g.dart | 7 +- pubspec.lock | 2 +- pubspec.yaml | 21 ++-- 15 files changed, 518 insertions(+), 18 deletions(-) create mode 100644 lib/src/features/emotions/data/repositories/emotion_repository.dart create mode 100644 lib/src/features/emotions/data/repositories/emotion_repository.g.dart create mode 100644 lib/src/features/emotions/data/repositories/emotion_repository_local.dart create mode 100644 lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart create mode 100644 lib/src/features/emotions/domain/entities/emotion_log.dart create mode 100644 lib/src/features/emotions/domain/models/category.dart create mode 100644 lib/src/features/emotions/domain/models/emotion.dart create mode 100644 lib/src/features/emotions/domain/models/emotion_log_model.dart create mode 100644 lib/src/utils/capitalize.dart diff --git a/lib/src/app.dart b/lib/src/app.dart index 1a8a848..6600af2 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -32,6 +32,7 @@ class MyApp extends ConsumerWidget { // * Example ID from this app: https://fluttertips.dev/ // * To avoid mistakes, store the ID as an environment variable and // * read it with String.fromEnvironment + // TODO: update isoAppStoreId iosAppStoreId: '6482293361', ), allowCancel: false, diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.dart b/lib/src/features/emotions/data/repositories/emotion_repository.dart new file mode 100644 index 0000000..e4692e2 --- /dev/null +++ b/lib/src/features/emotions/data/repositories/emotion_repository.dart @@ -0,0 +1,90 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:illemo/src/features/authentication/data/firebase_auth_repository.dart'; +import 'package:illemo/src/features/authentication/domain/app_user.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'emotion_repository.g.dart'; + +/// Firestore implementation of [EmotionRepository]. +/// Depends on [firebaseAuthProvider] for user id. +@Riverpod(keepAlive: true) +class EmotionRepository extends _$EmotionRepository { + EmotionRepository(); + + static String emotionsPath(String uid) => 'users/$uid/emotions'; + + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + late final UserID _userID; + + @override + Future build() async { + final user = ref.watch(firebaseAuthProvider).currentUser; + if (user == null) { + throw AssertionError('User can\'t be null'); + } + _userID = user.uid; + return getEmotionLogToday(); + } + + Future addEmotionLog(EmotionLog emotionLog) async { + await _firestore + .collection(emotionsPath(_userID)) + .add(EmotionLogModel.fromEntity(emotionLog).toMap()); + } + + Future updateEmotionLog(String id, EmotionLog emotionLog) async { + await _firestore + .collection(emotionsPath(_userID)) + .doc(id) + .update(EmotionLogModel.fromEntity(emotionLog, id: id).toMap()); + } + + Future deleteEmotionLog(String id) async { + await _firestore.collection(emotionsPath(_userID)).doc(id).delete(); + } + + Future getEmotionLog(String id) async { + DocumentSnapshot doc = await _firestore.collection(emotionsPath(_userID)).doc(id).get(); + if (doc.exists) { + return EmotionLogModel.fromMap(doc.data() as Map).toEntity(); + } + return null; + } + + Future getEmotionLogToday() async { + QuerySnapshot querySnapshot = await _firestore + .collection(emotionsPath(_userID)) + .where('date', isEqualTo: DateTime.now().toIso8601String().split('T').first) + .limit(1) + .get(); + if (querySnapshot.docs.isNotEmpty) { + return EmotionLogModel.fromMap(querySnapshot.docs.first.data() as Map) + .toEntity(); + } + return null; + } + + Stream> getEmotionLogs({ + DateTime? startDate, + DateTime? endDate, + }) { + Query query = _firestore.collection(emotionsPath(_userID)); + + if (startDate != null) { + query = + query.where('date', isGreaterThanOrEqualTo: startDate.toIso8601String().split('T').first); + } + + if (endDate != null) { + query = query.where('date', isLessThanOrEqualTo: endDate.toIso8601String().split('T').first); + } + + return query.snapshots().map((snapshot) { + return snapshot.docs + .map((doc) => EmotionLogModel.fromMap(doc.data() as Map).toEntity()) + .toList(); + }); + } +} diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.g.dart b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart new file mode 100644 index 0000000..aac666e --- /dev/null +++ b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'emotion_repository.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$emotionRepositoryHash() => r'13ae68e9d0d5907bd2a4b578cb4ff5990be29f78'; + +/// Firestore implementation of [EmotionRepository]. +/// Depends on [firebaseAuthProvider] for user id. +/// +/// Copied from [EmotionRepository]. +@ProviderFor(EmotionRepository) +final emotionRepositoryProvider = + AsyncNotifierProvider.internal( + EmotionRepository.new, + name: r'emotionRepositoryProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$emotionRepositoryHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$EmotionRepository = AsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/emotions/data/repositories/emotion_repository_local.dart b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart new file mode 100644 index 0000000..f9d08cb --- /dev/null +++ b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:illemo/src/features/emotions/data/repositories/emotion_repository.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; +import 'package:illemo/src/utils/shared_preferences_provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +part 'emotion_repository_local.g.dart'; + +/// Local implementation of [EmotionRepository]. +/// Depends on [sharedPreferencesProvider] for storing data. +@Riverpod(keepAlive: true) +class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements EmotionRepository { + EmotionRepositoryLocal(); + + final String _collectionPath = 'emotions'; + late final SharedPreferencesWithCache prefs; + + @override + Future build() async { + prefs = ref.watch(sharedPreferencesProvider).requireValue; + return getEmotionLogToday(); + } + + @override + Future addEmotionLog(EmotionLog emotionLog) async { + final key = '${_collectionPath}_${emotionLog.date.weekday}'; + await prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog).toMap())); + } + + @override + Future updateEmotionLog(String id, EmotionLog emotionLog) async { + /// id is the weekday (only stores up to 7 entries) + final key = '${_collectionPath}_${emotionLog.date.weekday}'; + await prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog, id: id).toMap())); + } + + @override + Future deleteEmotionLog(String id) async { + final key = '${_collectionPath}_$id'; + await prefs.remove(key); + } + + @override + Future getEmotionLog(String id) async { + final key = '${_collectionPath}_$id'; + final jsonString = prefs.getString(key); + if (jsonString != null) { + return EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); + } + return null; + } + + @override + Future getEmotionLogToday() async { + final key = '${_collectionPath}_${DateTime.now().weekday}'; + final jsonString = prefs.getString(key); + if (jsonString != null) { + final EmotionLog emotionLog = EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); + if (DateUtils.isSameDay(emotionLog.date, DateTime.now())) { + return emotionLog; + } + } + return null; + } + + @override + Stream> getEmotionLogs({ + DateTime? startDate, + DateTime? endDate, + }) async* { + final keys = prefs.keys.where((key) => key.startsWith(_collectionPath)); + final logs = []; + for (final key in keys) { + final jsonString = prefs.getString(key); + if (jsonString != null) { + final emotionLog = EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); + if ((startDate == null || emotionLog.date.isAfter(startDate)) && + (endDate == null || emotionLog.date.isBefore(endDate))) { + logs.add(emotionLog); + } + } + } + yield logs; + } +} diff --git a/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart b/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart new file mode 100644 index 0000000..a895145 --- /dev/null +++ b/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'emotion_repository_local.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$emotionRepositoryLocalHash() => + r'685a805fb855d044572d0d97ad9dfa374c88c82b'; + +/// Local implementation of [EmotionRepository]. +/// Depends on [sharedPreferencesProvider] for storing data. +/// +/// Copied from [EmotionRepositoryLocal]. +@ProviderFor(EmotionRepositoryLocal) +final emotionRepositoryLocalProvider = + AsyncNotifierProvider.internal( + EmotionRepositoryLocal.new, + name: r'emotionRepositoryLocalProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$emotionRepositoryLocalHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$EmotionRepositoryLocal = AsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/emotions/domain/entities/emotion_log.dart b/lib/src/features/emotions/domain/entities/emotion_log.dart new file mode 100644 index 0000000..ab50192 --- /dev/null +++ b/lib/src/features/emotions/domain/entities/emotion_log.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; + +class EmotionLog extends Equatable { + const EmotionLog({ + required this.emotion1, + required this.date, + this.emotion2, + this.emotion3, + this.id, + }); + + /// Only first emotion is required. Stored in db as id (int). + final Emotion emotion1; + final Emotion? emotion2; + final Emotion? emotion3; + + /// Used for date only, not time. + final DateTime date; + + /// Optional parameter for tracking doc id in firestore. + final EmotionLogID? id; + + @override + List get props => [emotion1, emotion2, emotion3, date]; + + @override + bool get stringify => true; +} diff --git a/lib/src/features/emotions/domain/models/category.dart b/lib/src/features/emotions/domain/models/category.dart new file mode 100644 index 0000000..409f0fb --- /dev/null +++ b/lib/src/features/emotions/domain/models/category.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +enum Category implements Comparable { + sad(1, baseColor: Colors.blue), + mad(2, baseColor: Colors.deepOrange), + scared(3, baseColor: Colors.purple), + joyful(4, baseColor: Colors.pink), + peaceful(5, baseColor: Colors.green), + powerful(6, baseColor: Colors.amber), + ; + + const Category(this.id, {required this.baseColor}); + + final int id; + final MaterialColor baseColor; + + /// Run a calculation to get the shaded color associated with the emotion's category. + /// The tier determines how dark the color is, with tier 1 being the darkest. + Color getColor(int tier) => baseColor[900 - tier * 200]!; + + @override + int compareTo(Category other) => id - other.id; + + static final Map _categoryById = { + for (var category in Category.values) category.id: category + }; + + static Category get(int id) { + return _categoryById[id] ?? (throw ArgumentError('Emotion with id $id not found')); + } +} diff --git a/lib/src/features/emotions/domain/models/emotion.dart b/lib/src/features/emotions/domain/models/emotion.dart new file mode 100644 index 0000000..98479e2 --- /dev/null +++ b/lib/src/features/emotions/domain/models/emotion.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:illemo/src/features/emotions/domain/models/category.dart'; +import 'package:illemo/src/utils/capitalize.dart'; + +enum Emotion { + sad(1, Category.sad, tier: 1), + guilty(2, Category.sad, tier: 2), + ashamed(3, Category.sad, tier: 2), + lonely(4, Category.sad, tier: 2), + depressed(5, Category.sad, tier: 2), + bored(6, Category.sad, tier: 2), + tired(7, Category.sad, tier: 2), + remorseful(8, Category.sad, tier: 3), + stupid(9, Category.sad, tier: 3), + inferior(10, Category.sad, tier: 3), + isolated(11, Category.sad, tier: 3), + apathetic(12, Category.sad, tier: 3), + sleepy(13, Category.sad, tier: 3), + peaceful(14, Category.peaceful, tier: 1), + content(15, Category.peaceful, tier: 2), + thoughtful(16, Category.peaceful, tier: 2), + intimate(17, Category.peaceful, tier: 2), + loving(18, Category.peaceful, tier: 2), + trusting(19, Category.peaceful, tier: 2), + nurturing(20, Category.peaceful, tier: 2), + relaxed(21, Category.peaceful, tier: 3), + pensive(22, Category.peaceful, tier: 3), + responsive(23, Category.peaceful, tier: 3), + serene(24, Category.peaceful, tier: 3), + secure(25, Category.peaceful, tier: 3), + thankful(26, Category.peaceful, tier: 3), + powerful(27, Category.powerful, tier: 1), + faithful(28, Category.powerful, tier: 2), + important(29, Category.powerful, tier: 2), + appreciated(30, Category.powerful, tier: 2), + respected(31, Category.powerful, tier: 2), + proud(32, Category.powerful, tier: 2), + aware(33, Category.powerful, tier: 2), + confident(34, Category.powerful, tier: 3), + discerning(35, Category.powerful, tier: 3), + valuable(36, Category.powerful, tier: 3), + worthwhile(37, Category.powerful, tier: 3), + successful(38, Category.powerful, tier: 3), + surprised(39, Category.powerful, tier: 3), + joyful(40, Category.joyful, tier: 1), + hopeful(41, Category.joyful, tier: 2), + creative(42, Category.joyful, tier: 2), + cheerful(43, Category.joyful, tier: 2), + energetic(44, Category.joyful, tier: 2), + sensuous(45, Category.joyful, tier: 2), + excited(46, Category.joyful, tier: 2), + optimistic(47, Category.joyful, tier: 3), + playful(48, Category.joyful, tier: 3), + amused(49, Category.joyful, tier: 3), + stimulating(50, Category.joyful, tier: 3), + fascinating(51, Category.joyful, tier: 3), + daring(52, Category.joyful, tier: 3), + scared(53, Category.scared, tier: 1), + anxious(54, Category.scared, tier: 2), + insecure(55, Category.scared, tier: 2), + submissive(56, Category.scared, tier: 2), + helpless(57, Category.scared, tier: 2), + rejected(58, Category.scared, tier: 2), + confused(59, Category.scared, tier: 2), + overwhelmed(60, Category.scared, tier: 3), + embarrassed(61, Category.scared, tier: 3), + inadequate(62, Category.scared, tier: 3), + insignificant(63, Category.scared, tier: 3), + discouraged(64, Category.scared, tier: 3), + bewildered(65, Category.scared, tier: 3), + mad(66, Category.mad, tier: 1), + critical(67, Category.mad, tier: 2), + hateful(68, Category.mad, tier: 2), + selfish(69, Category.mad, tier: 2), + angry(70, Category.mad, tier: 2), + hostile(71, Category.mad, tier: 2), + hurt(72, Category.mad, tier: 2), + skeptical(73, Category.mad, tier: 3), + irritated(74, Category.mad, tier: 3), + jealous(75, Category.mad, tier: 3), + frustrated(76, Category.mad, tier: 3), + sarcastic(77, Category.mad, tier: 3), + distant(78, Category.mad, tier: 3), + ; + + const Emotion( + this.id, + this.category, { + this.tier = 1, + }); + + final int id; + final Category category; + final int tier; + + Color get color => category.getColor(tier); + + static final Map _emotionById = { + for (var emotion in Emotion.values) emotion.id: emotion + }; + + static Emotion get(int id) { + return _emotionById[id] ?? (throw ArgumentError('Emotion with id $id not found')); + } + + @override + String toString() { + return name.capitalize(); + } +} diff --git a/lib/src/features/emotions/domain/models/emotion_log_model.dart b/lib/src/features/emotions/domain/models/emotion_log_model.dart new file mode 100644 index 0000000..9e23357 --- /dev/null +++ b/lib/src/features/emotions/domain/models/emotion_log_model.dart @@ -0,0 +1,82 @@ +import 'package:flutter/foundation.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion.dart'; +import 'package:uuid/uuid.dart'; + +typedef EmotionLogID = String; + +@immutable +class EmotionLogModel { + const EmotionLogModel({ + required this.id, + required this.emotion1, + required this.date, + required this.timestamp, + this.emotion2, + this.emotion3, + }); + + final EmotionLogID id; + + /// Only first emotion is required. Stored in db as id (int). + final int emotion1; + final int? emotion2; + final int? emotion3; + + /// Used for date only, not time. + /// Format is yyyy-MM-dd + final String date; + + /// Used to track when the log was updated. Stored in db as milliseconds since epoch (int). + final int timestamp; + + factory EmotionLogModel.fromEntity(EmotionLog entity, {EmotionLogID? id, int? timestamp}) { + return EmotionLogModel( + id: id ?? Uuid().v4(), + emotion1: entity.emotion1.id, + emotion2: entity.emotion2?.id, + emotion3: entity.emotion3?.id, + date: entity.date.toIso8601String().split('T').first, + timestamp: timestamp ?? DateTime.now().millisecondsSinceEpoch, + ); + } + + EmotionLog toEntity() { + return EmotionLog( + id: id, + emotion1: Emotion.get(emotion1), + emotion2: emotion2 != null ? Emotion.get(emotion2!) : null, + emotion3: emotion3 != null ? Emotion.get(emotion3!) : null, + date: DateTime.parse(date), + ); + } + + factory EmotionLogModel.fromMap(Map data) { + final id = data['id'] as EmotionLogID; + final emotion1 = data['emotion1'] as int; + final emotion2 = data['emotion2'] as int?; + final emotion3 = data['emotion3'] as int?; + final date = data['date'] as String; + final timestamp = data['timestamp'] as int; + + return EmotionLogModel( + id: id, + emotion1: emotion1, + emotion2: emotion2, + emotion3: emotion3, + date: date, + timestamp: timestamp, + ); + } + + Map toMap() { + return { + 'id': id, + 'emotion1': emotion1, + 'emotion2': emotion2, + 'emotion3': emotion3, + 'date': date, + 'timestamp': timestamp, + }; + } +} diff --git a/lib/src/features/onboarding/data/onboarding_repository.g.dart b/lib/src/features/onboarding/data/onboarding_repository.g.dart index 2dca7ca..ef74384 100644 --- a/lib/src/features/onboarding/data/onboarding_repository.g.dart +++ b/lib/src/features/onboarding/data/onboarding_repository.g.dart @@ -7,7 +7,7 @@ part of 'onboarding_repository.dart'; // ************************************************************************** String _$onboardingRepositoryHash() => - r'b3d2bcb49877fe1de659afaf4683aca9fccf5b3e'; + r'445c529dd1ac7515d8be0abd6159af6958ff3c5c'; /// See also [onboardingRepository]. @ProviderFor(onboardingRepository) diff --git a/lib/src/utils/capitalize.dart b/lib/src/utils/capitalize.dart new file mode 100644 index 0000000..b471769 --- /dev/null +++ b/lib/src/utils/capitalize.dart @@ -0,0 +1,8 @@ +extension CapitalizableString on String { + String capitalize() => + length > 0 ? "${this[0].toUpperCase()}${substring(1).toLowerCase()}" : toUpperCase(); + String toTitleCase() => replaceAll(RegExp(' +'), ' ') + .split(RegExp(r'(?<=\s)|(?<=-)')) + .map((str) => str.capitalize()) + .join(''); +} diff --git a/lib/src/utils/shared_preferences_provider.dart b/lib/src/utils/shared_preferences_provider.dart index 3001c47..8a75f4b 100644 --- a/lib/src/utils/shared_preferences_provider.dart +++ b/lib/src/utils/shared_preferences_provider.dart @@ -5,6 +5,6 @@ import 'package:shared_preferences/shared_preferences.dart'; part 'shared_preferences_provider.g.dart'; @Riverpod(keepAlive: true) -Future sharedPreferences(Ref ref) { - return SharedPreferences.getInstance(); +Future sharedPreferences(Ref ref) { + return SharedPreferencesWithCache.create(cacheOptions: SharedPreferencesWithCacheOptions()); } diff --git a/lib/src/utils/shared_preferences_provider.g.dart b/lib/src/utils/shared_preferences_provider.g.dart index d24abe4..d714c59 100644 --- a/lib/src/utils/shared_preferences_provider.g.dart +++ b/lib/src/utils/shared_preferences_provider.g.dart @@ -6,11 +6,12 @@ part of 'shared_preferences_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$sharedPreferencesHash() => r'48e60558ea6530114ea20ea03e69b9fb339ab129'; +String _$sharedPreferencesHash() => r'8ad0f57d954e8c413084bfe5492c81b218c3e4a0'; /// See also [sharedPreferences]. @ProviderFor(sharedPreferences) -final sharedPreferencesProvider = FutureProvider.internal( +final sharedPreferencesProvider = + FutureProvider.internal( sharedPreferences, name: r'sharedPreferencesProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,6 +23,6 @@ final sharedPreferencesProvider = FutureProvider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef SharedPreferencesRef = FutureProviderRef; +typedef SharedPreferencesRef = FutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/pubspec.lock b/pubspec.lock index 753ae43..575a7e7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1080,7 +1080,7 @@ packages: source: hosted version: "3.1.3" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff diff --git a/pubspec.yaml b/pubspec.yaml index f88a77e..3c65507 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,37 +13,36 @@ dependencies: equatable: ^2.0.7 firebase_auth: ^5.3.4 firebase_core: ^3.8.1 - firebase_ui_auth: ^1.16.0 + firebase_ui_auth: ^1.16.0 firebase_ui_firestore: ^1.7.0 flutter: sdk: flutter - riverpod: ^2.6.1 flutter_riverpod: ^2.6.1 flutter_svg: ^2.0.16 + force_update_helper: ^0.1.5 go_router: ^14.6.2 intl: ^0.19.0 + riverpod: ^2.6.1 + riverpod_annotation: ^2.6.1 rxdart: ^0.28.0 shared_preferences: ^2.3.3 - # the annotation package containing @riverpod - riverpod_annotation: ^2.6.1 - force_update_helper: ^0.1.5 url_launcher: ^6.3.1 + uuid: ^4.5.1 dev_dependencies: + # a tool for running code generators + build_runner: ^2.4.13 + # import custom_lint too as riverpod_lint depends on it + custom_lint: ^0.7.0 + flutter_lints: ^5.0.0 flutter_test: sdk: flutter mocktail: ^1.0.4 random_string: ^2.3.1 - flutter_lints: ^5.0.0 - # a tool for running code generators - build_runner: ^2.4.13 # the code generator riverpod_generator: ^2.6.3 # riverpod_lint makes it easier to work with Riverpod riverpod_lint: ^2.6.3 - # import custom_lint too as riverpod_lint depends on it - custom_lint: ^0.7.0 - flutter: uses-material-design: true From 8d9cc30b66eba322f44317b254900eea98832be3 Mon Sep 17 00:00:00 2001 From: alexdivadi Date: Thu, 13 Feb 2025 18:31:10 -0800 Subject: [PATCH 2/7] chore: run format, add docstrings --- analysis_options.yaml | 3 + lib/src/constants/strings.dart | 3 +- .../data/firebase_auth_repository.g.dart | 10 +- .../presentation/auth_providers.dart | 6 +- .../presentation/auth_providers.g.dart | 11 +- .../data/repositories/emotion_repository.dart | 21 ++ .../repositories/emotion_repository.g.dart | 8 +- .../emotion_repository_local.dart | 44 ++- .../emotion_repository_local.g.dart | 8 +- .../emotions/domain/models/category.dart | 9 + .../emotions/domain/models/emotion.dart | 3 + .../domain/models/emotion_log_model.dart | 6 +- .../application/entries_service.g.dart | 16 +- .../entries/data/entries_repository.g.dart | 22 +- .../entry_screen_controller.g.dart | 8 +- .../features/jobs/data/jobs_repository.g.dart | 18 +- .../edit_job_screen_controller.g.dart | 8 +- .../job_entries_list_controller.g.dart | 8 +- .../jobs_screen/jobs_screen_controller.g.dart | 8 +- .../data/onboarding_repository.g.dart | 11 +- .../presentation/onboarding_controller.g.dart | 8 +- lib/src/routing/app_router.g.dart | 3 +- lib/src/routing/app_startup.g.dart | 3 +- .../utils/shared_preferences_provider.g.dart | 8 +- pubspec.lock | 337 +++++++++--------- 25 files changed, 297 insertions(+), 293 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index f32c59c..98d787c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -13,6 +13,9 @@ analyzer: plugins: - custom_lint +formatter: + page_width: 100 + linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` diff --git a/lib/src/constants/strings.dart b/lib/src/constants/strings.dart index ef09224..aef64e0 100644 --- a/lib/src/constants/strings.dart +++ b/lib/src/constants/strings.dart @@ -5,8 +5,7 @@ class Strings { // Logout static const String logout = 'Logout'; - static const String logoutAreYouSure = - 'Are you sure that you want to logout?'; + static const String logoutAreYouSure = 'Are you sure that you want to logout?'; static const String logoutFailed = 'Logout failed'; // Sign In Page diff --git a/lib/src/features/authentication/data/firebase_auth_repository.g.dart b/lib/src/features/authentication/data/firebase_auth_repository.g.dart index 6905fb3..b64de85 100644 --- a/lib/src/features/authentication/data/firebase_auth_repository.g.dart +++ b/lib/src/features/authentication/data/firebase_auth_repository.g.dart @@ -29,9 +29,8 @@ String _$authRepositoryHash() => r'0e32dee9e183c43ec14a6b58d74d26deb3950cbc'; final authRepositoryProvider = Provider.internal( authRepository, name: r'authRepositoryProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$authRepositoryHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$authRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -46,9 +45,8 @@ String _$authStateChangesHash() => r'7bdb56f405df8ffc5554e0128ec15d474f011ec9'; final authStateChangesProvider = AutoDisposeStreamProvider.internal( authStateChanges, name: r'authStateChangesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$authStateChangesHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$authStateChangesHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/authentication/presentation/auth_providers.dart b/lib/src/features/authentication/presentation/auth_providers.dart index 08a3268..2a9893e 100644 --- a/lib/src/features/authentication/presentation/auth_providers.dart +++ b/lib/src/features/authentication/presentation/auth_providers.dart @@ -1,5 +1,4 @@ -import 'package:firebase_auth/firebase_auth.dart' - hide EmailAuthProvider, AuthProvider; +import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider, AuthProvider; import 'package:firebase_ui_auth/firebase_ui_auth.dart'; import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -7,8 +6,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'auth_providers.g.dart'; @Riverpod(keepAlive: true) -List> authProviders( - Ref ref) { +List> authProviders(Ref ref) { return [ EmailAuthProvider(), ]; diff --git a/lib/src/features/authentication/presentation/auth_providers.g.dart b/lib/src/features/authentication/presentation/auth_providers.g.dart index 2b16f38..332905d 100644 --- a/lib/src/features/authentication/presentation/auth_providers.g.dart +++ b/lib/src/features/authentication/presentation/auth_providers.g.dart @@ -10,20 +10,17 @@ String _$authProvidersHash() => r'8a83535c31539dac72f21c3f27b7d7fb77161e5f'; /// See also [authProviders]. @ProviderFor(authProviders) -final authProvidersProvider = - Provider>>.internal( +final authProvidersProvider = Provider>>.internal( authProviders, name: r'authProvidersProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$authProvidersHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$authProvidersHash, dependencies: null, allTransitiveDependencies: null, ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef AuthProvidersRef - = ProviderRef>>; +typedef AuthProvidersRef = ProviderRef>>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.dart b/lib/src/features/emotions/data/repositories/emotion_repository.dart index e4692e2..11bb6ea 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository.dart @@ -13,11 +13,15 @@ part 'emotion_repository.g.dart'; class EmotionRepository extends _$EmotionRepository { EmotionRepository(); + /// Returns the Firestore path for the emotions collection of a specific user. static String emotionsPath(String uid) => 'users/$uid/emotions'; final FirebaseFirestore _firestore = FirebaseFirestore.instance; late final UserID _userID; + /// Initializes the repository by getting the current user and fetching today's emotion log (which may be). + /// + /// Throws an [AssertionError] if the user is null. @override Future build() async { final user = ref.watch(firebaseAuthProvider).currentUser; @@ -28,12 +32,18 @@ class EmotionRepository extends _$EmotionRepository { return getEmotionLogToday(); } + /// Adds a new emotion log to Firestore. + /// + /// Converts the [EmotionLog] entity to a map using [EmotionLogModel] before adding it. Future addEmotionLog(EmotionLog emotionLog) async { await _firestore .collection(emotionsPath(_userID)) .add(EmotionLogModel.fromEntity(emotionLog).toMap()); } + /// Updates an existing emotion log in Firestore. + /// + /// Converts the [EmotionLog] entity to a map using [EmotionLogModel] before updating it. Future updateEmotionLog(String id, EmotionLog emotionLog) async { await _firestore .collection(emotionsPath(_userID)) @@ -41,10 +51,14 @@ class EmotionRepository extends _$EmotionRepository { .update(EmotionLogModel.fromEntity(emotionLog, id: id).toMap()); } + /// Deletes an emotion log from Firestore by its document ID. Future deleteEmotionLog(String id) async { await _firestore.collection(emotionsPath(_userID)).doc(id).delete(); } + /// Retrieves an emotion log from Firestore by its document ID. + /// + /// Returns the [EmotionLog] entity if found, otherwise returns null. Future getEmotionLog(String id) async { DocumentSnapshot doc = await _firestore.collection(emotionsPath(_userID)).doc(id).get(); if (doc.exists) { @@ -53,6 +67,9 @@ class EmotionRepository extends _$EmotionRepository { return null; } + /// Retrieves today's emotion log from Firestore. + /// + /// Returns the [EmotionLog] entity if found, otherwise returns null. Future getEmotionLogToday() async { QuerySnapshot querySnapshot = await _firestore .collection(emotionsPath(_userID)) @@ -66,6 +83,10 @@ class EmotionRepository extends _$EmotionRepository { return null; } + /// Streams a list of emotion logs from Firestore within an optional date range. + /// + /// If [startDate] is provided, only logs from that date onwards are included. + /// If [endDate] is provided, only logs up to that date are included. Stream> getEmotionLogs({ DateTime? startDate, DateTime? endDate, diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.g.dart b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart index aac666e..e3849d4 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository.g.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart @@ -13,13 +13,11 @@ String _$emotionRepositoryHash() => r'13ae68e9d0d5907bd2a4b578cb4ff5990be29f78'; /// /// Copied from [EmotionRepository]. @ProviderFor(EmotionRepository) -final emotionRepositoryProvider = - AsyncNotifierProvider.internal( +final emotionRepositoryProvider = AsyncNotifierProvider.internal( EmotionRepository.new, name: r'emotionRepositoryProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$emotionRepositoryHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$emotionRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/emotions/data/repositories/emotion_repository_local.dart b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart index f9d08cb..a60188a 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository_local.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart @@ -16,48 +16,69 @@ part 'emotion_repository_local.g.dart'; class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements EmotionRepository { EmotionRepositoryLocal(); + /// The path used for storing emotion logs in shared preferences. final String _collectionPath = 'emotions'; - late final SharedPreferencesWithCache prefs; + /// The shared preferences instance with cache. + late final SharedPreferencesWithCache _prefs; + + /// Initializes the repository and retrieves today's emotion log if available. @override Future build() async { - prefs = ref.watch(sharedPreferencesProvider).requireValue; + _prefs = ref.watch(sharedPreferencesProvider).requireValue; return getEmotionLogToday(); } + /// Adds a new emotion log to the local storage. + /// + /// [emotionLog] - The emotion log to be added. @override Future addEmotionLog(EmotionLog emotionLog) async { final key = '${_collectionPath}_${emotionLog.date.weekday}'; - await prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog).toMap())); + await _prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog).toMap())); } + /// Updates an existing emotion log in the local storage. + /// + /// [id] - The identifier of the emotion log to be updated. + /// [emotionLog] - The updated emotion log. @override Future updateEmotionLog(String id, EmotionLog emotionLog) async { /// id is the weekday (only stores up to 7 entries) final key = '${_collectionPath}_${emotionLog.date.weekday}'; - await prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog, id: id).toMap())); + await _prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog, id: id).toMap())); } + /// Deletes an emotion log from the local storage. + /// + /// [id] - The identifier of the emotion log to be deleted. @override Future deleteEmotionLog(String id) async { final key = '${_collectionPath}_$id'; - await prefs.remove(key); + await _prefs.remove(key); } + /// Retrieves an emotion log by its identifier from the local storage. + /// + /// [id] - The identifier of the emotion log to be retrieved. + /// Returns the emotion log if found, otherwise returns null. @override Future getEmotionLog(String id) async { final key = '${_collectionPath}_$id'; - final jsonString = prefs.getString(key); + final jsonString = _prefs.getString(key); if (jsonString != null) { return EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); } return null; } + /// Retrieves today's emotion log from the local storage. + /// + /// Returns the emotion log if found and the date matches today, otherwise returns null. @override Future getEmotionLogToday() async { final key = '${_collectionPath}_${DateTime.now().weekday}'; - final jsonString = prefs.getString(key); + final jsonString = _prefs.getString(key); if (jsonString != null) { final EmotionLog emotionLog = EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); if (DateUtils.isSameDay(emotionLog.date, DateTime.now())) { @@ -67,15 +88,20 @@ class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements Emotion return null; } + /// Retrieves a stream of emotion logs within the specified date range from the local storage. + /// + /// [startDate] - The start date of the range (inclusive). + /// [endDate] - The end date of the range (inclusive). + /// Returns a stream of emotion logs that fall within the specified date range. @override Stream> getEmotionLogs({ DateTime? startDate, DateTime? endDate, }) async* { - final keys = prefs.keys.where((key) => key.startsWith(_collectionPath)); + final keys = _prefs.keys.where((key) => key.startsWith(_collectionPath)); final logs = []; for (final key in keys) { - final jsonString = prefs.getString(key); + final jsonString = _prefs.getString(key); if (jsonString != null) { final emotionLog = EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); if ((startDate == null || emotionLog.date.isAfter(startDate)) && diff --git a/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart b/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart index a895145..11c4760 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart @@ -6,8 +6,7 @@ part of 'emotion_repository_local.dart'; // RiverpodGenerator // ************************************************************************** -String _$emotionRepositoryLocalHash() => - r'685a805fb855d044572d0d97ad9dfa374c88c82b'; +String _$emotionRepositoryLocalHash() => r'685a805fb855d044572d0d97ad9dfa374c88c82b'; /// Local implementation of [EmotionRepository]. /// Depends on [sharedPreferencesProvider] for storing data. @@ -18,9 +17,8 @@ final emotionRepositoryLocalProvider = AsyncNotifierProvider.internal( EmotionRepositoryLocal.new, name: r'emotionRepositoryLocalProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$emotionRepositoryLocalHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$emotionRepositoryLocalHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/emotions/domain/models/category.dart b/lib/src/features/emotions/domain/models/category.dart index 409f0fb..37d67a9 100644 --- a/lib/src/features/emotions/domain/models/category.dart +++ b/lib/src/features/emotions/domain/models/category.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:illemo/src/utils/capitalize.dart'; enum Category implements Comparable { sad(1, baseColor: Colors.blue), @@ -25,7 +26,15 @@ enum Category implements Comparable { for (var category in Category.values) category.id: category }; + /// Returns the [Category] associated with the given [id]. + /// + /// Throws an [ArgumentError] if no category with the given [id] is found. static Category get(int id) { return _categoryById[id] ?? (throw ArgumentError('Emotion with id $id not found')); } + + @override + String toString() { + return name.capitalize(); + } } diff --git a/lib/src/features/emotions/domain/models/emotion.dart b/lib/src/features/emotions/domain/models/emotion.dart index 98479e2..bf07dea 100644 --- a/lib/src/features/emotions/domain/models/emotion.dart +++ b/lib/src/features/emotions/domain/models/emotion.dart @@ -99,6 +99,9 @@ enum Emotion { for (var emotion in Emotion.values) emotion.id: emotion }; + /// Returns the [Emotion] associated with the given [id]. + /// + /// Throws an [ArgumentError] if no emotion with the given [id] is found. static Emotion get(int id) { return _emotionById[id] ?? (throw ArgumentError('Emotion with id $id not found')); } diff --git a/lib/src/features/emotions/domain/models/emotion_log_model.dart b/lib/src/features/emotions/domain/models/emotion_log_model.dart index 9e23357..f04657b 100644 --- a/lib/src/features/emotions/domain/models/emotion_log_model.dart +++ b/lib/src/features/emotions/domain/models/emotion_log_model.dart @@ -18,16 +18,16 @@ class EmotionLogModel { final EmotionLogID id; - /// Only first emotion is required. Stored in db as id (int). + /// Only first emotion is required. Stored in db as id (an [int]). final int emotion1; final int? emotion2; final int? emotion3; /// Used for date only, not time. - /// Format is yyyy-MM-dd + /// Format is `yyyy-MM-dd`. final String date; - /// Used to track when the log was updated. Stored in db as milliseconds since epoch (int). + /// Used to track when the log was updated. Stored in db as milliseconds since epoch (an [int]). final int timestamp; factory EmotionLogModel.fromEntity(EmotionLog entity, {EmotionLogID? id, int? timestamp}) { diff --git a/lib/src/features/entries/application/entries_service.g.dart b/lib/src/features/entries/application/entries_service.g.dart index 0c0f69d..378fdd6 100644 --- a/lib/src/features/entries/application/entries_service.g.dart +++ b/lib/src/features/entries/application/entries_service.g.dart @@ -13,9 +13,8 @@ String _$entriesServiceHash() => r'106c29e519ac1706956f952263745337399caba9'; final entriesServiceProvider = AutoDisposeProvider.internal( entriesService, name: r'entriesServiceProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$entriesServiceHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$entriesServiceHash, dependencies: null, allTransitiveDependencies: null, ); @@ -23,8 +22,7 @@ final entriesServiceProvider = AutoDisposeProvider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element typedef EntriesServiceRef = AutoDisposeProviderRef; -String _$entriesTileModelStreamHash() => - r'e8f3184f1b1db43eb92198669492a36d3ee03356'; +String _$entriesTileModelStreamHash() => r'e8f3184f1b1db43eb92198669492a36d3ee03356'; /// See also [entriesTileModelStream]. @ProviderFor(entriesTileModelStream) @@ -32,16 +30,14 @@ final entriesTileModelStreamProvider = AutoDisposeStreamProvider>.internal( entriesTileModelStream, name: r'entriesTileModelStreamProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$entriesTileModelStreamHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$entriesTileModelStreamHash, dependencies: null, allTransitiveDependencies: null, ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef EntriesTileModelStreamRef - = AutoDisposeStreamProviderRef>; +typedef EntriesTileModelStreamRef = AutoDisposeStreamProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/entries/data/entries_repository.g.dart b/lib/src/features/entries/data/entries_repository.g.dart index 06e92a4..4a76565 100644 --- a/lib/src/features/entries/data/entries_repository.g.dart +++ b/lib/src/features/entries/data/entries_repository.g.dart @@ -10,13 +10,11 @@ String _$entriesRepositoryHash() => r'17cd56c685d800f8456e9f526108ae479eb0aec2'; /// See also [entriesRepository]. @ProviderFor(entriesRepository) -final entriesRepositoryProvider = - AutoDisposeProvider.internal( +final entriesRepositoryProvider = AutoDisposeProvider.internal( entriesRepository, name: r'entriesRepositoryProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$entriesRepositoryHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$entriesRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -82,8 +80,7 @@ class JobEntriesQueryFamily extends Family> { static const Iterable? _allTransitiveDependencies = null; @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + Iterable? get allTransitiveDependencies => _allTransitiveDependencies; @override String? get name => r'jobEntriesQueryProvider'; @@ -102,12 +99,9 @@ class JobEntriesQueryProvider extends AutoDisposeProvider> { from: jobEntriesQueryProvider, name: r'jobEntriesQueryProvider', debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$jobEntriesQueryHash, + const bool.fromEnvironment('dart.vm.product') ? null : _$jobEntriesQueryHash, dependencies: JobEntriesQueryFamily._dependencies, - allTransitiveDependencies: - JobEntriesQueryFamily._allTransitiveDependencies, + allTransitiveDependencies: JobEntriesQueryFamily._allTransitiveDependencies, jobId: jobId, ); @@ -167,8 +161,8 @@ mixin JobEntriesQueryRef on AutoDisposeProviderRef> { String get jobId; } -class _JobEntriesQueryProviderElement - extends AutoDisposeProviderElement> with JobEntriesQueryRef { +class _JobEntriesQueryProviderElement extends AutoDisposeProviderElement> + with JobEntriesQueryRef { _JobEntriesQueryProviderElement(super.provider); @override diff --git a/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart b/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart index 5cac5dd..a90ee39 100644 --- a/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart +++ b/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart @@ -6,8 +6,7 @@ part of 'entry_screen_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$entryScreenControllerHash() => - r'75638e7eac6bacd498349a143fc5fc827171674a'; +String _$entryScreenControllerHash() => r'75638e7eac6bacd498349a143fc5fc827171674a'; /// See also [EntryScreenController]. @ProviderFor(EntryScreenController) @@ -15,9 +14,8 @@ final entryScreenControllerProvider = AutoDisposeAsyncNotifierProvider.internal( EntryScreenController.new, name: r'entryScreenControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$entryScreenControllerHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$entryScreenControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/jobs/data/jobs_repository.g.dart b/lib/src/features/jobs/data/jobs_repository.g.dart index 446d355..2fc7755 100644 --- a/lib/src/features/jobs/data/jobs_repository.g.dart +++ b/lib/src/features/jobs/data/jobs_repository.g.dart @@ -13,9 +13,8 @@ String _$jobsRepositoryHash() => r'38b37bbcb0ced4ca0754f549ebbe9384bc2bda31'; final jobsRepositoryProvider = Provider.internal( jobsRepository, name: r'jobsRepositoryProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$jobsRepositoryHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$jobsRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -30,8 +29,7 @@ String _$jobsQueryHash() => r'aeaccb50f75b9e5bc97b07443935ffd432dba51a'; final jobsQueryProvider = AutoDisposeProvider>.internal( jobsQuery, name: r'jobsQueryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jobsQueryHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null : _$jobsQueryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -97,8 +95,7 @@ class JobStreamFamily extends Family> { static const Iterable? _allTransitiveDependencies = null; @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + Iterable? get allTransitiveDependencies => _allTransitiveDependencies; @override String? get name => r'jobStreamProvider'; @@ -117,9 +114,7 @@ class JobStreamProvider extends AutoDisposeStreamProvider { from: jobStreamProvider, name: r'jobStreamProvider', debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$jobStreamHash, + const bool.fromEnvironment('dart.vm.product') ? null : _$jobStreamHash, dependencies: JobStreamFamily._dependencies, allTransitiveDependencies: JobStreamFamily._allTransitiveDependencies, jobId: jobId, @@ -181,8 +176,7 @@ mixin JobStreamRef on AutoDisposeStreamProviderRef { String get jobId; } -class _JobStreamProviderElement extends AutoDisposeStreamProviderElement - with JobStreamRef { +class _JobStreamProviderElement extends AutoDisposeStreamProviderElement with JobStreamRef { _JobStreamProviderElement(super.provider); @override diff --git a/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart b/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart index 4a7ae1f..b946650 100644 --- a/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart +++ b/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart @@ -6,8 +6,7 @@ part of 'edit_job_screen_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$editJobScreenControllerHash() => - r'e2985913f443860f6aa9d1b0aa462d4e5c25bed4'; +String _$editJobScreenControllerHash() => r'e2985913f443860f6aa9d1b0aa462d4e5c25bed4'; /// See also [EditJobScreenController]. @ProviderFor(EditJobScreenController) @@ -15,9 +14,8 @@ final editJobScreenControllerProvider = AutoDisposeAsyncNotifierProvider.internal( EditJobScreenController.new, name: r'editJobScreenControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$editJobScreenControllerHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$editJobScreenControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart b/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart index 09cce2b..266358d 100644 --- a/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart +++ b/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart @@ -6,8 +6,7 @@ part of 'job_entries_list_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$jobsEntriesListControllerHash() => - r'f9a08b66a0c962d210a09aebb711d38acb354b1e'; +String _$jobsEntriesListControllerHash() => r'f9a08b66a0c962d210a09aebb711d38acb354b1e'; /// See also [JobsEntriesListController]. @ProviderFor(JobsEntriesListController) @@ -15,9 +14,8 @@ final jobsEntriesListControllerProvider = AutoDisposeAsyncNotifierProvider.internal( JobsEntriesListController.new, name: r'jobsEntriesListControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$jobsEntriesListControllerHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$jobsEntriesListControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart b/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart index 5f8db94..8c63127 100644 --- a/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart +++ b/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart @@ -6,8 +6,7 @@ part of 'jobs_screen_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$jobsScreenControllerHash() => - r'e3a40258404cf512fd12924d8f0a485f75d7d6fb'; +String _$jobsScreenControllerHash() => r'e3a40258404cf512fd12924d8f0a485f75d7d6fb'; /// See also [JobsScreenController]. @ProviderFor(JobsScreenController) @@ -15,9 +14,8 @@ final jobsScreenControllerProvider = AutoDisposeAsyncNotifierProvider.internal( JobsScreenController.new, name: r'jobsScreenControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$jobsScreenControllerHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$jobsScreenControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/onboarding/data/onboarding_repository.g.dart b/lib/src/features/onboarding/data/onboarding_repository.g.dart index ef74384..370bdd9 100644 --- a/lib/src/features/onboarding/data/onboarding_repository.g.dart +++ b/lib/src/features/onboarding/data/onboarding_repository.g.dart @@ -6,18 +6,15 @@ part of 'onboarding_repository.dart'; // RiverpodGenerator // ************************************************************************** -String _$onboardingRepositoryHash() => - r'445c529dd1ac7515d8be0abd6159af6958ff3c5c'; +String _$onboardingRepositoryHash() => r'445c529dd1ac7515d8be0abd6159af6958ff3c5c'; /// See also [onboardingRepository]. @ProviderFor(onboardingRepository) -final onboardingRepositoryProvider = - FutureProvider.internal( +final onboardingRepositoryProvider = FutureProvider.internal( onboardingRepository, name: r'onboardingRepositoryProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$onboardingRepositoryHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$onboardingRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/onboarding/presentation/onboarding_controller.g.dart b/lib/src/features/onboarding/presentation/onboarding_controller.g.dart index 441fe94..6a5525c 100644 --- a/lib/src/features/onboarding/presentation/onboarding_controller.g.dart +++ b/lib/src/features/onboarding/presentation/onboarding_controller.g.dart @@ -6,8 +6,7 @@ part of 'onboarding_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$onboardingControllerHash() => - r'232966a6326a75bb5f5166c8b76bbbb15087adaf'; +String _$onboardingControllerHash() => r'232966a6326a75bb5f5166c8b76bbbb15087adaf'; /// See also [OnboardingController]. @ProviderFor(OnboardingController) @@ -15,9 +14,8 @@ final onboardingControllerProvider = AutoDisposeAsyncNotifierProvider.internal( OnboardingController.new, name: r'onboardingControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$onboardingControllerHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$onboardingControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/routing/app_router.g.dart b/lib/src/routing/app_router.g.dart index 2669284..c9443fd 100644 --- a/lib/src/routing/app_router.g.dart +++ b/lib/src/routing/app_router.g.dart @@ -13,8 +13,7 @@ String _$goRouterHash() => r'bdb6fbb3c1421654e085ee95c8071b4996f3f578'; final goRouterProvider = AutoDisposeProvider.internal( goRouter, name: r'goRouterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$goRouterHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null : _$goRouterHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/routing/app_startup.g.dart b/lib/src/routing/app_startup.g.dart index 4f07aae..5d868d6 100644 --- a/lib/src/routing/app_startup.g.dart +++ b/lib/src/routing/app_startup.g.dart @@ -13,8 +13,7 @@ String _$appStartupHash() => r'e4ee7c8520e85c205f71d32783e8c8f4809ea3a6'; final appStartupProvider = FutureProvider.internal( appStartup, name: r'appStartupProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$appStartupHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null : _$appStartupHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/utils/shared_preferences_provider.g.dart b/lib/src/utils/shared_preferences_provider.g.dart index d714c59..f46bd7c 100644 --- a/lib/src/utils/shared_preferences_provider.g.dart +++ b/lib/src/utils/shared_preferences_provider.g.dart @@ -10,13 +10,11 @@ String _$sharedPreferencesHash() => r'8ad0f57d954e8c413084bfe5492c81b218c3e4a0'; /// See also [sharedPreferences]. @ProviderFor(sharedPreferences) -final sharedPreferencesProvider = - FutureProvider.internal( +final sharedPreferencesProvider = FutureProvider.internal( sharedPreferences, name: r'sharedPreferencesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$sharedPreferencesHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$sharedPreferencesHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/pubspec.lock b/pubspec.lock index 575a7e7..e120872 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,39 +5,34 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57 url: "https://pub.dev" source: hosted - version: "76.0.0" + version: "80.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: eae3133cbb06de9205899b822e3897fc6a8bc278ad4c944b4ce612689369694b + sha256: e051259913915ea5bc8fe18664596bea08592fd123930605d562969cd7315fcd url: "https://pub.dev" source: hosted - version: "1.3.47" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.3" + version: "1.3.51" analyzer: dependency: transitive description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e" url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "7.3.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + sha256: "1d460d14e3c2ae36dc2b32cef847c4479198cf87704f63c3c3c8150ee50c3916" url: "https://pub.dev" source: hosted - version: "0.11.3" + version: "0.12.0" args: dependency: transitive description: @@ -50,66 +45,66 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" build: dependency: transitive description: name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_config: dependency: transitive description: name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" build_daemon: dependency: transitive description: name: build_daemon - sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.0.4" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" + sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" url: "https://pub.dev" source: hosted - version: "2.4.13" + version: "2.4.15" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" url: "https://pub.dev" source: hosted - version: "7.3.2" + version: "8.0.0" built_collection: dependency: transitive description: @@ -122,18 +117,18 @@ packages: dependency: transitive description: name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" url: "https://pub.dev" source: hosted - version: "8.9.2" + version: "8.9.3" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -162,34 +157,34 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" cloud_firestore: dependency: "direct main" description: name: cloud_firestore - sha256: "852c1a14399be76c71a881d2475392393f2ceef77c0859405a6bcac5ec8c0221" + sha256: d7204f3263ba3236c037972f1ea2821569bd7b896fa348c3d557e3b76b6dc143 url: "https://pub.dev" source: hosted - version: "5.5.1" + version: "5.6.3" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface - sha256: "69f0baeb7ac0577946f82991075b18133fd81441aeda487e41fb5692958117cd" + sha256: "10a8519164a0e38fce52f78d540bce1170fc210d07989fe49597723400fcd0f1" url: "https://pub.dev" source: hosted - version: "6.5.1" + version: "6.6.3" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: ff65ee2f8caafb1bcba3133666d5ee00848f94c677900a368cad593982ef8ba8 + sha256: "4b9e34f53c32dc9891aea247d82bfb21fe7779c0064d84baea1a4f18210146de" url: "https://pub.dev" source: hosted - version: "4.3.5" + version: "4.4.3" code_builder: dependency: transitive description: @@ -202,10 +197,10 @@ packages: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" convert: dependency: transitive description: @@ -234,42 +229,42 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: "3486c470bb93313a9417f926c7dd694a2e349220992d7b9d14534dc49c15bba9" + sha256: "021897cce2b6c783b2521543e362e7fe1a2eaab17bf80514d8de37f99942ed9e" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.3" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: "42cdc41994eeeddab0d7a722c7093ec52bd0761921eeb2cbdbf33d192a234759" + sha256: e4235b9d8cef59afe621eba086d245205c8a0a6c70cd470be7cb17494d6df32d url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.3" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: "02450c3e45e2a6e8b26c4d16687596ab3c4644dd5792e3313aa9ceba5a49b7f5" + sha256: "6dcee8a017181941c51a110da7e267c1d104dc74bec8862eeb8c85b5c8759a9e" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.1" custom_lint_visitor: dependency: transitive description: name: custom_lint_visitor - sha256: bfe9b7a09c4775a587b58d10ebb871d4fe618237639b1e84d5ec62d7dfef25f9 + sha256: "36282d85714af494ee2d7da8c8913630aa6694da99f104fb2ed4afcf8fc857d8" url: "https://pub.dev" source: hosted - version: "1.0.0+6.11.0" + version: "1.0.0+7.3.0" dart_style: dependency: transitive description: name: dart_style - sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" url: "https://pub.dev" source: hosted - version: "2.3.7" + version: "3.0.1" desktop_webview_auth: dependency: transitive description: @@ -298,10 +293,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" ffi: dependency: transitive description: @@ -322,98 +317,98 @@ packages: dependency: "direct main" description: name: firebase_auth - sha256: "03483af6e67b7c4b696ca9386989a6cd5593569e1ac5af6907ea5f7fd9c16d8b" + sha256: "2886a01a895565722add556510263231390a9f1d1d51eee34c22f9b94a73dd54" url: "https://pub.dev" source: hosted - version: "5.3.4" + version: "5.4.2" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "3e1409f48c48930635705b1237ebbdee8c54c19106a0a4fb321dbb4b642820c4" + sha256: "2e8fe7e6b5869c981f62c0de1a0abef6f626a1daffe92e1e6881448a9d3da778" url: "https://pub.dev" source: hosted - version: "7.4.10" + version: "7.5.2" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: d83fe95c44d73c9c29b006ac7df3aa5e1b8ce92b62edc44e8f86250951fe2cd0 + sha256: c9600115b6f74365a51c735d4c43d4632ad44bfde505fe7c13c838701cd01ff2 url: "https://pub.dev" source: hosted - version: "5.13.5" + version: "5.13.8" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: fef81a53ba1ca618def1f8bef4361df07968434e62cb204c1fb90bb880a03da2 + sha256: "93dc4dd12f9b02c5767f235307f609e61ed9211047132d07f9e02c668f0bfc33" url: "https://pub.dev" source: hosted - version: "3.8.1" + version: "3.11.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: b94b217e3ad745e784960603d33d99471621ecca151c99c670869b76e50ad2a6 + sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf url: "https://pub.dev" source: hosted - version: "5.3.1" + version: "5.4.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "9e69806bb3d905aeec3c1242e0e1475de6ea6d48f456af29d598fb229a2b4e5e" + sha256: "0e13c80f0de8acaa5d0519cbe23c8b4cc138a2d5d508b5755c861bdfc9762678" url: "https://pub.dev" source: hosted - version: "2.18.2" + version: "2.20.0" firebase_dynamic_links: dependency: transitive description: name: firebase_dynamic_links - sha256: "3560e0e9ae91fa96d8ee70faa260b12c49524de283a32d47583b1b0a4f73761a" + sha256: f3b546180b1920ffd366623de384e906d3eccd4368efa8cf155476b0f4780064 url: "https://pub.dev" source: hosted - version: "6.0.11" + version: "6.1.2" firebase_dynamic_links_platform_interface: dependency: transitive description: name: firebase_dynamic_links_platform_interface - sha256: de7c990154d9164edf9524c18bf98f869f079a962198d80a1dcef3e0b69cea8f + sha256: "95ea6f6c5b4b62aa05e312aff30f423e957c2bed3dcf3baa6d262e936021d3a1" url: "https://pub.dev" source: hosted - version: "0.2.6+47" + version: "0.2.7+2" firebase_ui_auth: dependency: "direct main" description: name: firebase_ui_auth - sha256: "66fbbd52b608e6647b6454db23cfd612e9e9be9df6fb633f2aa65479bde29b30" + sha256: cf2cd23625f3df3c6b27e37d04132980f17d707b1adc160bddf425afe1782eed url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.16.1" firebase_ui_firestore: dependency: "direct main" description: name: firebase_ui_firestore - sha256: "9492b9f989457a05e21c47d006beb4cef09be1c56d0fe8df7158f9b056be63a9" + sha256: "9043b9e579cfb23fff6f3c6ab1bdb32697cd4dbebba734db969cb488dd960957" url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.7.1" firebase_ui_localizations: dependency: transitive description: name: firebase_ui_localizations - sha256: "7c0d9f59a7f8dfe728019bc7ac5dc85e119f59ec9ede2e01b128b39b8492090b" + sha256: "01c0c872ce08d16d217490af3438c97c4fcf1187e856dd2525f892178782fc46" url: "https://pub.dev" source: hosted - version: "1.13.0" + version: "1.13.1" firebase_ui_oauth: dependency: transitive description: name: firebase_ui_oauth - sha256: "26fc29107b5ec0c8b1e8a9eb2234f544a5340e87ca4a9741ba2d5ecec0c39e10" + sha256: f7a0a12e7d4f0518848709f093400796a5c7e3ef13f3498ac83793d291341d9e url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "1.6.1" firebase_ui_shared: dependency: transitive description: @@ -460,10 +455,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123" + sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_test: dependency: "direct dev" description: flutter @@ -478,10 +473,10 @@ packages: dependency: "direct main" description: name: force_update_helper - sha256: "40227dff227dabcbb8bf947e1f5dd0a187f339dedd861c6c3e9c1267d2eed3ec" + sha256: e8dcd6381b7edbc3511117b3aa95ceff849e8902d4c12ec8182171c37aa9470d url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.1.7" freezed_annotation: dependency: transitive description: @@ -502,18 +497,18 @@ packages: dependency: transitive description: name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" go_router: dependency: "direct main" description: name: go_router - sha256: "2fd11229f59e23e967b0775df8d5948a519cd7e1e8b6e849729e010587b46539" + sha256: "04539267a740931c6d4479a10d466717ca5901c6fdfd3fcda09391bbb8ebd651" url: "https://pub.dev" source: hosted - version: "14.6.2" + version: "14.8.0" graphs: dependency: transitive description: @@ -526,34 +521,34 @@ packages: dependency: transitive description: name: hotreloader - sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e + sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.3.0" http: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.3.0" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" http_parser: dependency: transitive description: name: http_parser - sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.1.2" intl: dependency: "direct main" description: @@ -574,10 +569,10 @@ packages: dependency: transitive description: name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.7.2" json_annotation: dependency: transitive description: @@ -590,18 +585,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -614,10 +609,10 @@ packages: dependency: transitive description: name: lints - sha256: "4a16b3f03741e1252fda5de3ce712666d010ba2122f8e912c94f9f7b90e1a4c3" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.1.1" logging: dependency: transitive description: @@ -626,22 +621,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - macros: - dependency: transitive - description: - name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" - url: "https://pub.dev" - source: hosted - version: "0.1.3-main.0" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -654,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" mime: dependency: transitive description: @@ -686,26 +673,26 @@ packages: dependency: transitive description: name: package_info_plus - sha256: "70c421fe9d9cc1a9a7f3b05ae56befd469fe4f8daa3b484823141a55442d858d" + sha256: "67eae327b1b0faf761964a1d2e5d323c797f3799db0e85aa232db8d9e922bc35" url: "https://pub.dev" source: hosted - version: "8.1.2" + version: "8.2.1" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: a5ef9986efc7bf772f2696183a3992615baa76c1ffb1189318dd8803778fb05b + sha256: "205ec83335c2ab9107bbba3f8997f9356d72ca3c715d2f038fc773d0366b4c76" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.1.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_parsing: dependency: transitive description: @@ -742,10 +729,10 @@ packages: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "6.1.0" platform: dependency: transitive description: @@ -782,10 +769,10 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.5.0" random_string: dependency: "direct dev" description: @@ -806,10 +793,10 @@ packages: dependency: transitive description: name: riverpod_analyzer_utils - sha256: c6b8222b2b483cb87ae77ad147d6408f400c64f060df7a225b127f4afef4f8c8 + sha256: "837a6dc33f490706c7f4632c516bcd10804ee4d9ccc8046124ca56388715fdf3" url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.5.9" riverpod_annotation: dependency: "direct main" description: @@ -822,18 +809,18 @@ packages: dependency: "direct dev" description: name: riverpod_generator - sha256: "63546d70952015f0981361636bf8f356d9cfd9d7f6f0815e3c07789a41233188" + sha256: "120d3310f687f43e7011bb213b90a436f1bbc300f0e4b251a72c39bccb017a4f" url: "https://pub.dev" source: hosted - version: "2.6.3" + version: "2.6.4" riverpod_lint: dependency: "direct dev" description: name: riverpod_lint - sha256: "83e4caa337a9840469b7b9bd8c2351ce85abad80f570d84146911b32086fbd99" + sha256: b05408412b0f75dec954e032c855bc28349eeed2d2187f94519e1ddfdf8b3693 url: "https://pub.dev" source: hosted - version: "2.6.3" + version: "2.6.4" rxdart: dependency: "direct main" description: @@ -846,26 +833,26 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82" + sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.5.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549" + sha256: ea86be7b7114f9e94fddfbb52649e59a03d6627ccd2387ebddcd6624719e9f16 url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.4.5" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d" + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_linux: dependency: transitive description: @@ -910,10 +897,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" sky_engine: dependency: transitive description: flutter @@ -923,18 +910,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "2.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" sprintf: dependency: transitive description: @@ -947,10 +934,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" state_notifier: dependency: transitive description: @@ -963,42 +950,42 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" timing: dependency: transitive description: @@ -1067,18 +1054,18 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" uuid: dependency: "direct main" description: @@ -1091,18 +1078,18 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7" + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" url: "https://pub.dev" source: hosted - version: "1.1.15" + version: "1.1.18" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb" + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" url: "https://pub.dev" source: hosted - version: "1.1.12" + version: "1.1.13" vector_graphics_compiler: dependency: transitive description: @@ -1123,18 +1110,18 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.3.1" watcher: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web: dependency: transitive description: @@ -1155,18 +1142,18 @@ packages: dependency: transitive description: name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" win32: dependency: transitive description: name: win32 - sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.9.0" + version: "5.10.1" xdg_directories: dependency: transitive description: @@ -1187,10 +1174,10 @@ packages: dependency: transitive description: name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" sdks: - dart: ">=3.6.0 <4.0.0" - flutter: ">=3.24.0" + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.27.0" From e2d8839d131d469d49adb43ca7f714771e521968 Mon Sep 17 00:00:00 2001 From: alexdivadi Date: Sat, 15 Feb 2025 11:08:05 -0800 Subject: [PATCH 3/7] chore: add dev flavor --- .gitignore | 4 +- .vscode/launch.json | 11 +- android/app/build.gradle | 14 + assets/{ => common}/time-tracking.svg | 0 firebase.json | 2 +- flutterfire-config.sh | 33 ++ ios/Podfile | 2 +- ios/Podfile.lock | 120 +++---- ios/Runner.xcodeproj/project.pbxproj | 316 ++++++++++++++++-- .../xcshareddata/xcschemes/dev.xcscheme | 77 +++++ .../{Runner.xcscheme => prod.xcscheme} | 10 +- ios/Runner/Info.plist | 2 +- lib/env/flavor.dart | 16 + lib/env/initialize_firebase.dart | 22 ++ lib/main.dart | 9 +- lib/src/app.dart | 2 +- .../data/onboarding_repository.dart | 2 +- pubspec.yaml | 8 +- 18 files changed, 552 insertions(+), 98 deletions(-) rename assets/{ => common}/time-tracking.svg (100%) create mode 100755 flutterfire-config.sh create mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme rename ios/Runner.xcodeproj/xcshareddata/xcschemes/{Runner.xcscheme => prod.xcscheme} (93%) create mode 100644 lib/env/flavor.dart create mode 100644 lib/env/initialize_firebase.dart diff --git a/.gitignore b/.gitignore index 56ec81b..f206952 100644 --- a/.gitignore +++ b/.gitignore @@ -95,12 +95,14 @@ unlinked_spec.ds !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages # Firebase configuration files -lib/firebase_options.dart +lib/firebase_options*.dart ios/Runner/GoogleService-Info.plist +ios/flavors/**/GoogleService-Info.plist ios/firebase_app_id_file.json macos/Runner/GoogleService-Info.plist macos/firebase_app_id_file.json android/app/google-services.json +android/app/src/**/google-services.json # Firebase hosting .firebase/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 6a64ca3..112f0a9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,9 +5,16 @@ "version": "0.2.0", "configurations": [ { - "name": "Run", + "name": "dev", "request": "launch", - "type": "dart" + "type": "dart", + "args": ["--flavor", "dev"] + }, + { + "name": "prod", + "request": "launch", + "type": "dart", + "args": ["--flavor", "prod"] }, { "name": "Run (profile mode)", diff --git a/android/app/build.gradle b/android/app/build.gradle index dba0258..62ac890 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -40,6 +40,20 @@ android { signingConfig = signingConfigs.debug } } + + flavorDimensions "app" + productFlavors { + dev { + dimension = "app" + resValue(type = "string", name = "app_name", value = "[DEV] Illemo") + applicationIdSuffix = ".dev" + } + prod { + dimension = "app" + resValue(type = "string", name = "app_name", value = "Illemo") + applicationId = "com.illemo.app" + } + } } flutter { diff --git a/assets/time-tracking.svg b/assets/common/time-tracking.svg similarity index 100% rename from assets/time-tracking.svg rename to assets/common/time-tracking.svg diff --git a/firebase.json b/firebase.json index b5eebd9..fce496a 100644 --- a/firebase.json +++ b/firebase.json @@ -1 +1 @@ -{"hosting":{"public":"build/web","ignore":["firebase.json","**/.*","**/node_modules/**"],"rewrites":[{"source":"**","destination":"/index.html"}]},"flutter":{"platforms":{"android":{"default":{"projectId":"illemo","appId":"1:400942831450:android:ac70caf455b226fbb38563","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"illemo","appId":"1:400942831450:ios:0254744a14137cedb38563","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"illemo","appId":"1:400942831450:ios:0254744a14137cedb38563","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"illemo","configurations":{"android":"1:400942831450:android:ac70caf455b226fbb38563","ios":"1:400942831450:ios:0254744a14137cedb38563","macos":"1:400942831450:ios:0254744a14137cedb38563","web":"1:400942831450:web:fe76d1af4b29664bb38563"}}}}}} \ No newline at end of file +{"hosting":{"public":"build/web","ignore":["firebase.json","**/.*","**/node_modules/**"],"rewrites":[{"source":"**","destination":"/index.html"}]},"flutter":{"platforms":{"android":{"default":{"projectId":"illemo","appId":"1:400942831450:android:ac70caf455b226fbb38563","fileOutput":"android/app/google-services.json"},"buildConfigurations":{"src/prod":{"projectId":"illemo","appId":"1:400942831450:android:ac70caf455b226fbb38563","fileOutput":"android/app/src/prod/google-services.json"},"src/dev":{"projectId":"illemo-dev","appId":"1:456664777287:android:a55eef198655aad75e6762","fileOutput":"android/app/src/dev/google-services.json"}}},"ios":{"default":{"projectId":"illemo","appId":"1:400942831450:ios:0254744a14137cedb38563","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"},"buildConfigurations":{"Debug-prod":{"projectId":"illemo","appId":"1:400942831450:ios:0254744a14137cedb38563","uploadDebugSymbols":false,"fileOutput":"ios/flavors/prod/GoogleService-Info.plist"},"Debug-dev":{"projectId":"illemo-dev","appId":"1:456664777287:ios:79fd31685af89ae85e6762","uploadDebugSymbols":false,"fileOutput":"ios/flavors/dev/GoogleService-Info.plist"}}},"macos":{"default":{"projectId":"illemo-dev","appId":"1:456664777287:ios:e1029d7cb89df3b85e6762","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"illemo","configurations":{"android":"1:400942831450:android:ac70caf455b226fbb38563","ios":"1:400942831450:ios:0254744a14137cedb38563","macos":"1:400942831450:ios:0254744a14137cedb38563","web":"1:400942831450:web:fe76d1af4b29664bb38563"}},"lib/firebase_options_prod.dart":{"projectId":"illemo","configurations":{"android":"1:400942831450:android:ac70caf455b226fbb38563","ios":"1:400942831450:ios:0254744a14137cedb38563","macos":"1:400942831450:ios:0254744a14137cedb38563","web":"1:400942831450:web:fe76d1af4b29664bb38563"}},"lib/firebase_options_dev.dart":{"projectId":"illemo-dev","configurations":{"android":"1:456664777287:android:a55eef198655aad75e6762","ios":"1:456664777287:ios:79fd31685af89ae85e6762","macos":"1:456664777287:ios:e1029d7cb89df3b85e6762","web":"1:456664777287:web:551e49c957d1eed55e6762"}}}}}} \ No newline at end of file diff --git a/flutterfire-config.sh b/flutterfire-config.sh new file mode 100755 index 0000000..19b64a7 --- /dev/null +++ b/flutterfire-config.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Script to generate Firebase configuration files for different environments/flavors +# Feel free to reuse and adapt this script for your own projects + +if [[ $# -eq 0 ]]; then + echo "Error: No environment specified. Use 'dev', 'stg', or 'prod'." + exit 1 +fi + +case $1 in + dev) + flutterfire config \ + --project=illemo-dev \ + --out=lib/firebase_options_dev.dart \ + --ios-bundle-id=com.illemo.app.dev \ + --ios-out=ios/flavors/dev/GoogleService-Info.plist \ + --android-package-name=com.illemo.app.dev \ + --android-out=android/app/src/dev/google-services.json + ;; + prod) + flutterfire config \ + --project=illemo \ + --out=lib/firebase_options_prod.dart \ + --ios-bundle-id=com.illemo.app \ + --ios-out=ios/flavors/prod/GoogleService-Info.plist \ + --android-package-name=com.illemo.app \ + --android-out=android/app/src/prod/google-services.json + ;; + *) + echo "Error: Invalid environment specified. Use 'dev', 'stg', or 'prod'." + exit 1 + ;; +esac \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index 986bc39..ac4fcbf 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -28,7 +28,7 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do - pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '11.4.0' + pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '11.7.0' use_frameworks! use_modular_headers! diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b95951b..3ff9e06 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,61 +1,61 @@ PODS: - - cloud_firestore (5.5.1): - - Firebase/Firestore (= 11.4.0) + - cloud_firestore (5.6.3): + - Firebase/Firestore (= 11.7.0) - firebase_core - Flutter - desktop_webview_auth (0.0.1): - Flutter - - Firebase/Auth (11.4.0): + - Firebase/Auth (11.7.0): - Firebase/CoreOnly - - FirebaseAuth (~> 11.4.0) - - Firebase/CoreOnly (11.4.0): - - FirebaseCore (= 11.4.0) - - Firebase/DynamicLinks (11.4.0): + - FirebaseAuth (~> 11.7.0) + - Firebase/CoreOnly (11.7.0): + - FirebaseCore (~> 11.7.0) + - Firebase/DynamicLinks (11.7.0): - Firebase/CoreOnly - - FirebaseDynamicLinks (~> 11.4.0) - - Firebase/Firestore (11.4.0): + - FirebaseDynamicLinks (~> 11.7.0) + - Firebase/Firestore (11.7.0): - Firebase/CoreOnly - - FirebaseFirestore (~> 11.4.0) - - firebase_auth (5.3.4): - - Firebase/Auth (= 11.4.0) + - FirebaseFirestore (~> 11.7.0) + - firebase_auth (5.4.2): + - Firebase/Auth (= 11.7.0) - firebase_core - Flutter - - firebase_core (3.8.1): - - Firebase/CoreOnly (= 11.4.0) + - firebase_core (3.11.0): + - Firebase/CoreOnly (= 11.7.0) - Flutter - - firebase_dynamic_links (6.0.11): - - Firebase/DynamicLinks (= 11.4.0) + - firebase_dynamic_links (6.1.2): + - Firebase/DynamicLinks (= 11.7.0) - firebase_core - Flutter - - FirebaseAppCheckInterop (11.5.0) - - FirebaseAuth (11.4.0): + - FirebaseAppCheckInterop (11.8.0) + - FirebaseAuth (11.7.0): - FirebaseAppCheckInterop (~> 11.0) - FirebaseAuthInterop (~> 11.0) - - FirebaseCore (~> 11.4) - - FirebaseCoreExtension (~> 11.4) + - FirebaseCore (~> 11.7.0) + - FirebaseCoreExtension (~> 11.7.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/Environment (~> 8.0) - GTMSessionFetcher/Core (< 5.0, >= 3.4) - RecaptchaInterop (~> 100.0) - - FirebaseAuthInterop (11.5.0) - - FirebaseCore (11.4.0): - - FirebaseCoreInternal (~> 11.0) + - FirebaseAuthInterop (11.8.0) + - FirebaseCore (11.7.0): + - FirebaseCoreInternal (~> 11.7.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Logger (~> 8.0) - - FirebaseCoreExtension (11.4.0): - - FirebaseCore (~> 11.0) - - FirebaseCoreInternal (11.5.0): + - FirebaseCoreExtension (11.7.0): + - FirebaseCore (~> 11.7.0) + - FirebaseCoreInternal (11.7.0): - "GoogleUtilities/NSData+zlib (~> 8.0)" - - FirebaseDynamicLinks (11.4.0): - - FirebaseCore (~> 11.0) - - FirebaseFirestore (11.4.0): - - FirebaseFirestoreBinary (= 11.4.0) + - FirebaseDynamicLinks (11.7.0): + - FirebaseCore (~> 11.7.0) + - FirebaseFirestore (11.7.0): + - FirebaseFirestoreBinary (= 11.7.0) - FirebaseFirestoreAbseilBinary (1.2024011602.0) - - FirebaseFirestoreBinary (11.4.0): - - FirebaseCore (= 11.4.0) - - FirebaseCoreExtension (= 11.4.0) - - FirebaseFirestoreInternalBinary (= 11.4.0) - - FirebaseSharedSwift (= 11.4.0) + - FirebaseFirestoreBinary (11.7.0): + - FirebaseCore (= 11.7.0) + - FirebaseCoreExtension (= 11.7.0) + - FirebaseFirestoreInternalBinary (= 11.7.0) + - FirebaseSharedSwift (= 11.7.0) - FirebaseFirestoreGRPCBoringSSLBinary (1.65.1) - FirebaseFirestoreGRPCCoreBinary (1.65.1): - FirebaseFirestoreAbseilBinary (= 1.2024011602.0) @@ -63,13 +63,13 @@ PODS: - FirebaseFirestoreGRPCCPPBinary (1.65.1): - FirebaseFirestoreAbseilBinary (= 1.2024011602.0) - FirebaseFirestoreGRPCCoreBinary (= 1.65.1) - - FirebaseFirestoreInternalBinary (11.4.0): - - FirebaseCore (= 11.4.0) + - FirebaseFirestoreInternalBinary (11.7.0): + - FirebaseCore (= 11.7.0) - FirebaseFirestoreAbseilBinary (= 1.2024011602.0) - FirebaseFirestoreGRPCCPPBinary (= 1.65.1) - leveldb-library (~> 1.22) - nanopb (~> 3.30910.0) - - FirebaseSharedSwift (11.4.0) + - FirebaseSharedSwift (11.7.0) - Flutter (1.0.0) - GoogleUtilities/AppDelegateSwizzler (8.0.2): - GoogleUtilities/Environment @@ -92,7 +92,7 @@ PODS: - GoogleUtilities/Reachability (8.0.2): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GTMSessionFetcher/Core (4.1.0) + - GTMSessionFetcher/Core (4.3.0) - leveldb-library (1.22.6) - nanopb (3.30910.0): - nanopb/decode (= 3.30910.0) @@ -114,7 +114,7 @@ DEPENDENCIES: - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_dynamic_links (from `.symlinks/plugins/firebase_dynamic_links/ios`) - - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `11.4.0`) + - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `11.7.0`) - Flutter (from `Flutter`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) @@ -156,7 +156,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_dynamic_links/ios" FirebaseFirestore: :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git - :tag: 11.4.0 + :tag: 11.7.0 Flutter: :path: Flutter package_info_plus: @@ -169,33 +169,33 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: FirebaseFirestore: :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git - :tag: 11.4.0 + :tag: 11.7.0 SPEC CHECKSUMS: - cloud_firestore: 3fafe78d755b01fe1fd267a87bb52e80b7dacacc + cloud_firestore: 75151f94e2adb9685d6133d6cd06bb2d9026604e desktop_webview_auth: d645139460ef203d50bd0cdb33356785dd939cce - Firebase: cf1b19f21410b029b6786a54e9764a0cacad3c99 - firebase_auth: c4bdd9d7b338ac004008cb5024a643584e0ec03f - firebase_core: 418aed674e9a0b8b6088aec16cde82a811f6261f - firebase_dynamic_links: b6c2a7d6e50d2d0c0ae2acb213af78e9a3d9ce9b - FirebaseAppCheckInterop: d265d9f4484e7ec1c591086408840fdd383d1213 - FirebaseAuth: c359af98bd703cbf4293eec107a40de08ede6ce6 - FirebaseAuthInterop: 1219bee9b23e6ebe84c256a0d95adab53d11c331 - FirebaseCore: e0510f1523bc0eb21653cac00792e1e2bd6f1771 - FirebaseCoreExtension: 4445e4cd877e0790c4af33bedca61eaef27b7513 - FirebaseCoreInternal: f47dd28ae7782e6a4738aad3106071a8fe0af604 - FirebaseDynamicLinks: 192110d77418357fe994f2823a7df7db3ccb15bf - FirebaseFirestore: 97f18b411aaca686a9e94ba742341228643b1a46 + Firebase: a64bf6a8546e6eab54f1c715cd6151f39d2329f4 + firebase_auth: bcc25c2992fb3e655b59faca6f0f5bc9215d463d + firebase_core: aa979ae726f00b3ef4ccf59dfb96170af84efbd4 + firebase_dynamic_links: de96307a996b1c92b3b715f683ed5e320ad3c033 + FirebaseAppCheckInterop: 7bf86d55a2b7e9bd91464120eba3e52e4b63b2e2 + FirebaseAuth: 77e25aa24f3e1c626c5babd3338551fc1669ee0e + FirebaseAuthInterop: 651756e70d9a0b9160db39ead71fd5507dbb6c84 + FirebaseCore: 3227e35f4197a924206fbcdc0349325baf4f5de4 + FirebaseCoreExtension: 206c1b399f0d103055207c16f299b28e3dbd1949 + FirebaseCoreInternal: d6c17dafc8dc33614733a8b52df78fcb4394c881 + FirebaseDynamicLinks: e81e03f6076bd02081ae6e06631797e134380a76 + FirebaseFirestore: 61305c5ac196ec1526dde68ac132543a7749a081 FirebaseFirestoreAbseilBinary: fa2ebd2ed02cadef5382e4f7c93f1b265c812c85 - FirebaseFirestoreBinary: d0380b879b58d663b22467017d0e348d5b17b36b + FirebaseFirestoreBinary: 86eaad2ff00b789242734496029a3d08d4d86a89 FirebaseFirestoreGRPCBoringSSLBinary: d86ebbe2adc8d15d7ebf305fff7d6358385327f8 FirebaseFirestoreGRPCCoreBinary: 472bd808e1886a5efb2fd03dd09b98d34641a335 FirebaseFirestoreGRPCCPPBinary: db76d83d2b7517623f8426ed7f7a17bad2478084 - FirebaseFirestoreInternalBinary: 01c33a6d789b95dce32dbdfcaf30d60ddf8902d8 - FirebaseSharedSwift: 505dae2d05969dbf6d43749a642bb1bf230f0252 + FirebaseFirestoreInternalBinary: 1850c8c72f3d7933a00a4d0bae88021df87c9e10 + FirebaseSharedSwift: a45efd84d60ebbfdcdbaebc66948af3630459e62 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d - GTMSessionFetcher: 923b710231ad3d6f3f0495ac1ced35421e07d9a6 + GTMSessionFetcher: 257ead9ba8e15a2d389d79496e02b9cc5dd0c62c leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 @@ -203,6 +203,6 @@ SPEC CHECKSUMS: shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe -PODFILE CHECKSUM: 7971d8193c543b454b5092b866ba615e5fd53a2a +PODFILE CHECKSUM: c70cad528bc2dff4479290bb137f19fe65bb7e08 COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 520a3ba..a8ece71 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -31,10 +31,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 07ACBC9FE73D42080C2292DA /* Pods-Runner.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-dev.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 39ED94431E1BA3F48DC22182 /* Pods-Runner.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-dev.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 4717B7480637AA09CAEB3FC0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 4B680343221DB14045D05CF3 /* Pods-Runner.debug-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-prod.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -47,8 +50,11 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B1282A8CCA46FCB860DC6348 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; BE5E729672CE82DA371BE171 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CC85ED41B9F97B69E3979D82 /* Pods-Runner.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-prod.xcconfig"; sourceTree = ""; }; D1CF56C09A84454F93ABC256 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; D4DF1EAB0E660ABB5816D63F /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; + DEEC04F07299965E544DA14C /* Pods-Runner.debug-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-dev.xcconfig"; sourceTree = ""; }; + E0F275124C52BD7CAB532D9D /* Pods-Runner.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-prod.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -123,6 +129,12 @@ B1282A8CCA46FCB860DC6348 /* Pods-Runner.debug.xcconfig */, 4717B7480637AA09CAEB3FC0 /* Pods-Runner.release.xcconfig */, D1CF56C09A84454F93ABC256 /* Pods-Runner.profile.xcconfig */, + 4B680343221DB14045D05CF3 /* Pods-Runner.debug-prod.xcconfig */, + DEEC04F07299965E544DA14C /* Pods-Runner.debug-dev.xcconfig */, + E0F275124C52BD7CAB532D9D /* Pods-Runner.release-prod.xcconfig */, + 07ACBC9FE73D42080C2292DA /* Pods-Runner.release-dev.xcconfig */, + CC85ED41B9F97B69E3979D82 /* Pods-Runner.profile-prod.xcconfig */, + 39ED94431E1BA3F48DC22182 /* Pods-Runner.profile-dev.xcconfig */, ); path = Pods; sourceTree = ""; @@ -143,6 +155,7 @@ 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 48CDBEA72553743FA74C2372 /* [CP] Embed Pods Frameworks */, 3FB93D246001D3A79E3BB749 /* [CP] Copy Pods Resources */, + A3F91875AD94B51DEF204F81 /* FlutterFire: "flutterfire bundle-service-file" */, ); buildRules = ( ); @@ -289,6 +302,24 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + A3F91875AD94B51DEF204F81 /* FlutterFire: "flutterfire bundle-service-file" */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "FlutterFire: \"flutterfire bundle-service-file\""; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\"\nflutterfire bundle-service-file --plist-destination=\"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app\" --build-configuration=${CONFIGURATION} --platform=ios --apple-project-path=\"${SRCROOT}\"\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -323,7 +354,7 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { + 249021D3217E4FDB00AE95B9 /* Profile-prod */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -371,18 +402,20 @@ TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; - name = Profile; + name = "Profile-prod"; }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { + 249021D4217E4FDB00AE95B9 /* Profile-prod */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + APP_DISPLAY_NAME = Illemo; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = M54ZVB688G; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -394,9 +427,242 @@ SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; - name = Profile; + name = "Profile-prod"; + }; + 5CE5ACB02D611390001A4216 /* Debug-dev */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Debug-dev"; + }; + 5CE5ACB12D611390001A4216 /* Debug-dev */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + APP_DISPLAY_NAME = "[DEV] Illemo"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = M54ZVB688G; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.illemo.app.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = "Debug-dev"; + }; + 5CE5ACB22D611397001A4216 /* Release-dev */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = "Release-dev"; + }; + 5CE5ACB32D611397001A4216 /* Release-dev */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + APP_DISPLAY_NAME = "[DEV] Illemo"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = M54ZVB688G; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.illemo.app.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = "Release-dev"; + }; + 5CE5ACB42D61139E001A4216 /* Profile-dev */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = "Profile-dev"; + }; + 5CE5ACB52D61139E001A4216 /* Profile-dev */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + APP_DISPLAY_NAME = "[DEV] Illemo"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = M54ZVB688G; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.illemo.app.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = "Profile-dev"; }; - 97C147031CF9000F007C117D /* Debug */ = { + 97C147031CF9000F007C117D /* Debug-prod */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -449,9 +715,9 @@ SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Debug; + name = "Debug-prod"; }; - 97C147041CF9000F007C117D /* Release */ = { + 97C147041CF9000F007C117D /* Release-prod */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -501,18 +767,20 @@ TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; - name = Release; + name = "Release-prod"; }; - 97C147061CF9000F007C117D /* Debug */ = { + 97C147061CF9000F007C117D /* Debug-prod */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { + APP_DISPLAY_NAME = Illemo; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = M54ZVB688G; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -525,18 +793,20 @@ SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; - name = Debug; + name = "Debug-prod"; }; - 97C147071CF9000F007C117D /* Release */ = { + 97C147071CF9000F007C117D /* Release-prod */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + APP_DISPLAY_NAME = Illemo; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = M54ZVB688G; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -548,7 +818,7 @@ SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; - name = Release; + name = "Release-prod"; }; /* End XCBuildConfiguration section */ @@ -556,22 +826,28 @@ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, + 97C147031CF9000F007C117D /* Debug-prod */, + 5CE5ACB02D611390001A4216 /* Debug-dev */, + 97C147041CF9000F007C117D /* Release-prod */, + 5CE5ACB22D611397001A4216 /* Release-dev */, + 249021D3217E4FDB00AE95B9 /* Profile-prod */, + 5CE5ACB42D61139E001A4216 /* Profile-dev */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-prod"; }; 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, + 97C147061CF9000F007C117D /* Debug-prod */, + 5CE5ACB12D611390001A4216 /* Debug-dev */, + 97C147071CF9000F007C117D /* Release-prod */, + 5CE5ACB32D611397001A4216 /* Release-dev */, + 249021D4217E4FDB00AE95B9 /* Profile-prod */, + 5CE5ACB52D61139E001A4216 /* Profile-dev */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-prod"; }; /* End XCConfigurationList section */ }; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme new file mode 100644 index 0000000..3c206a5 --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme similarity index 93% rename from ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme index 5e31d3d..29d48a1 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme @@ -23,7 +23,7 @@ @@ -40,7 +40,7 @@ + buildConfiguration = "Debug-prod"> diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index d9002bf..e8bad25 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Illemo + $(APP_DISPLAY_NAME) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/lib/env/flavor.dart b/lib/env/flavor.dart new file mode 100644 index 0000000..882f7fb --- /dev/null +++ b/lib/env/flavor.dart @@ -0,0 +1,16 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +enum Flavor { dev, prod } + +Flavor getFlavor() { + if (kIsWeb && kReleaseMode) { + return Flavor.prod; // --flavor is not supported on web + } + return switch (appFlavor) { + 'prod' => Flavor.prod, + 'dev' => Flavor.dev, + null => Flavor.dev, // if not specified, default to dev + _ => throw UnsupportedError('Invalid flavor: $appFlavor'), + }; +} diff --git a/lib/env/initialize_firebase.dart b/lib/env/initialize_firebase.dart new file mode 100644 index 0000000..bf1c61c --- /dev/null +++ b/lib/env/initialize_firebase.dart @@ -0,0 +1,22 @@ +import 'dart:developer'; + +import 'package:illemo/env/flavor.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:illemo/firebase_options_dev.dart' as dev; +import 'package:illemo/firebase_options_prod.dart' as prod; + +Future initializeFirebaseApp() async { + final flavor = getFlavor(); + final firebaseOptions = switch (flavor) { + Flavor.prod => prod.DefaultFirebaseOptions.currentPlatform, + Flavor.dev => dev.DefaultFirebaseOptions.currentPlatform, + }; + + try { + await Firebase.initializeApp( + options: firebaseOptions, + ); + } catch (e) { + log(e.toString(), error: e); + } +} diff --git a/lib/main.dart b/lib/main.dart index 04974af..fb4d472 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,9 @@ -import 'package:firebase_core/firebase_core.dart'; +import 'dart:developer'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:illemo/firebase_options.dart'; +import 'package:illemo/env/initialize_firebase.dart'; import 'package:illemo/src/app.dart'; import 'package:illemo/src/localization/string_hardcoded.dart'; // ignore:depend_on_referenced_packages @@ -16,7 +17,7 @@ Future main() async { // * https://docs.flutter.dev/testing/errors registerErrorHandlers(); // * Initialize Firebase - await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + await initializeFirebaseApp(); // * Entry point of the app runApp(const ProviderScope( child: MyApp(), @@ -31,7 +32,7 @@ void registerErrorHandlers() { }; // * Handle errors from the underlying platform/OS PlatformDispatcher.instance.onError = (Object error, StackTrace stack) { - debugPrint(error.toString()); + log(error.toString(), error: error, stackTrace: stack); return true; }; // * Show some error UI when any widget in the app fails to build diff --git a/lib/src/app.dart b/lib/src/app.dart index 6600af2..2465907 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -55,7 +55,7 @@ class MyApp extends ConsumerWidget { } }, onException: (e, st) { - log(e.toString()); + log(e.toString(), error: e, stackTrace: st); }, child: child!, ), diff --git a/lib/src/features/onboarding/data/onboarding_repository.dart b/lib/src/features/onboarding/data/onboarding_repository.dart index ea8ba80..1fae101 100644 --- a/lib/src/features/onboarding/data/onboarding_repository.dart +++ b/lib/src/features/onboarding/data/onboarding_repository.dart @@ -7,7 +7,7 @@ part 'onboarding_repository.g.dart'; class OnboardingRepository { OnboardingRepository(this.sharedPreferences); - final SharedPreferences sharedPreferences; + final SharedPreferencesWithCache sharedPreferences; static const onboardingCompleteKey = 'onboardingComplete'; diff --git a/pubspec.yaml b/pubspec.yaml index 3c65507..31b8182 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,4 +47,10 @@ dev_dependencies: flutter: uses-material-design: true assets: - - assets/time-tracking.svg + - assets/common/ + - path: assets/dev/ + flavors: + - dev + - path: assets/prod/ + flavors: + - production From 8ec3e7561385b405d3b82f8ab8fd1bd39b1436ac Mon Sep 17 00:00:00 2001 From: alexdivadi Date: Thu, 20 Feb 2025 20:54:19 -0800 Subject: [PATCH 4/7] feat: add emotion screen and handle multiple repositories --- .../data/firebase_auth_repository.g.dart | 10 +-- .../presentation/auth_providers.g.dart | 11 ++-- .../data/repositories/emotion_repository.dart | 61 +++++++++++-------- .../repositories/emotion_repository.g.dart | 24 +++++--- .../emotion_repository_local.dart | 35 +++++------ .../emotion_repository_local.g.dart | 28 --------- .../emotions/presentation/dashboard.dart | 0 .../emotions/presentation/emotion_picker.dart | 42 +++++++++++++ .../service/emotion_today_service.dart | 31 ++++++++++ .../service/emotion_today_service.g.dart | 27 ++++++++ .../entries/application/entries_service.dart | 2 +- .../application/entries_service.g.dart | 16 +++-- .../entries/data/entries_repository.g.dart | 22 ++++--- .../entry_screen_controller.g.dart | 8 ++- .../features/jobs/data/jobs_repository.g.dart | 18 ++++-- .../edit_job_screen_controller.g.dart | 8 ++- .../job_entries_list_controller.g.dart | 8 ++- .../jobs_screen/jobs_screen_controller.g.dart | 8 ++- .../data/onboarding_repository.g.dart | 11 ++-- .../presentation/onboarding_controller.g.dart | 8 ++- lib/src/routing/app_router.dart | 25 +++++--- lib/src/routing/app_router.g.dart | 5 +- lib/src/routing/app_startup.g.dart | 3 +- .../utils/shared_preferences_provider.g.dart | 8 ++- 24 files changed, 275 insertions(+), 144 deletions(-) delete mode 100644 lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart create mode 100644 lib/src/features/emotions/presentation/dashboard.dart create mode 100644 lib/src/features/emotions/presentation/emotion_picker.dart create mode 100644 lib/src/features/emotions/service/emotion_today_service.dart create mode 100644 lib/src/features/emotions/service/emotion_today_service.g.dart diff --git a/lib/src/features/authentication/data/firebase_auth_repository.g.dart b/lib/src/features/authentication/data/firebase_auth_repository.g.dart index b64de85..6905fb3 100644 --- a/lib/src/features/authentication/data/firebase_auth_repository.g.dart +++ b/lib/src/features/authentication/data/firebase_auth_repository.g.dart @@ -29,8 +29,9 @@ String _$authRepositoryHash() => r'0e32dee9e183c43ec14a6b58d74d26deb3950cbc'; final authRepositoryProvider = Provider.internal( authRepository, name: r'authRepositoryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$authRepositoryHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$authRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -45,8 +46,9 @@ String _$authStateChangesHash() => r'7bdb56f405df8ffc5554e0128ec15d474f011ec9'; final authStateChangesProvider = AutoDisposeStreamProvider.internal( authStateChanges, name: r'authStateChangesProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$authStateChangesHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$authStateChangesHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/authentication/presentation/auth_providers.g.dart b/lib/src/features/authentication/presentation/auth_providers.g.dart index 332905d..2b16f38 100644 --- a/lib/src/features/authentication/presentation/auth_providers.g.dart +++ b/lib/src/features/authentication/presentation/auth_providers.g.dart @@ -10,17 +10,20 @@ String _$authProvidersHash() => r'8a83535c31539dac72f21c3f27b7d7fb77161e5f'; /// See also [authProviders]. @ProviderFor(authProviders) -final authProvidersProvider = Provider>>.internal( +final authProvidersProvider = + Provider>>.internal( authProviders, name: r'authProvidersProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$authProvidersHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$authProvidersHash, dependencies: null, allTransitiveDependencies: null, ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef AuthProvidersRef = ProviderRef>>; +typedef AuthProvidersRef + = ProviderRef>>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.dart b/lib/src/features/emotions/data/repositories/emotion_repository.dart index 11bb6ea..e948608 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository.dart @@ -1,66 +1,59 @@ +import 'dart:developer'; + import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:illemo/src/features/authentication/data/firebase_auth_repository.dart'; import 'package:illemo/src/features/authentication/domain/app_user.dart'; +import 'package:illemo/src/features/emotions/data/repositories/emotion_repository_local.dart'; import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; +import 'package:illemo/src/utils/shared_preferences_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'emotion_repository.g.dart'; /// Firestore implementation of [EmotionRepository]. -/// Depends on [firebaseAuthProvider] for user id. -@Riverpod(keepAlive: true) -class EmotionRepository extends _$EmotionRepository { - EmotionRepository(); +/// Requires [UserID] userID. +class EmotionRepository { + EmotionRepository({ + required this.userID, + }); /// Returns the Firestore path for the emotions collection of a specific user. static String emotionsPath(String uid) => 'users/$uid/emotions'; final FirebaseFirestore _firestore = FirebaseFirestore.instance; - late final UserID _userID; - - /// Initializes the repository by getting the current user and fetching today's emotion log (which may be). - /// - /// Throws an [AssertionError] if the user is null. - @override - Future build() async { - final user = ref.watch(firebaseAuthProvider).currentUser; - if (user == null) { - throw AssertionError('User can\'t be null'); - } - _userID = user.uid; - return getEmotionLogToday(); - } + final UserID userID; /// Adds a new emotion log to Firestore. /// /// Converts the [EmotionLog] entity to a map using [EmotionLogModel] before adding it. Future addEmotionLog(EmotionLog emotionLog) async { await _firestore - .collection(emotionsPath(_userID)) + .collection(emotionsPath(userID)) .add(EmotionLogModel.fromEntity(emotionLog).toMap()); } /// Updates an existing emotion log in Firestore. /// /// Converts the [EmotionLog] entity to a map using [EmotionLogModel] before updating it. - Future updateEmotionLog(String id, EmotionLog emotionLog) async { + Future updateEmotionLog(EmotionLogID id, EmotionLog emotionLog) async { await _firestore - .collection(emotionsPath(_userID)) + .collection(emotionsPath(userID)) .doc(id) .update(EmotionLogModel.fromEntity(emotionLog, id: id).toMap()); } /// Deletes an emotion log from Firestore by its document ID. Future deleteEmotionLog(String id) async { - await _firestore.collection(emotionsPath(_userID)).doc(id).delete(); + await _firestore.collection(emotionsPath(userID)).doc(id).delete(); } /// Retrieves an emotion log from Firestore by its document ID. /// /// Returns the [EmotionLog] entity if found, otherwise returns null. Future getEmotionLog(String id) async { - DocumentSnapshot doc = await _firestore.collection(emotionsPath(_userID)).doc(id).get(); + DocumentSnapshot doc = await _firestore.collection(emotionsPath(userID)).doc(id).get(); if (doc.exists) { return EmotionLogModel.fromMap(doc.data() as Map).toEntity(); } @@ -72,7 +65,7 @@ class EmotionRepository extends _$EmotionRepository { /// Returns the [EmotionLog] entity if found, otherwise returns null. Future getEmotionLogToday() async { QuerySnapshot querySnapshot = await _firestore - .collection(emotionsPath(_userID)) + .collection(emotionsPath(userID)) .where('date', isEqualTo: DateTime.now().toIso8601String().split('T').first) .limit(1) .get(); @@ -91,7 +84,7 @@ class EmotionRepository extends _$EmotionRepository { DateTime? startDate, DateTime? endDate, }) { - Query query = _firestore.collection(emotionsPath(_userID)); + Query query = _firestore.collection(emotionsPath(userID)); if (startDate != null) { query = @@ -109,3 +102,21 @@ class EmotionRepository extends _$EmotionRepository { }); } } + +/// Provider for [EmotionRepository]. +/// Requires [UserID] userID. +@riverpod +EmotionRepository emotionRepository(Ref ref) { + final currentUser = ref.watch(firebaseAuthProvider).currentUser; + if (currentUser == null) { + throw AssertionError('User can\'t be null when fetching emotions'); + } + + if (currentUser.isAnonymous) { + log('Using local storage for anonymous user'); + final prefs = ref.watch(sharedPreferencesProvider).requireValue; + return EmotionRepositoryLocal(userID: currentUser.uid, prefs: prefs); + } + log('Using firestore for authenticated user'); + return EmotionRepository(userID: currentUser.uid); +} diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.g.dart b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart index e3849d4..7bc55a5 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository.g.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart @@ -6,22 +6,26 @@ part of 'emotion_repository.dart'; // RiverpodGenerator // ************************************************************************** -String _$emotionRepositoryHash() => r'13ae68e9d0d5907bd2a4b578cb4ff5990be29f78'; +String _$emotionRepositoryHash() => r'b227856391c76764a5ab645787075557b65cb5db'; -/// Firestore implementation of [EmotionRepository]. -/// Depends on [firebaseAuthProvider] for user id. +/// Provider for [EmotionRepository]. +/// Requires [UserID] userID. /// -/// Copied from [EmotionRepository]. -@ProviderFor(EmotionRepository) -final emotionRepositoryProvider = AsyncNotifierProvider.internal( - EmotionRepository.new, +/// Copied from [emotionRepository]. +@ProviderFor(emotionRepository) +final emotionRepositoryProvider = + AutoDisposeProvider.internal( + emotionRepository, name: r'emotionRepositoryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$emotionRepositoryHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$emotionRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); -typedef _$EmotionRepository = AsyncNotifier; +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +typedef EmotionRepositoryRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/emotions/data/repositories/emotion_repository_local.dart b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart index a60188a..e1ac90e 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository_local.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart @@ -1,33 +1,28 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:illemo/src/features/authentication/domain/app_user.dart'; import 'package:illemo/src/features/emotions/data/repositories/emotion_repository.dart'; import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; import 'package:illemo/src/utils/shared_preferences_provider.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; -part 'emotion_repository_local.g.dart'; - /// Local implementation of [EmotionRepository]. /// Depends on [sharedPreferencesProvider] for storing data. -@Riverpod(keepAlive: true) -class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements EmotionRepository { - EmotionRepositoryLocal(); +class EmotionRepositoryLocal implements EmotionRepository { + const EmotionRepositoryLocal({ + required this.userID, + required this.prefs, + }); /// The path used for storing emotion logs in shared preferences. final String _collectionPath = 'emotions'; /// The shared preferences instance with cache. - late final SharedPreferencesWithCache _prefs; - - /// Initializes the repository and retrieves today's emotion log if available. + final SharedPreferencesWithCache prefs; @override - Future build() async { - _prefs = ref.watch(sharedPreferencesProvider).requireValue; - return getEmotionLogToday(); - } + final UserID userID; /// Adds a new emotion log to the local storage. /// @@ -35,7 +30,7 @@ class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements Emotion @override Future addEmotionLog(EmotionLog emotionLog) async { final key = '${_collectionPath}_${emotionLog.date.weekday}'; - await _prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog).toMap())); + await prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog).toMap())); } /// Updates an existing emotion log in the local storage. @@ -46,7 +41,7 @@ class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements Emotion Future updateEmotionLog(String id, EmotionLog emotionLog) async { /// id is the weekday (only stores up to 7 entries) final key = '${_collectionPath}_${emotionLog.date.weekday}'; - await _prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog, id: id).toMap())); + await prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog, id: id).toMap())); } /// Deletes an emotion log from the local storage. @@ -55,7 +50,7 @@ class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements Emotion @override Future deleteEmotionLog(String id) async { final key = '${_collectionPath}_$id'; - await _prefs.remove(key); + await prefs.remove(key); } /// Retrieves an emotion log by its identifier from the local storage. @@ -65,7 +60,7 @@ class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements Emotion @override Future getEmotionLog(String id) async { final key = '${_collectionPath}_$id'; - final jsonString = _prefs.getString(key); + final jsonString = prefs.getString(key); if (jsonString != null) { return EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); } @@ -78,7 +73,7 @@ class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements Emotion @override Future getEmotionLogToday() async { final key = '${_collectionPath}_${DateTime.now().weekday}'; - final jsonString = _prefs.getString(key); + final jsonString = prefs.getString(key); if (jsonString != null) { final EmotionLog emotionLog = EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); if (DateUtils.isSameDay(emotionLog.date, DateTime.now())) { @@ -98,10 +93,10 @@ class EmotionRepositoryLocal extends _$EmotionRepositoryLocal implements Emotion DateTime? startDate, DateTime? endDate, }) async* { - final keys = _prefs.keys.where((key) => key.startsWith(_collectionPath)); + final keys = prefs.keys.where((key) => key.startsWith(_collectionPath)); final logs = []; for (final key in keys) { - final jsonString = _prefs.getString(key); + final jsonString = prefs.getString(key); if (jsonString != null) { final emotionLog = EmotionLogModel.fromMap(jsonDecode(jsonString)).toEntity(); if ((startDate == null || emotionLog.date.isAfter(startDate)) && diff --git a/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart b/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart deleted file mode 100644 index 11c4760..0000000 --- a/lib/src/features/emotions/data/repositories/emotion_repository_local.g.dart +++ /dev/null @@ -1,28 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'emotion_repository_local.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$emotionRepositoryLocalHash() => r'685a805fb855d044572d0d97ad9dfa374c88c82b'; - -/// Local implementation of [EmotionRepository]. -/// Depends on [sharedPreferencesProvider] for storing data. -/// -/// Copied from [EmotionRepositoryLocal]. -@ProviderFor(EmotionRepositoryLocal) -final emotionRepositoryLocalProvider = - AsyncNotifierProvider.internal( - EmotionRepositoryLocal.new, - name: r'emotionRepositoryLocalProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$emotionRepositoryLocalHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$EmotionRepositoryLocal = AsyncNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/emotions/presentation/dashboard.dart b/lib/src/features/emotions/presentation/dashboard.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/features/emotions/presentation/emotion_picker.dart b/lib/src/features/emotions/presentation/emotion_picker.dart new file mode 100644 index 0000000..e6d6af4 --- /dev/null +++ b/lib/src/features/emotions/presentation/emotion_picker.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion.dart'; +import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; + +class EmotionPickerScreen extends ConsumerWidget { + const EmotionPickerScreen({super.key}); + + static const path = "/emotion/pick"; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final todaysEmotionLog = ref.watch(emotionTodayServiceProvider); + return Scaffold( + appBar: AppBar( + title: const Text('Pick an Emotion'), + ), + body: Center( + child: todaysEmotionLog.isLoading + ? const CircularProgressIndicator.adaptive() + : Column( + children: [ + if (todaysEmotionLog.value != null) + Text('You are feeling ${todaysEmotionLog.value!.emotion1} today.'), + Text('Pick an emotion.'), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + ref.read(emotionTodayServiceProvider.notifier).updateEmotionLogToday( + todaysEmotionLog.value?.id, + EmotionLog(date: DateTime.now(), emotion1: Emotion.appreciated), + ); + }, + child: const Text('Happy'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/features/emotions/service/emotion_today_service.dart b/lib/src/features/emotions/service/emotion_today_service.dart new file mode 100644 index 0000000..b18626e --- /dev/null +++ b/lib/src/features/emotions/service/emotion_today_service.dart @@ -0,0 +1,31 @@ +import 'package:illemo/src/features/emotions/data/repositories/emotion_repository.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'emotion_today_service.g.dart'; + +@riverpod +class EmotionTodayService extends _$EmotionTodayService { + late final EmotionRepository _emotionRepository; + + @override + Future build() { + _emotionRepository = ref.watch(emotionRepositoryProvider); + return _emotionRepository.getEmotionLogToday(); + } + + void updateEmotionLogToday(EmotionLogID? id, EmotionLog emotionLog) async { + if (id == null) { + await _emotionRepository.addEmotionLog(emotionLog); + } else { + await _emotionRepository.updateEmotionLog(id, emotionLog); + } + state = AsyncValue.data(emotionLog); + } + + void deleteEmotionLogToday(EmotionLogID id) async { + await _emotionRepository.deleteEmotionLog(id); + state = AsyncValue.data(null); + } +} diff --git a/lib/src/features/emotions/service/emotion_today_service.g.dart b/lib/src/features/emotions/service/emotion_today_service.g.dart new file mode 100644 index 0000000..de25da3 --- /dev/null +++ b/lib/src/features/emotions/service/emotion_today_service.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'emotion_today_service.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$emotionTodayServiceHash() => + r'fd16fdca855ecca2dc1760da8288eadb7fd4a59d'; + +/// See also [EmotionTodayService]. +@ProviderFor(EmotionTodayService) +final emotionTodayServiceProvider = + AutoDisposeAsyncNotifierProvider.internal( + EmotionTodayService.new, + name: r'emotionTodayServiceProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$emotionTodayServiceHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$EmotionTodayService = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/entries/application/entries_service.dart b/lib/src/features/entries/application/entries_service.dart index e0ba9d9..5c2a444 100644 --- a/lib/src/features/entries/application/entries_service.dart +++ b/lib/src/features/entries/application/entries_service.dart @@ -20,7 +20,7 @@ class EntriesService { final JobsRepository jobsRepository; final EntriesRepository entriesRepository; - /// combine List, List into List + /// combine [List], [List] into [List] Stream> _allEntriesStream(UserID uid) => CombineLatestStream.combine2( entriesRepository.watchEntries(uid: uid), jobsRepository.watchJobs(uid: uid), diff --git a/lib/src/features/entries/application/entries_service.g.dart b/lib/src/features/entries/application/entries_service.g.dart index 378fdd6..0c0f69d 100644 --- a/lib/src/features/entries/application/entries_service.g.dart +++ b/lib/src/features/entries/application/entries_service.g.dart @@ -13,8 +13,9 @@ String _$entriesServiceHash() => r'106c29e519ac1706956f952263745337399caba9'; final entriesServiceProvider = AutoDisposeProvider.internal( entriesService, name: r'entriesServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$entriesServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$entriesServiceHash, dependencies: null, allTransitiveDependencies: null, ); @@ -22,7 +23,8 @@ final entriesServiceProvider = AutoDisposeProvider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element typedef EntriesServiceRef = AutoDisposeProviderRef; -String _$entriesTileModelStreamHash() => r'e8f3184f1b1db43eb92198669492a36d3ee03356'; +String _$entriesTileModelStreamHash() => + r'e8f3184f1b1db43eb92198669492a36d3ee03356'; /// See also [entriesTileModelStream]. @ProviderFor(entriesTileModelStream) @@ -30,14 +32,16 @@ final entriesTileModelStreamProvider = AutoDisposeStreamProvider>.internal( entriesTileModelStream, name: r'entriesTileModelStreamProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$entriesTileModelStreamHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$entriesTileModelStreamHash, dependencies: null, allTransitiveDependencies: null, ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef EntriesTileModelStreamRef = AutoDisposeStreamProviderRef>; +typedef EntriesTileModelStreamRef + = AutoDisposeStreamProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/features/entries/data/entries_repository.g.dart b/lib/src/features/entries/data/entries_repository.g.dart index 4a76565..06e92a4 100644 --- a/lib/src/features/entries/data/entries_repository.g.dart +++ b/lib/src/features/entries/data/entries_repository.g.dart @@ -10,11 +10,13 @@ String _$entriesRepositoryHash() => r'17cd56c685d800f8456e9f526108ae479eb0aec2'; /// See also [entriesRepository]. @ProviderFor(entriesRepository) -final entriesRepositoryProvider = AutoDisposeProvider.internal( +final entriesRepositoryProvider = + AutoDisposeProvider.internal( entriesRepository, name: r'entriesRepositoryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$entriesRepositoryHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$entriesRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -80,7 +82,8 @@ class JobEntriesQueryFamily extends Family> { static const Iterable? _allTransitiveDependencies = null; @override - Iterable? get allTransitiveDependencies => _allTransitiveDependencies; + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; @override String? get name => r'jobEntriesQueryProvider'; @@ -99,9 +102,12 @@ class JobEntriesQueryProvider extends AutoDisposeProvider> { from: jobEntriesQueryProvider, name: r'jobEntriesQueryProvider', debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jobEntriesQueryHash, + const bool.fromEnvironment('dart.vm.product') + ? null + : _$jobEntriesQueryHash, dependencies: JobEntriesQueryFamily._dependencies, - allTransitiveDependencies: JobEntriesQueryFamily._allTransitiveDependencies, + allTransitiveDependencies: + JobEntriesQueryFamily._allTransitiveDependencies, jobId: jobId, ); @@ -161,8 +167,8 @@ mixin JobEntriesQueryRef on AutoDisposeProviderRef> { String get jobId; } -class _JobEntriesQueryProviderElement extends AutoDisposeProviderElement> - with JobEntriesQueryRef { +class _JobEntriesQueryProviderElement + extends AutoDisposeProviderElement> with JobEntriesQueryRef { _JobEntriesQueryProviderElement(super.provider); @override diff --git a/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart b/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart index a90ee39..5cac5dd 100644 --- a/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart +++ b/lib/src/features/entries/presentation/entry_screen/entry_screen_controller.g.dart @@ -6,7 +6,8 @@ part of 'entry_screen_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$entryScreenControllerHash() => r'75638e7eac6bacd498349a143fc5fc827171674a'; +String _$entryScreenControllerHash() => + r'75638e7eac6bacd498349a143fc5fc827171674a'; /// See also [EntryScreenController]. @ProviderFor(EntryScreenController) @@ -14,8 +15,9 @@ final entryScreenControllerProvider = AutoDisposeAsyncNotifierProvider.internal( EntryScreenController.new, name: r'entryScreenControllerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$entryScreenControllerHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$entryScreenControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/jobs/data/jobs_repository.g.dart b/lib/src/features/jobs/data/jobs_repository.g.dart index 2fc7755..446d355 100644 --- a/lib/src/features/jobs/data/jobs_repository.g.dart +++ b/lib/src/features/jobs/data/jobs_repository.g.dart @@ -13,8 +13,9 @@ String _$jobsRepositoryHash() => r'38b37bbcb0ced4ca0754f549ebbe9384bc2bda31'; final jobsRepositoryProvider = Provider.internal( jobsRepository, name: r'jobsRepositoryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jobsRepositoryHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$jobsRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -29,7 +30,8 @@ String _$jobsQueryHash() => r'aeaccb50f75b9e5bc97b07443935ffd432dba51a'; final jobsQueryProvider = AutoDisposeProvider>.internal( jobsQuery, name: r'jobsQueryProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null : _$jobsQueryHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$jobsQueryHash, dependencies: null, allTransitiveDependencies: null, ); @@ -95,7 +97,8 @@ class JobStreamFamily extends Family> { static const Iterable? _allTransitiveDependencies = null; @override - Iterable? get allTransitiveDependencies => _allTransitiveDependencies; + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; @override String? get name => r'jobStreamProvider'; @@ -114,7 +117,9 @@ class JobStreamProvider extends AutoDisposeStreamProvider { from: jobStreamProvider, name: r'jobStreamProvider', debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jobStreamHash, + const bool.fromEnvironment('dart.vm.product') + ? null + : _$jobStreamHash, dependencies: JobStreamFamily._dependencies, allTransitiveDependencies: JobStreamFamily._allTransitiveDependencies, jobId: jobId, @@ -176,7 +181,8 @@ mixin JobStreamRef on AutoDisposeStreamProviderRef { String get jobId; } -class _JobStreamProviderElement extends AutoDisposeStreamProviderElement with JobStreamRef { +class _JobStreamProviderElement extends AutoDisposeStreamProviderElement + with JobStreamRef { _JobStreamProviderElement(super.provider); @override diff --git a/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart b/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart index b946650..4a7ae1f 100644 --- a/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart +++ b/lib/src/features/jobs/presentation/edit_job_screen/edit_job_screen_controller.g.dart @@ -6,7 +6,8 @@ part of 'edit_job_screen_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$editJobScreenControllerHash() => r'e2985913f443860f6aa9d1b0aa462d4e5c25bed4'; +String _$editJobScreenControllerHash() => + r'e2985913f443860f6aa9d1b0aa462d4e5c25bed4'; /// See also [EditJobScreenController]. @ProviderFor(EditJobScreenController) @@ -14,8 +15,9 @@ final editJobScreenControllerProvider = AutoDisposeAsyncNotifierProvider.internal( EditJobScreenController.new, name: r'editJobScreenControllerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$editJobScreenControllerHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$editJobScreenControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart b/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart index 266358d..09cce2b 100644 --- a/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart +++ b/lib/src/features/jobs/presentation/job_entries_screen/job_entries_list_controller.g.dart @@ -6,7 +6,8 @@ part of 'job_entries_list_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$jobsEntriesListControllerHash() => r'f9a08b66a0c962d210a09aebb711d38acb354b1e'; +String _$jobsEntriesListControllerHash() => + r'f9a08b66a0c962d210a09aebb711d38acb354b1e'; /// See also [JobsEntriesListController]. @ProviderFor(JobsEntriesListController) @@ -14,8 +15,9 @@ final jobsEntriesListControllerProvider = AutoDisposeAsyncNotifierProvider.internal( JobsEntriesListController.new, name: r'jobsEntriesListControllerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jobsEntriesListControllerHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$jobsEntriesListControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart b/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart index 8c63127..5f8db94 100644 --- a/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart +++ b/lib/src/features/jobs/presentation/jobs_screen/jobs_screen_controller.g.dart @@ -6,7 +6,8 @@ part of 'jobs_screen_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$jobsScreenControllerHash() => r'e3a40258404cf512fd12924d8f0a485f75d7d6fb'; +String _$jobsScreenControllerHash() => + r'e3a40258404cf512fd12924d8f0a485f75d7d6fb'; /// See also [JobsScreenController]. @ProviderFor(JobsScreenController) @@ -14,8 +15,9 @@ final jobsScreenControllerProvider = AutoDisposeAsyncNotifierProvider.internal( JobsScreenController.new, name: r'jobsScreenControllerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jobsScreenControllerHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$jobsScreenControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/onboarding/data/onboarding_repository.g.dart b/lib/src/features/onboarding/data/onboarding_repository.g.dart index 370bdd9..ef74384 100644 --- a/lib/src/features/onboarding/data/onboarding_repository.g.dart +++ b/lib/src/features/onboarding/data/onboarding_repository.g.dart @@ -6,15 +6,18 @@ part of 'onboarding_repository.dart'; // RiverpodGenerator // ************************************************************************** -String _$onboardingRepositoryHash() => r'445c529dd1ac7515d8be0abd6159af6958ff3c5c'; +String _$onboardingRepositoryHash() => + r'445c529dd1ac7515d8be0abd6159af6958ff3c5c'; /// See also [onboardingRepository]. @ProviderFor(onboardingRepository) -final onboardingRepositoryProvider = FutureProvider.internal( +final onboardingRepositoryProvider = + FutureProvider.internal( onboardingRepository, name: r'onboardingRepositoryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$onboardingRepositoryHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$onboardingRepositoryHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/features/onboarding/presentation/onboarding_controller.g.dart b/lib/src/features/onboarding/presentation/onboarding_controller.g.dart index 6a5525c..441fe94 100644 --- a/lib/src/features/onboarding/presentation/onboarding_controller.g.dart +++ b/lib/src/features/onboarding/presentation/onboarding_controller.g.dart @@ -6,7 +6,8 @@ part of 'onboarding_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$onboardingControllerHash() => r'232966a6326a75bb5f5166c8b76bbbb15087adaf'; +String _$onboardingControllerHash() => + r'232966a6326a75bb5f5166c8b76bbbb15087adaf'; /// See also [OnboardingController]. @ProviderFor(OnboardingController) @@ -14,8 +15,9 @@ final onboardingControllerProvider = AutoDisposeAsyncNotifierProvider.internal( OnboardingController.new, name: r'onboardingControllerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$onboardingControllerHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$onboardingControllerHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/routing/app_router.dart b/lib/src/routing/app_router.dart index 4d4a8ec..5eddb3a 100644 --- a/lib/src/routing/app_router.dart +++ b/lib/src/routing/app_router.dart @@ -1,22 +1,23 @@ import 'package:flutter/material.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:go_router/go_router.dart'; import 'package:illemo/src/features/authentication/data/firebase_auth_repository.dart'; import 'package:illemo/src/features/authentication/presentation/custom_profile_screen.dart'; import 'package:illemo/src/features/authentication/presentation/custom_sign_in_screen.dart'; -import 'package:illemo/src/features/entries/presentation/entries_screen.dart'; +import 'package:illemo/src/features/emotions/presentation/emotion_picker.dart'; import 'package:illemo/src/features/entries/domain/entry.dart'; -import 'package:illemo/src/features/jobs/domain/job.dart'; +import 'package:illemo/src/features/entries/presentation/entries_screen.dart'; import 'package:illemo/src/features/entries/presentation/entry_screen/entry_screen.dart'; -import 'package:illemo/src/features/jobs/presentation/job_entries_screen/job_entries_screen.dart'; -import 'package:go_router/go_router.dart'; +import 'package:illemo/src/features/jobs/domain/job.dart'; import 'package:illemo/src/features/jobs/presentation/edit_job_screen/edit_job_screen.dart'; +import 'package:illemo/src/features/jobs/presentation/job_entries_screen/job_entries_screen.dart'; import 'package:illemo/src/features/jobs/presentation/jobs_screen/jobs_screen.dart'; import 'package:illemo/src/features/onboarding/data/onboarding_repository.dart'; import 'package:illemo/src/features/onboarding/presentation/onboarding_screen.dart'; import 'package:illemo/src/routing/go_router_refresh_stream.dart'; import 'package:illemo/src/routing/not_found_screen.dart'; import 'package:illemo/src/routing/scaffold_with_nested_navigation.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'app_router.g.dart'; @@ -38,6 +39,7 @@ enum AppRoute { editEntry, entries, profile, + emotionPicker, } @riverpod @@ -62,11 +64,12 @@ GoRouter goRouter(Ref ref) { final isLoggedIn = authRepository.currentUser != null; if (isLoggedIn) { if (path.startsWith('/onboarding') || path.startsWith('/signIn')) { - return '/jobs'; + return EmotionPickerScreen.path; } } else { if (path.startsWith('/onboarding') || path.startsWith('/jobs') || + path.startsWith(EmotionPickerScreen.path) || path.startsWith('/entries') || path.startsWith('/account')) { return '/signIn'; @@ -100,6 +103,14 @@ GoRouter goRouter(Ref ref) { StatefulShellBranch( navigatorKey: _jobsNavigatorKey, routes: [ + GoRoute( + path: EmotionPickerScreen.path, + name: AppRoute.emotionPicker.name, + pageBuilder: (context, state) { + return const NoTransitionPage( + child: EmotionPickerScreen(), + ); + }), GoRoute( path: '/jobs', name: AppRoute.jobs.name, diff --git a/lib/src/routing/app_router.g.dart b/lib/src/routing/app_router.g.dart index c9443fd..58bea95 100644 --- a/lib/src/routing/app_router.g.dart +++ b/lib/src/routing/app_router.g.dart @@ -6,14 +6,15 @@ part of 'app_router.dart'; // RiverpodGenerator // ************************************************************************** -String _$goRouterHash() => r'bdb6fbb3c1421654e085ee95c8071b4996f3f578'; +String _$goRouterHash() => r'3196f151cdbfbdd9a72a4a1f99932812a22d0bee'; /// See also [goRouter]. @ProviderFor(goRouter) final goRouterProvider = AutoDisposeProvider.internal( goRouter, name: r'goRouterProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null : _$goRouterHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$goRouterHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/routing/app_startup.g.dart b/lib/src/routing/app_startup.g.dart index 5d868d6..4f07aae 100644 --- a/lib/src/routing/app_startup.g.dart +++ b/lib/src/routing/app_startup.g.dart @@ -13,7 +13,8 @@ String _$appStartupHash() => r'e4ee7c8520e85c205f71d32783e8c8f4809ea3a6'; final appStartupProvider = FutureProvider.internal( appStartup, name: r'appStartupProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null : _$appStartupHash, + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$appStartupHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/lib/src/utils/shared_preferences_provider.g.dart b/lib/src/utils/shared_preferences_provider.g.dart index f46bd7c..d714c59 100644 --- a/lib/src/utils/shared_preferences_provider.g.dart +++ b/lib/src/utils/shared_preferences_provider.g.dart @@ -10,11 +10,13 @@ String _$sharedPreferencesHash() => r'8ad0f57d954e8c413084bfe5492c81b218c3e4a0'; /// See also [sharedPreferences]. @ProviderFor(sharedPreferences) -final sharedPreferencesProvider = FutureProvider.internal( +final sharedPreferencesProvider = + FutureProvider.internal( sharedPreferences, name: r'sharedPreferencesProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$sharedPreferencesHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$sharedPreferencesHash, dependencies: null, allTransitiveDependencies: null, ); From 9a7b5cfa8f23c032e0c4f455dec2387cbe044d05 Mon Sep 17 00:00:00 2001 From: alexdivadi Date: Sat, 22 Feb 2025 18:16:30 -0800 Subject: [PATCH 5/7] feat: emotion picker screen design --- lib/src/common_widgets/looping_listview.dart | 45 +++++ .../emotion_picker_controller.dart} | 0 .../emotions/presentation/emotion_picker.dart | 42 ----- .../presentation/screens/calendar.dart | 31 ++++ .../presentation/screens/dashboard.dart | 31 ++++ .../presentation/screens/emotion_picker.dart | 165 ++++++++++++++++++ .../service/emotion_today_service.dart | 4 + .../service/emotion_today_service.g.dart | 2 +- lib/src/routing/app_router.dart | 21 ++- lib/src/routing/app_router.g.dart | 2 +- 10 files changed, 294 insertions(+), 49 deletions(-) create mode 100644 lib/src/common_widgets/looping_listview.dart rename lib/src/features/emotions/presentation/{dashboard.dart => controllers/emotion_picker_controller.dart} (100%) delete mode 100644 lib/src/features/emotions/presentation/emotion_picker.dart create mode 100644 lib/src/features/emotions/presentation/screens/calendar.dart create mode 100644 lib/src/features/emotions/presentation/screens/dashboard.dart create mode 100644 lib/src/features/emotions/presentation/screens/emotion_picker.dart diff --git a/lib/src/common_widgets/looping_listview.dart b/lib/src/common_widgets/looping_listview.dart new file mode 100644 index 0000000..89fe9cd --- /dev/null +++ b/lib/src/common_widgets/looping_listview.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +class LoopingListView extends StatelessWidget { + const LoopingListView( + {super.key, + required this.children, + this.onSelectedItemChanged, + this.direction = Axis.horizontal, + this.controller, + this.itemExtent = 200}); + + final List children; + final void Function(int index)? onSelectedItemChanged; + final Axis direction; + final ScrollController? controller; + final double itemExtent; + + @override + Widget build(BuildContext context) { + return ListWheelScrollView.useDelegate( + controller: controller, + itemExtent: itemExtent, + diameterRatio: 3, + perspective: 0.001, + useMagnifier: true, + onSelectedItemChanged: onSelectedItemChanged, + childDelegate: ListWheelChildLoopingListDelegate(children: children), + ); + } +} + +extension LoopingListScrollController on ScrollController { + void animateToItem(int index, + {required double itemExtent, + required int itemCount, + required Duration duration, + required Curve curve}) { + final currentIndex = (offset / itemExtent).round() % itemCount; + final difference = (currentIndex - index).abs() > itemCount / 2 + ? currentIndex - (index + (currentIndex - index).sign * itemCount) + : currentIndex - index; + final target = (offset / itemExtent - difference).round() * itemExtent; + animateTo(target, duration: duration, curve: curve); + } +} diff --git a/lib/src/features/emotions/presentation/dashboard.dart b/lib/src/features/emotions/presentation/controllers/emotion_picker_controller.dart similarity index 100% rename from lib/src/features/emotions/presentation/dashboard.dart rename to lib/src/features/emotions/presentation/controllers/emotion_picker_controller.dart diff --git a/lib/src/features/emotions/presentation/emotion_picker.dart b/lib/src/features/emotions/presentation/emotion_picker.dart deleted file mode 100644 index e6d6af4..0000000 --- a/lib/src/features/emotions/presentation/emotion_picker.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; -import 'package:illemo/src/features/emotions/domain/models/emotion.dart'; -import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; - -class EmotionPickerScreen extends ConsumerWidget { - const EmotionPickerScreen({super.key}); - - static const path = "/emotion/pick"; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final todaysEmotionLog = ref.watch(emotionTodayServiceProvider); - return Scaffold( - appBar: AppBar( - title: const Text('Pick an Emotion'), - ), - body: Center( - child: todaysEmotionLog.isLoading - ? const CircularProgressIndicator.adaptive() - : Column( - children: [ - if (todaysEmotionLog.value != null) - Text('You are feeling ${todaysEmotionLog.value!.emotion1} today.'), - Text('Pick an emotion.'), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () { - ref.read(emotionTodayServiceProvider.notifier).updateEmotionLogToday( - todaysEmotionLog.value?.id, - EmotionLog(date: DateTime.now(), emotion1: Emotion.appreciated), - ); - }, - child: const Text('Happy'), - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/features/emotions/presentation/screens/calendar.dart b/lib/src/features/emotions/presentation/screens/calendar.dart new file mode 100644 index 0000000..b6c4744 --- /dev/null +++ b/lib/src/features/emotions/presentation/screens/calendar.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:illemo/src/features/emotions/presentation/screens/emotion_picker.dart'; +import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; + +class CalendarScreen extends ConsumerWidget { + const CalendarScreen({super.key}); + + static const path = "/calendar"; + static const title = "Calendar"; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final todaysEmotionLog = ref.watch(emotionTodayServiceProvider); + return Scaffold( + appBar: AppBar( + title: const Text(title), + ), + body: Center( + child: todaysEmotionLog.isLoading + ? const CircularProgressIndicator.adaptive() + : ElevatedButton( + onPressed: () => context.go(EmotionPickerScreen.path), + child: todaysEmotionLog.value != null + ? Text('You are feeling ${todaysEmotionLog.value!.emotion1} today.') + : Text('Log your emotions!')), + ), + ); + } +} diff --git a/lib/src/features/emotions/presentation/screens/dashboard.dart b/lib/src/features/emotions/presentation/screens/dashboard.dart new file mode 100644 index 0000000..2cc4f53 --- /dev/null +++ b/lib/src/features/emotions/presentation/screens/dashboard.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:illemo/src/features/emotions/presentation/screens/emotion_picker.dart'; +import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; + +class DashboardScreen extends ConsumerWidget { + const DashboardScreen({super.key}); + + static const path = "/dashboard"; + static const title = "Dashboard"; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final todaysEmotionLog = ref.watch(emotionTodayServiceProvider); + return Scaffold( + appBar: AppBar( + title: const Text(title), + ), + body: Center( + child: todaysEmotionLog.isLoading + ? const CircularProgressIndicator.adaptive() + : ElevatedButton( + onPressed: () => context.push(EmotionPickerScreen.path), + child: todaysEmotionLog.value != null + ? Text('You are feeling ${todaysEmotionLog.value!.emotion1} today.') + : Text('Log your emotions!')), + ), + ); + } +} diff --git a/lib/src/features/emotions/presentation/screens/emotion_picker.dart b/lib/src/features/emotions/presentation/screens/emotion_picker.dart new file mode 100644 index 0000000..6efe2ff --- /dev/null +++ b/lib/src/features/emotions/presentation/screens/emotion_picker.dart @@ -0,0 +1,165 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:illemo/src/common_widgets/looping_listview.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; +import 'package:illemo/src/features/emotions/domain/models/category.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion.dart'; +import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; + +class EmotionPickerScreen extends ConsumerStatefulWidget { + const EmotionPickerScreen({super.key, this.todaysEmotionLog}); + + static const path = "/emotion/pick"; + static const title = "Pick an Emotion"; + + final EmotionLog? todaysEmotionLog; + + @override + ConsumerState createState() => _EmotionPickerScreenState(); +} + +class _EmotionPickerScreenState extends ConsumerState { + late ScrollController _controllerHorizontal; + final Map _verticalControllers = {}; + final List _selectedEmotions = []; + Emotion? currentEmotion; + + @override + void initState() { + super.initState(); + if (widget.todaysEmotionLog != null) { + _selectedEmotions.add(widget.todaysEmotionLog!.emotion1); + if (widget.todaysEmotionLog!.emotion2 != null) { + _selectedEmotions.add(widget.todaysEmotionLog!.emotion2!); + } + if (widget.todaysEmotionLog!.emotion3 != null) { + _selectedEmotions.add(widget.todaysEmotionLog!.emotion3!); + } + currentEmotion = widget.todaysEmotionLog!.emotion1; + } + _controllerHorizontal = ScrollController(); + for (var category in Category.values) { + _verticalControllers[category] = ScrollController(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text(EmotionPickerScreen.title), + ), + body: Stack(children: [ + _buildEmotionWheels(), + Positioned( + top: 0, + left: 0, + right: 0, + child: IgnorePointer( + child: Container( + height: 200, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Colors.black.withValues(alpha: 0.85), Colors.transparent], + ), + ), + ), + ), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: IgnorePointer( + child: Container( + height: 200, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [Colors.black.withValues(alpha: 0.85), Colors.transparent], + ), + ), + ), + ), + ), + ]), + ); + } + + Widget _buildEmotionWheels() => ListView.builder( + controller: _controllerHorizontal, + physics: const ClampingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemCount: Category.values.length, + itemBuilder: (context, categoryIndex) { + final category = Category.values[categoryIndex]; + final emotions = Emotion.values.where((emotion) => emotion.category == category).toList(); + return SizedBox( + width: 200, + child: LoopingListView( + controller: _verticalControllers[category], + direction: Axis.vertical, + children: emotions.map((emotion) { + return InkWell( + onTap: () { + log('Selected emotion: $emotion'); + setState(() { + currentEmotion = emotion; + }); + _controllerHorizontal.animateTo( + _controllerHorizontal.position.minScrollExtent + + 200 * category.index.toDouble() - + 100, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + ); + _verticalControllers[category]!.animateToItem( + emotions.indexOf(emotion), + itemExtent: 200, + itemCount: emotions.length, + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + }, + child: Container( + width: 200, + height: 200, + decoration: BoxDecoration( + color: emotion.color, + border: currentEmotion == emotion + ? Border.all(color: Colors.white, width: 3) + : null, + ), + child: Center( + child: Text( + '$emotion', + style: TextStyle( + fontSize: 24, + fontWeight: + currentEmotion == emotion ? FontWeight.bold : FontWeight.normal, + color: currentEmotion == emotion ? Colors.white : Colors.black, + ), + ), + ), + ), + ); + }).toList()), + ); + }, + ); + + @override + void dispose() { + _controllerHorizontal.dispose(); + for (var controller in _verticalControllers.values) { + controller.dispose(); + } + super.dispose(); + } +} diff --git a/lib/src/features/emotions/service/emotion_today_service.dart b/lib/src/features/emotions/service/emotion_today_service.dart index b18626e..1d3e6eb 100644 --- a/lib/src/features/emotions/service/emotion_today_service.dart +++ b/lib/src/features/emotions/service/emotion_today_service.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:illemo/src/features/emotions/data/repositories/emotion_repository.dart'; import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; @@ -21,11 +23,13 @@ class EmotionTodayService extends _$EmotionTodayService { } else { await _emotionRepository.updateEmotionLog(id, emotionLog); } + log('Updated today\'s emotions: $emotionLog'); state = AsyncValue.data(emotionLog); } void deleteEmotionLogToday(EmotionLogID id) async { await _emotionRepository.deleteEmotionLog(id); + log('Deleted today\'s emotions.'); state = AsyncValue.data(null); } } diff --git a/lib/src/features/emotions/service/emotion_today_service.g.dart b/lib/src/features/emotions/service/emotion_today_service.g.dart index de25da3..842afbd 100644 --- a/lib/src/features/emotions/service/emotion_today_service.g.dart +++ b/lib/src/features/emotions/service/emotion_today_service.g.dart @@ -7,7 +7,7 @@ part of 'emotion_today_service.dart'; // ************************************************************************** String _$emotionTodayServiceHash() => - r'fd16fdca855ecca2dc1760da8288eadb7fd4a59d'; + r'8f552f1921c9467adecb0bf35b1c908361d71673'; /// See also [EmotionTodayService]. @ProviderFor(EmotionTodayService) diff --git a/lib/src/routing/app_router.dart b/lib/src/routing/app_router.dart index 5eddb3a..33601e1 100644 --- a/lib/src/routing/app_router.dart +++ b/lib/src/routing/app_router.dart @@ -3,7 +3,8 @@ import 'package:go_router/go_router.dart'; import 'package:illemo/src/features/authentication/data/firebase_auth_repository.dart'; import 'package:illemo/src/features/authentication/presentation/custom_profile_screen.dart'; import 'package:illemo/src/features/authentication/presentation/custom_sign_in_screen.dart'; -import 'package:illemo/src/features/emotions/presentation/emotion_picker.dart'; +import 'package:illemo/src/features/emotions/presentation/screens/dashboard.dart'; +import 'package:illemo/src/features/emotions/presentation/screens/emotion_picker.dart'; import 'package:illemo/src/features/entries/domain/entry.dart'; import 'package:illemo/src/features/entries/presentation/entries_screen.dart'; import 'package:illemo/src/features/entries/presentation/entry_screen/entry_screen.dart'; @@ -40,6 +41,7 @@ enum AppRoute { entries, profile, emotionPicker, + dashboard, } @riverpod @@ -64,12 +66,13 @@ GoRouter goRouter(Ref ref) { final isLoggedIn = authRepository.currentUser != null; if (isLoggedIn) { if (path.startsWith('/onboarding') || path.startsWith('/signIn')) { - return EmotionPickerScreen.path; + return DashboardScreen.path; } } else { if (path.startsWith('/onboarding') || path.startsWith('/jobs') || path.startsWith(EmotionPickerScreen.path) || + path.startsWith(DashboardScreen.path) || path.startsWith('/entries') || path.startsWith('/account')) { return '/signIn'; @@ -93,6 +96,14 @@ GoRouter goRouter(Ref ref) { child: CustomSignInScreen(), ), ), + GoRoute( + path: EmotionPickerScreen.path, + name: AppRoute.emotionPicker.name, + pageBuilder: (context, state) { + return const NoTransitionPage( + child: EmotionPickerScreen(), + ); + }), // Stateful navigation based on: // https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/stateful_shell_route.dart StatefulShellRoute.indexedStack( @@ -104,11 +115,11 @@ GoRouter goRouter(Ref ref) { navigatorKey: _jobsNavigatorKey, routes: [ GoRoute( - path: EmotionPickerScreen.path, - name: AppRoute.emotionPicker.name, + path: DashboardScreen.path, + name: AppRoute.dashboard.name, pageBuilder: (context, state) { return const NoTransitionPage( - child: EmotionPickerScreen(), + child: DashboardScreen(), ); }), GoRoute( diff --git a/lib/src/routing/app_router.g.dart b/lib/src/routing/app_router.g.dart index 58bea95..62e37e7 100644 --- a/lib/src/routing/app_router.g.dart +++ b/lib/src/routing/app_router.g.dart @@ -6,7 +6,7 @@ part of 'app_router.dart'; // RiverpodGenerator // ************************************************************************** -String _$goRouterHash() => r'3196f151cdbfbdd9a72a4a1f99932812a22d0bee'; +String _$goRouterHash() => r'c78ff5290110bd13902c2ed16b3d6f00fe00111d'; /// See also [goRouter]. @ProviderFor(goRouter) From a4e473a82fbdf0f790f5289ef8f9e2470ea1cea0 Mon Sep 17 00:00:00 2001 From: alexdivadi Date: Fri, 28 Feb 2025 19:58:00 -0800 Subject: [PATCH 6/7] feat: full emotion logging w/ firestore --- .../data/repositories/emotion_repository.dart | 25 ++- .../repositories/emotion_repository.g.dart | 2 +- .../emotion_repository_local.dart | 6 +- .../emotions/domain/entities/emotion_log.dart | 34 ++++ .../emotion_picker_controller.dart | 0 .../presentation/screens/dashboard.dart | 3 +- .../presentation/screens/emotion_picker.dart | 154 ++++++++++++---- .../presentation/screens/emotion_upload.dart | 96 ++++++++++ .../service/emotion_today_service.dart | 20 +- .../service/emotion_today_service.g.dart | 174 +++++++++++++++++- lib/src/routing/app_router.dart | 33 +++- lib/src/routing/app_router.g.dart | 2 +- 12 files changed, 484 insertions(+), 65 deletions(-) delete mode 100644 lib/src/features/emotions/presentation/controllers/emotion_picker_controller.dart create mode 100644 lib/src/features/emotions/presentation/screens/emotion_upload.dart diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.dart b/lib/src/features/emotions/data/repositories/emotion_repository.dart index e948608..d2af1cf 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository.dart @@ -9,6 +9,7 @@ import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; import 'package:illemo/src/utils/shared_preferences_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; part 'emotion_repository.g.dart'; @@ -28,20 +29,26 @@ class EmotionRepository { /// Adds a new emotion log to Firestore. /// /// Converts the [EmotionLog] entity to a map using [EmotionLogModel] before adding it. - Future addEmotionLog(EmotionLog emotionLog) async { - await _firestore - .collection(emotionsPath(userID)) - .add(EmotionLogModel.fromEntity(emotionLog).toMap()); + Future addEmotionLog(EmotionLog emotionLog) async { + final EmotionLogModel emotionLogModel = EmotionLogModel.fromEntity(emotionLog); + final docRef = _firestore.collection(emotionsPath(userID)).doc(emotionLogModel.id); + await docRef.set(emotionLogModel.toMap()); + return emotionLogModel.id; } /// Updates an existing emotion log in Firestore. /// /// Converts the [EmotionLog] entity to a map using [EmotionLogModel] before updating it. Future updateEmotionLog(EmotionLogID id, EmotionLog emotionLog) async { - await _firestore - .collection(emotionsPath(userID)) - .doc(id) - .update(EmotionLogModel.fromEntity(emotionLog, id: id).toMap()); + final docRef = _firestore.collection(emotionsPath(userID)).doc(id); + final emotionLogModel = EmotionLogModel.fromEntity(emotionLog, id: id); + final doc = await docRef.get(); + if (doc.exists) { + await docRef.update(emotionLogModel.toMap()); + } else { + await docRef.set(emotionLogModel.toMap()); + } + log("Updated emotion log with ID: $id"); } /// Deletes an emotion log from Firestore by its document ID. @@ -114,7 +121,7 @@ EmotionRepository emotionRepository(Ref ref) { if (currentUser.isAnonymous) { log('Using local storage for anonymous user'); - final prefs = ref.watch(sharedPreferencesProvider).requireValue; + final SharedPreferencesWithCache prefs = ref.watch(sharedPreferencesProvider).requireValue; return EmotionRepositoryLocal(userID: currentUser.uid, prefs: prefs); } log('Using firestore for authenticated user'); diff --git a/lib/src/features/emotions/data/repositories/emotion_repository.g.dart b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart index 7bc55a5..65a3096 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository.g.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository.g.dart @@ -6,7 +6,7 @@ part of 'emotion_repository.dart'; // RiverpodGenerator // ************************************************************************** -String _$emotionRepositoryHash() => r'b227856391c76764a5ab645787075557b65cb5db'; +String _$emotionRepositoryHash() => r'4c707ff8ed33361c7bea9e1b837b1fbdbf10071e'; /// Provider for [EmotionRepository]. /// Requires [UserID] userID. diff --git a/lib/src/features/emotions/data/repositories/emotion_repository_local.dart b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart index e1ac90e..f94644d 100644 --- a/lib/src/features/emotions/data/repositories/emotion_repository_local.dart +++ b/lib/src/features/emotions/data/repositories/emotion_repository_local.dart @@ -28,9 +28,11 @@ class EmotionRepositoryLocal implements EmotionRepository { /// /// [emotionLog] - The emotion log to be added. @override - Future addEmotionLog(EmotionLog emotionLog) async { + Future addEmotionLog(EmotionLog emotionLog) async { final key = '${_collectionPath}_${emotionLog.date.weekday}'; - await prefs.setString(key, jsonEncode(EmotionLogModel.fromEntity(emotionLog).toMap())); + final EmotionLogModel emotionLogModel = EmotionLogModel.fromEntity(emotionLog); + await prefs.setString(key, jsonEncode(emotionLogModel.toMap())); + return emotionLogModel.id; } /// Updates an existing emotion log in the local storage. diff --git a/lib/src/features/emotions/domain/entities/emotion_log.dart b/lib/src/features/emotions/domain/entities/emotion_log.dart index ab50192..1ca116b 100644 --- a/lib/src/features/emotions/domain/entities/emotion_log.dart +++ b/lib/src/features/emotions/domain/entities/emotion_log.dart @@ -27,4 +27,38 @@ class EmotionLog extends Equatable { @override bool get stringify => true; + + /// Factory constructor to create an EmotionLog from a list of emotions. + factory EmotionLog.fromEmotions({ + required List emotions, + required DateTime date, + EmotionLogID? id, + }) { + assert(emotions.isNotEmpty && emotions.length <= 3, + 'Emotions list must contain between 1 and 3 items.'); + return EmotionLog( + emotion1: emotions[0], + emotion2: emotions.length > 1 ? emotions[1] : null, + emotion3: emotions.length > 2 ? emotions[2] : null, + date: date, + id: id, + ); + } + + /// Creates a copy of the current EmotionLog with updated values. + EmotionLog copyWith({ + Emotion? emotion1, + Emotion? emotion2, + Emotion? emotion3, + DateTime? date, + EmotionLogID? id, + }) { + return EmotionLog( + emotion1: emotion1 ?? this.emotion1, + emotion2: emotion2 ?? this.emotion2, + emotion3: emotion3 ?? this.emotion3, + date: date ?? this.date, + id: id ?? this.id, + ); + } } diff --git a/lib/src/features/emotions/presentation/controllers/emotion_picker_controller.dart b/lib/src/features/emotions/presentation/controllers/emotion_picker_controller.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/src/features/emotions/presentation/screens/dashboard.dart b/lib/src/features/emotions/presentation/screens/dashboard.dart index 2cc4f53..1735273 100644 --- a/lib/src/features/emotions/presentation/screens/dashboard.dart +++ b/lib/src/features/emotions/presentation/screens/dashboard.dart @@ -21,7 +21,8 @@ class DashboardScreen extends ConsumerWidget { child: todaysEmotionLog.isLoading ? const CircularProgressIndicator.adaptive() : ElevatedButton( - onPressed: () => context.push(EmotionPickerScreen.path), + onPressed: () => + context.push(EmotionPickerScreen.path, extra: todaysEmotionLog.value), child: todaysEmotionLog.value != null ? Text('You are feeling ${todaysEmotionLog.value!.emotion1} today.') : Text('Log your emotions!')), diff --git a/lib/src/features/emotions/presentation/screens/emotion_picker.dart b/lib/src/features/emotions/presentation/screens/emotion_picker.dart index 6efe2ff..ee6fa48 100644 --- a/lib/src/features/emotions/presentation/screens/emotion_picker.dart +++ b/lib/src/features/emotions/presentation/screens/emotion_picker.dart @@ -2,11 +2,12 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; import 'package:illemo/src/common_widgets/looping_listview.dart'; import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; import 'package:illemo/src/features/emotions/domain/models/category.dart'; import 'package:illemo/src/features/emotions/domain/models/emotion.dart'; -import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; +import 'package:illemo/src/features/emotions/presentation/screens/emotion_upload.dart'; class EmotionPickerScreen extends ConsumerStatefulWidget { const EmotionPickerScreen({super.key, this.todaysEmotionLog}); @@ -45,51 +46,134 @@ class _EmotionPickerScreenState extends ConsumerState { } } + void pushEmotion(Emotion emotion) { + if (_selectedEmotions.length < 3) { + setState(() { + _selectedEmotions.add(emotion); + currentEmotion = null; + }); + } + } + + void _removeEmotion(int index) { + setState(() { + currentEmotion = _selectedEmotions.removeAt(index); + }); + } + + void _submitEmotions() { + context.go(EmotionUpload.path, extra: { + 'emotionIDs': _selectedEmotions.map((e) => e.id).toList(), + 'id': widget.todaysEmotionLog?.id, + }); + } + @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.black, - appBar: AppBar( - title: const Text(EmotionPickerScreen.title), - ), - body: Stack(children: [ - _buildEmotionWheels(), - Positioned( - top: 0, - left: 0, - right: 0, - child: IgnorePointer( - child: Container( - height: 200, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Colors.black.withValues(alpha: 0.85), Colors.transparent], + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text(EmotionPickerScreen.title), + ), + body: Stack(children: [ + _buildEmotionWheels(), + Positioned( + top: 0, + left: 0, + right: 0, + child: IgnorePointer( + child: Container( + height: 200, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Colors.black.withValues(alpha: 0.85), Colors.transparent], + ), ), ), ), ), - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: IgnorePointer( - child: Container( - height: 200, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [Colors.black.withValues(alpha: 0.85), Colors.transparent], + Positioned( + bottom: 0, + left: 0, + right: 0, + child: IgnorePointer( + child: Container( + height: 200, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [Colors.black.withValues(alpha: 0.85), Colors.transparent], + ), ), ), ), ), - ), - ]), - ); + if (_selectedEmotions.length >= 3) + Positioned( + top: 0, + left: 0, + right: 0, + bottom: 0, + child: Center( + child: Container( + color: Colors.black.withValues(alpha: (0.5)), + ), + ), + ), + if (_selectedEmotions.length >= 3) + Center( + child: ElevatedButton( + onPressed: _submitEmotions, + child: const Text('Submit'), + ), + ), + Positioned( + top: 16, + left: 16, + child: Row( + children: [ + for (int index = 0; index < _selectedEmotions.length; index++) + Padding( + padding: const EdgeInsets.only(right: 8), + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: _selectedEmotions[index].color, + borderRadius: BorderRadius.circular(25), + ), + child: IconButton( + icon: const Icon(Icons.close), + color: Colors.white, + onPressed: () => _removeEmotion(index), + ), + ), + ), + ], + ), + ), + ]), + floatingActionButton: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (currentEmotion != null && _selectedEmotions.length < 3) + FloatingActionButton( + heroTag: 'addEmotion', + onPressed: () => pushEmotion(currentEmotion!), + child: const Icon(Icons.add), + ), + const SizedBox(width: 16), + if (_selectedEmotions.isNotEmpty && _selectedEmotions.length < 3) + FloatingActionButton( + heroTag: 'submitEmotions', + onPressed: _submitEmotions, + child: const Icon(Icons.arrow_forward), + ), + ], + )); } Widget _buildEmotionWheels() => ListView.builder( diff --git a/lib/src/features/emotions/presentation/screens/emotion_upload.dart b/lib/src/features/emotions/presentation/screens/emotion_upload.dart new file mode 100644 index 0000000..00c126b --- /dev/null +++ b/lib/src/features/emotions/presentation/screens/emotion_upload.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; +import 'package:illemo/src/features/emotions/presentation/screens/dashboard.dart'; +import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; + +class EmotionUpload extends ConsumerWidget { + EmotionUpload({ + super.key, + required args, + }) : emotionIds = args['emotionIDs'] as List, + id = args['id'] as EmotionLogID?; + + static const path = '/emotion/upload'; + final List emotionIds; + final EmotionLogID? id; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(uploadEmotionLogProvider(emotionIds, id)); + return Scaffold( + body: state.when( + loading: _buildLoading, + error: (e, st) => _buildError(e, st, onPressed: () => context.go(DashboardScreen.path)), + data: (_) => _buildSuccess(onPressed: () => context.go(DashboardScreen.path)), + ), + ); + } + + Widget _buildLoading() { + return const Center( + child: CircularProgressIndicator.adaptive(), + ); + } + + Widget _buildSuccess({required onPressed}) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.check_circle, + color: Colors.green, + size: 100.0, + ), + SizedBox(height: 20.0), + Text( + 'Success!', + style: TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.bold, + ), + ), + ElevatedButton( + onPressed: onPressed, + child: const Text('Go to Dashboard'), + ), + ], + ), + ); + } + + Widget _buildError(error, stackTrace, {required onPressed}) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.error, + color: Colors.red, + size: 100.0, + ), + SizedBox(height: 20.0), + Text( + 'Error', + style: TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.bold, + ), + ), + Text( + '$error', + style: TextStyle( + fontSize: 16.0, + ), + ), + ElevatedButton( + onPressed: onPressed, + child: const Text('Back to Dashboard'), + ), + ], + ), + ); + } +} diff --git a/lib/src/features/emotions/service/emotion_today_service.dart b/lib/src/features/emotions/service/emotion_today_service.dart index 1d3e6eb..46359d1 100644 --- a/lib/src/features/emotions/service/emotion_today_service.dart +++ b/lib/src/features/emotions/service/emotion_today_service.dart @@ -1,13 +1,15 @@ import 'dart:developer'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:illemo/src/features/emotions/data/repositories/emotion_repository.dart'; import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; +import 'package:illemo/src/features/emotions/domain/models/emotion.dart'; import 'package:illemo/src/features/emotions/domain/models/emotion_log_model.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'emotion_today_service.g.dart'; -@riverpod +@Riverpod(keepAlive: true) class EmotionTodayService extends _$EmotionTodayService { late final EmotionRepository _emotionRepository; @@ -19,12 +21,12 @@ class EmotionTodayService extends _$EmotionTodayService { void updateEmotionLogToday(EmotionLogID? id, EmotionLog emotionLog) async { if (id == null) { - await _emotionRepository.addEmotionLog(emotionLog); + id = await _emotionRepository.addEmotionLog(emotionLog); } else { await _emotionRepository.updateEmotionLog(id, emotionLog); } log('Updated today\'s emotions: $emotionLog'); - state = AsyncValue.data(emotionLog); + state = AsyncValue.data(emotionLog.copyWith(id: id)); } void deleteEmotionLogToday(EmotionLogID id) async { @@ -33,3 +35,15 @@ class EmotionTodayService extends _$EmotionTodayService { state = AsyncValue.data(null); } } + +@riverpod +Future uploadEmotionLog(Ref ref, List emotionIds, EmotionLogID? id) async { + final emotions = emotionIds.map((id) => Emotion.get(id)).toList(); + final EmotionLog log = EmotionLog.fromEmotions( + emotions: emotions, + date: DateTime.now(), + id: id, + ); + final EmotionTodayService emotionTodayService = ref.read(emotionTodayServiceProvider.notifier); + return emotionTodayService.updateEmotionLogToday(id, log); +} diff --git a/lib/src/features/emotions/service/emotion_today_service.g.dart b/lib/src/features/emotions/service/emotion_today_service.g.dart index 842afbd..2ed2469 100644 --- a/lib/src/features/emotions/service/emotion_today_service.g.dart +++ b/lib/src/features/emotions/service/emotion_today_service.g.dart @@ -6,13 +6,181 @@ part of 'emotion_today_service.dart'; // RiverpodGenerator // ************************************************************************** +String _$uploadEmotionLogHash() => r'8001e2456e91990abd0ac6337854442673c7e8cc'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [uploadEmotionLog]. +@ProviderFor(uploadEmotionLog) +const uploadEmotionLogProvider = UploadEmotionLogFamily(); + +/// See also [uploadEmotionLog]. +class UploadEmotionLogFamily extends Family> { + /// See also [uploadEmotionLog]. + const UploadEmotionLogFamily(); + + /// See also [uploadEmotionLog]. + UploadEmotionLogProvider call( + List emotionIds, + String? id, + ) { + return UploadEmotionLogProvider( + emotionIds, + id, + ); + } + + @override + UploadEmotionLogProvider getProviderOverride( + covariant UploadEmotionLogProvider provider, + ) { + return call( + provider.emotionIds, + provider.id, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'uploadEmotionLogProvider'; +} + +/// See also [uploadEmotionLog]. +class UploadEmotionLogProvider extends AutoDisposeFutureProvider { + /// See also [uploadEmotionLog]. + UploadEmotionLogProvider( + List emotionIds, + String? id, + ) : this._internal( + (ref) => uploadEmotionLog( + ref as UploadEmotionLogRef, + emotionIds, + id, + ), + from: uploadEmotionLogProvider, + name: r'uploadEmotionLogProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$uploadEmotionLogHash, + dependencies: UploadEmotionLogFamily._dependencies, + allTransitiveDependencies: + UploadEmotionLogFamily._allTransitiveDependencies, + emotionIds: emotionIds, + id: id, + ); + + UploadEmotionLogProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.emotionIds, + required this.id, + }) : super.internal(); + + final List emotionIds; + final String? id; + + @override + Override overrideWith( + FutureOr Function(UploadEmotionLogRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: UploadEmotionLogProvider._internal( + (ref) => create(ref as UploadEmotionLogRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + emotionIds: emotionIds, + id: id, + ), + ); + } + + @override + AutoDisposeFutureProviderElement createElement() { + return _UploadEmotionLogProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is UploadEmotionLogProvider && + other.emotionIds == emotionIds && + other.id == id; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, emotionIds.hashCode); + hash = _SystemHash.combine(hash, id.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin UploadEmotionLogRef on AutoDisposeFutureProviderRef { + /// The parameter `emotionIds` of this provider. + List get emotionIds; + + /// The parameter `id` of this provider. + String? get id; +} + +class _UploadEmotionLogProviderElement + extends AutoDisposeFutureProviderElement with UploadEmotionLogRef { + _UploadEmotionLogProviderElement(super.provider); + + @override + List get emotionIds => (origin as UploadEmotionLogProvider).emotionIds; + @override + String? get id => (origin as UploadEmotionLogProvider).id; +} + String _$emotionTodayServiceHash() => - r'8f552f1921c9467adecb0bf35b1c908361d71673'; + r'ea48e04a511cf85f186205c9e5cb7ae8f39eb1c7'; /// See also [EmotionTodayService]. @ProviderFor(EmotionTodayService) final emotionTodayServiceProvider = - AutoDisposeAsyncNotifierProvider.internal( + AsyncNotifierProvider.internal( EmotionTodayService.new, name: r'emotionTodayServiceProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,6 +190,6 @@ final emotionTodayServiceProvider = allTransitiveDependencies: null, ); -typedef _$EmotionTodayService = AutoDisposeAsyncNotifier; +typedef _$EmotionTodayService = AsyncNotifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/src/routing/app_router.dart b/lib/src/routing/app_router.dart index 33601e1..fd6c444 100644 --- a/lib/src/routing/app_router.dart +++ b/lib/src/routing/app_router.dart @@ -3,8 +3,10 @@ import 'package:go_router/go_router.dart'; import 'package:illemo/src/features/authentication/data/firebase_auth_repository.dart'; import 'package:illemo/src/features/authentication/presentation/custom_profile_screen.dart'; import 'package:illemo/src/features/authentication/presentation/custom_sign_in_screen.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; import 'package:illemo/src/features/emotions/presentation/screens/dashboard.dart'; import 'package:illemo/src/features/emotions/presentation/screens/emotion_picker.dart'; +import 'package:illemo/src/features/emotions/presentation/screens/emotion_upload.dart'; import 'package:illemo/src/features/entries/domain/entry.dart'; import 'package:illemo/src/features/entries/presentation/entries_screen.dart'; import 'package:illemo/src/features/entries/presentation/entry_screen/entry_screen.dart'; @@ -41,6 +43,7 @@ enum AppRoute { entries, profile, emotionPicker, + emotionUpload, dashboard, } @@ -99,11 +102,22 @@ GoRouter goRouter(Ref ref) { GoRoute( path: EmotionPickerScreen.path, name: AppRoute.emotionPicker.name, - pageBuilder: (context, state) { - return const NoTransitionPage( - child: EmotionPickerScreen(), + builder: (context, state) { + final todaysEmotionLog = state.extra as EmotionLog?; + return EmotionPickerScreen( + todaysEmotionLog: todaysEmotionLog, ); }), + GoRoute( + path: EmotionUpload.path, + name: AppRoute.emotionUpload.name, + builder: (context, state) { + final args = state.extra as Map; + return EmotionUpload( + args: args, + ); + }, + ), // Stateful navigation based on: // https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/stateful_shell_route.dart StatefulShellRoute.indexedStack( @@ -115,13 +129,12 @@ GoRouter goRouter(Ref ref) { navigatorKey: _jobsNavigatorKey, routes: [ GoRoute( - path: DashboardScreen.path, - name: AppRoute.dashboard.name, - pageBuilder: (context, state) { - return const NoTransitionPage( - child: DashboardScreen(), - ); - }), + path: DashboardScreen.path, + name: AppRoute.dashboard.name, + pageBuilder: (context, state) => const NoTransitionPage( + child: DashboardScreen(), + ), + ), GoRoute( path: '/jobs', name: AppRoute.jobs.name, diff --git a/lib/src/routing/app_router.g.dart b/lib/src/routing/app_router.g.dart index 62e37e7..5a4eea6 100644 --- a/lib/src/routing/app_router.g.dart +++ b/lib/src/routing/app_router.g.dart @@ -6,7 +6,7 @@ part of 'app_router.dart'; // RiverpodGenerator // ************************************************************************** -String _$goRouterHash() => r'c78ff5290110bd13902c2ed16b3d6f00fe00111d'; +String _$goRouterHash() => r'b178b74303cc98d5865d505682db470d8d4aff12'; /// See also [goRouter]. @ProviderFor(goRouter) From 24745348f7c9e3de9edc6ca45accad4257081e0c Mon Sep 17 00:00:00 2001 From: alexdivadi Date: Sat, 1 Mar 2025 06:38:28 -0800 Subject: [PATCH 7/7] refactor: clean up ui a bit --- .../emotions/domain/entities/emotion_log.dart | 4 ++ .../presentation/screens/dashboard.dart | 68 +++++++++++++++++-- .../presentation/screens/emotion_picker.dart | 56 ++++++++------- .../presentation/screens/emotion_upload.dart | 28 +++++--- 4 files changed, 117 insertions(+), 39 deletions(-) diff --git a/lib/src/features/emotions/domain/entities/emotion_log.dart b/lib/src/features/emotions/domain/entities/emotion_log.dart index 1ca116b..ee0e6c8 100644 --- a/lib/src/features/emotions/domain/entities/emotion_log.dart +++ b/lib/src/features/emotions/domain/entities/emotion_log.dart @@ -22,6 +22,10 @@ class EmotionLog extends Equatable { /// Optional parameter for tracking doc id in firestore. final EmotionLogID? id; + /// Returns a list of emotions in the log. + List get emotions => + [emotion1, if (emotion2 != null) emotion2!, if (emotion3 != null) emotion3!]; + @override List get props => [emotion1, emotion2, emotion3, date]; diff --git a/lib/src/features/emotions/presentation/screens/dashboard.dart b/lib/src/features/emotions/presentation/screens/dashboard.dart index 1735273..525c548 100644 --- a/lib/src/features/emotions/presentation/screens/dashboard.dart +++ b/lib/src/features/emotions/presentation/screens/dashboard.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:illemo/src/features/emotions/domain/entities/emotion_log.dart'; import 'package:illemo/src/features/emotions/presentation/screens/emotion_picker.dart'; import 'package:illemo/src/features/emotions/service/emotion_today_service.dart'; @@ -20,13 +21,68 @@ class DashboardScreen extends ConsumerWidget { body: Center( child: todaysEmotionLog.isLoading ? const CircularProgressIndicator.adaptive() - : ElevatedButton( - onPressed: () => - context.push(EmotionPickerScreen.path, extra: todaysEmotionLog.value), - child: todaysEmotionLog.value != null - ? Text('You are feeling ${todaysEmotionLog.value!.emotion1} today.') - : Text('Log your emotions!')), + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Today\'s Emotions', style: Theme.of(context).textTheme.headlineMedium), + const SizedBox(height: 16.0), + todaysEmotionLog.hasError + ? Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + "Something went wrong. Check your connection or try restarting the app.", + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(color: Colors.red)), + ) + : InkWell( + onTap: () => + context.push(EmotionPickerScreen.path, extra: todaysEmotionLog.value), + child: Container( + height: 300, + width: 300, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(16.0), + ), + clipBehavior: Clip.hardEdge, + child: todaysEmotionLog.value != null + ? _buildEmotionLog(todaysEmotionLog.value!) + : Container( + height: 300, + width: double.infinity, + padding: const EdgeInsets.all(16.0), + child: Text('Log your emotions!')), + ), + ), + ], + ), ), ); } + + Widget _buildEmotionLog(EmotionLog emotionLog) { + return Column( + children: List.generate(3, (i) { + if (emotionLog.emotions.length > i) { + return Container( + height: 100, + width: double.infinity, + padding: const EdgeInsets.all(16.0), + color: emotionLog.emotions[i].color, + child: Center( + child: Text('${emotionLog.emotions[i]}', style: const TextStyle(fontSize: 24))), + ); + } else { + return Container( + height: 100, + width: double.infinity, + padding: const EdgeInsets.all(16.0), + color: Colors.grey[300], + ); + } + }), + ); + } } diff --git a/lib/src/features/emotions/presentation/screens/emotion_picker.dart b/lib/src/features/emotions/presentation/screens/emotion_picker.dart index ee6fa48..7bb744c 100644 --- a/lib/src/features/emotions/presentation/screens/emotion_picker.dart +++ b/lib/src/features/emotions/presentation/screens/emotion_picker.dart @@ -127,7 +127,11 @@ class _EmotionPickerScreenState extends ConsumerState { Center( child: ElevatedButton( onPressed: _submitEmotions, - child: const Text('Submit'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.greenAccent, + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + ), + child: const Text('Submit :)', style: TextStyle(fontSize: 24, color: Colors.black)), ), ), Positioned( @@ -162,6 +166,7 @@ class _EmotionPickerScreenState extends ConsumerState { if (currentEmotion != null && _selectedEmotions.length < 3) FloatingActionButton( heroTag: 'addEmotion', + backgroundColor: Colors.greenAccent, onPressed: () => pushEmotion(currentEmotion!), child: const Icon(Icons.add), ), @@ -169,6 +174,7 @@ class _EmotionPickerScreenState extends ConsumerState { if (_selectedEmotions.isNotEmpty && _selectedEmotions.length < 3) FloatingActionButton( heroTag: 'submitEmotions', + backgroundColor: Colors.grey, onPressed: _submitEmotions, child: const Icon(Icons.arrow_forward), ), @@ -191,31 +197,33 @@ class _EmotionPickerScreenState extends ConsumerState { direction: Axis.vertical, children: emotions.map((emotion) { return InkWell( - onTap: () { - log('Selected emotion: $emotion'); - setState(() { - currentEmotion = emotion; - }); - _controllerHorizontal.animateTo( - _controllerHorizontal.position.minScrollExtent + - 200 * category.index.toDouble() - - 100, - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - ); - _verticalControllers[category]!.animateToItem( - emotions.indexOf(emotion), - itemExtent: 200, - itemCount: emotions.length, - duration: const Duration(milliseconds: 500), - curve: Curves.easeInOut, - ); - }, + onTap: _selectedEmotions.contains(emotion) + ? null + : () { + log('Selected emotion: $emotion'); + setState(() { + currentEmotion = emotion; + }); + _controllerHorizontal.animateTo( + _controllerHorizontal.position.minScrollExtent + + 200 * category.index.toDouble() - + 100, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + ); + _verticalControllers[category]!.animateToItem( + emotions.indexOf(emotion), + itemExtent: 200, + itemCount: emotions.length, + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + }, child: Container( width: 200, height: 200, decoration: BoxDecoration( - color: emotion.color, + color: _selectedEmotions.contains(emotion) ? Colors.grey : emotion.color, border: currentEmotion == emotion ? Border.all(color: Colors.white, width: 3) : null, @@ -227,7 +235,9 @@ class _EmotionPickerScreenState extends ConsumerState { fontSize: 24, fontWeight: currentEmotion == emotion ? FontWeight.bold : FontWeight.normal, - color: currentEmotion == emotion ? Colors.white : Colors.black, + color: currentEmotion == emotion || _selectedEmotions.contains(emotion) + ? Colors.white + : Colors.black, ), ), ), diff --git a/lib/src/features/emotions/presentation/screens/emotion_upload.dart b/lib/src/features/emotions/presentation/screens/emotion_upload.dart index 00c126b..cce679c 100644 --- a/lib/src/features/emotions/presentation/screens/emotion_upload.dart +++ b/lib/src/features/emotions/presentation/screens/emotion_upload.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; @@ -39,19 +41,20 @@ class EmotionUpload extends ConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( + const Icon( Icons.check_circle, color: Colors.green, size: 100.0, ), - SizedBox(height: 20.0), - Text( + const SizedBox(height: 20.0), + const Text( 'Success!', style: TextStyle( fontSize: 24.0, fontWeight: FontWeight.bold, ), ), + const SizedBox(height: 20.0), ElevatedButton( onPressed: onPressed, child: const Text('Go to Dashboard'), @@ -62,29 +65,34 @@ class EmotionUpload extends ConsumerWidget { } Widget _buildError(error, stackTrace, {required onPressed}) { + log('$error', error: error, stackTrace: stackTrace); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( + const Icon( Icons.error, color: Colors.red, size: 100.0, ), - SizedBox(height: 20.0), - Text( + const SizedBox(height: 20.0), + const Text( 'Error', style: TextStyle( fontSize: 24.0, fontWeight: FontWeight.bold, ), ), - Text( - '$error', - style: TextStyle( - fontSize: 16.0, + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + 'Something went wrong.', + style: TextStyle( + fontSize: 16.0, + ), ), ), + const SizedBox(height: 20.0), ElevatedButton( onPressed: onPressed, child: const Text('Back to Dashboard'),