From 5b118a3da51c5214671866508ca5cd83d57650ff Mon Sep 17 00:00:00 2001 From: matatkos Date: Fri, 9 Aug 2024 14:44:13 +0200 Subject: [PATCH] Last updates --- README.md | 13 +- android/build.gradle | 2 +- lib/app/app_constants.dart | 1 + lib/app/generated/assets.gen.dart | 28 +- lib/app/generated/fonts.gen.dart | 2 +- lib/app/l10n/arb/app_cs.arb | 27 +- lib/app/l10n/arb/app_de.arb | 27 +- lib/app/l10n/arb/app_en.arb | 27 +- lib/app/l10n/arb/app_es.arb | 27 +- lib/app/l10n/arb/app_fr.arb | 27 +- lib/app/l10n/arb/app_it.arb | 27 +- lib/app/l10n/arb/app_ja.arb | 27 +- lib/app/l10n/arb/app_nb.arb | 27 +- lib/app/l10n/arb/app_pl.arb | 27 +- lib/app/l10n/arb/app_pt.arb | 27 +- lib/app/l10n/arb/app_pt_PT.arb | 27 +- lib/app/l10n/arb/app_ru.arb | 27 +- lib/app/l10n/arb/app_sk.arb | 27 +- lib/app/l10n/arb/app_uk.arb | 27 +- lib/app/router/routes.g.dart | 146 +- lib/app/theme/colors.dart | 7 + lib/app/theme/dark_theme.dart | 23 +- lib/app/theme/theme.dart | 5 +- .../breathing/breathing_game_screen.dart | 4 +- lib/helpers/color_helpers.dart | 14 +- lib/main.dart | 16 +- lib/providers/mood_chart_filter_provider.dart | 30 +- lib/providers/mood_entry_provider.dart | 18 - .../mood_heatmap_filter_provider.dart | 123 ++ lib/providers/mood_state_provider.dart | 68 +- .../contacts/chat_contacts_screen.dart | 1 + .../contacts/crisis_message_screen.dart | 6 + .../eating_disorder_contacts_screen.dart | 4 +- .../contacts/region_contacts_screen.dart | 15 +- .../eating_disorder_distractions_screen.dart | 16 +- .../eating_disorder_samples_screen.dart | 16 +- .../eating_disorder_screen.dart | 17 +- .../eating_disorder_tasks_screen.dart | 19 +- .../eating_disorder_tips_screen.dart | 37 +- .../eating_disorder/meal_plan_screen.dart | 7 +- .../tips/eating_disorder_tips_fail.dart | 4 + .../tips/eating_disorder_tips_figure.dart | 3 + .../tips/eating_disorder_tips_general.dart | 4 + .../tips/eating_disorder_tips_overeat.dart | 5 +- .../tips/eating_disorder_tips_remorse.dart | 4 + .../tips/eating_disorder_tips_vomit.dart | 5 +- .../diary/my_records_diary_add_screen.dart | 1 + .../diary/my_records_diary_detail_screen.dart | 10 +- .../my_records_detail_journal_screen.dart | 10 +- .../mood/mood_entry_detail_screen.dart | 93 +- .../my_records/mood/mood_picker_screen.dart | 282 ++-- .../my_records/mood/mood_records_screen.dart | 483 ++++-- .../my_records/mood/mood_track_screen.dart | 30 +- .../my_records/mood/search_mood_entry.dart | 281 +++- .../home/my_records/my_records_screen.dart | 7 +- .../home/self_harm/self_harm_screen.dart | 17 +- .../self_harm/self_harm_timer_screen.dart | 82 +- .../home/self_harm/self_harm_tips_screen.dart | 7 +- .../suicidal_thoughts_screen.dart | 13 +- lib/screens/main/main_screen.dart | 10 +- lib/screens/main/settings_screen.dart | 77 +- lib/services/db/database_service.dart | 1 + .../db/my_records/mood_track_dao.dart | 136 +- .../db/my_records/mood_track_model.dart | 17 + .../my_records_sleep_track_dao.dart | 2 + lib/widgets/contacts/chat_contact_tile.dart | 16 +- lib/widgets/contacts/my_contact_tile.dart | 20 +- lib/widgets/contacts/phone_contact_tile.dart | 26 +- .../contacts/region_item_contacts_list.dart | 10 +- lib/widgets/diary/diary_edit_content.dart | 7 +- lib/widgets/diary/diary_tile.dart | 3 +- lib/widgets/heatmap/data/heatmap_color.dart | 6 + .../heatmap/data/heatmap_color_mode.dart | 4 + lib/widgets/heatmap/heatmap.dart | 163 ++ lib/widgets/heatmap/heatmap_calendar.dart | 248 +++ lib/widgets/heatmap/util/datasets_util.dart | 41 + lib/widgets/heatmap/util/date_util.dart | 130 ++ lib/widgets/heatmap/util/widget_util.dart | 18 + .../heatmap/widget/heatmap_calendar_page.dart | 114 ++ .../heatmap/widget/heatmap_calendar_row.dart | 167 +++ .../heatmap/widget/heatmap_color_tip.dart | 130 ++ .../heatmap/widget/heatmap_column.dart | 132 ++ .../heatmap/widget/heatmap_container.dart | 69 + .../heatmap/widget/heatmap_month2.dart | 37 + .../heatmap/widget/heatmap_month_text.dart | 92 ++ lib/widgets/heatmap/widget/heatmap_page.dart | 212 +++ .../heatmap/widget/heatmap_week_text.dart | 47 + lib/widgets/home_tile.dart | 12 +- .../checklist_form_content.dart | 6 +- .../plan_form_content.dart | 4 + lib/widgets/mood/chosen_emotions.dart | 14 +- lib/widgets/mood/mood_heatmap.dart | 71 + lib/widgets/mood/mood_picker.dart | 36 +- lib/widgets/nepanikar_button.dart | 40 +- lib/widgets/nepanikar_dialog.dart | 7 +- lib/widgets/nepanikar_dropdown.dart | 1 - .../my_records/my_records_mood_track_dto.dart | 48 +- pubspec.lock | 1334 ----------------- 98 files changed, 3764 insertions(+), 2101 deletions(-) delete mode 100644 lib/providers/mood_entry_provider.dart create mode 100644 lib/providers/mood_heatmap_filter_provider.dart create mode 100644 lib/widgets/heatmap/data/heatmap_color.dart create mode 100644 lib/widgets/heatmap/data/heatmap_color_mode.dart create mode 100644 lib/widgets/heatmap/heatmap.dart create mode 100644 lib/widgets/heatmap/heatmap_calendar.dart create mode 100644 lib/widgets/heatmap/util/datasets_util.dart create mode 100644 lib/widgets/heatmap/util/date_util.dart create mode 100644 lib/widgets/heatmap/util/widget_util.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_calendar_page.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_calendar_row.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_color_tip.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_column.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_container.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_month2.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_month_text.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_page.dart create mode 100644 lib/widgets/heatmap/widget/heatmap_week_text.dart create mode 100644 lib/widgets/mood/mood_heatmap.dart delete mode 100644 pubspec.lock diff --git a/README.md b/README.md index 9dc4106c..716c233c 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ First aid for psychological issues - Official mobile app for [nepanikar.eu](https://nepanikar.eu/) -Original Qt version: https://github.com/ichlubna/mind - ## Project specs & conventions - targeted platforms: iOS, Android @@ -13,10 +11,11 @@ Original Qt version: https://github.com/ichlubna/mind - routing: [GoRouter](https://pub.dev/packages/go_router) - data persistence: [sembast](https://pub.dev/packages/sembast) (NoSQL) -We are committing generated files (.g.dart, .freezed.dart) to VCS to save time in Continuous Integration. - ## Project documentation +###Running application +Run command `flutter run` from root folder of application. + ### Adding a new asset [flutter_gen](https://pub.dev/packages/flutter_gen) package let's us quickly generate paths for images/icons. @@ -53,13 +52,9 @@ Make there the necessary changes then simply run the script located in `./bin/ne to regenerate the JSON file. ### Commands - +- `flutter run` - run application - `flutter packages pub run build_runner build -d` - regenerate go_route/freezed/assets files - `flutter pub run build_runner watch -d` - watch and generate automatically changes when files are changed - `flutter gen-l10n` - recompile .arb language files - `flutter pub run flutter_native_splash:create` - regenerate splash screen -### Credits -We thank these tools and services for making better developer experience: -- [Codemagic](https://codemagic.io/) - CI/CD for mobile apps (automatic builds, tests & publishing to stores) -- [Localazy](https://localazy.com/) - localization management \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 80502e0c..b809781c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.20' repositories { google() mavenCentral() diff --git a/lib/app/app_constants.dart b/lib/app/app_constants.dart index 9f3e0c24..e09ac0a9 100644 --- a/lib/app/app_constants.dart +++ b/lib/app/app_constants.dart @@ -16,4 +16,5 @@ class AppConstants { static const nepanikarWeb = 'https://nepanikar.eu/'; static const nepanikarInstagram = 'https://www.instagram.com/aplikace_nepanikar'; static const nepanikarFacebook = 'https://www.facebook.com/aplikacenepanikar/'; + static const nepanikarDonate = 'https://www.darujme.cz/darovat/1203622?referrer=&frequency=once&amount=300&widget=1202392'; } diff --git a/lib/app/generated/assets.gen.dart b/lib/app/generated/assets.gen.dart index 59eedbf7..ae4d8ced 100644 --- a/lib/app/generated/assets.gen.dart +++ b/lib/app/generated/assets.gen.dart @@ -5,7 +5,7 @@ // coverage:ignore-file // ignore_for_file: type=lint -// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal +// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -700,7 +700,16 @@ class AssetGenImage { ); } - ImageProvider provider() => AssetImage(_assetName); + ImageProvider provider({ + AssetBundle? bundle, + String? package, + }) { + return AssetImage( + _assetName, + bundle: bundle, + package: package, + ); + } String get path => _assetName; @@ -723,13 +732,14 @@ class SvgGenImage { AlignmentGeometry alignment = Alignment.center, bool allowDrawingOutsideViewBox = false, WidgetBuilder? placeholderBuilder, - Color? color, - BlendMode colorBlendMode = BlendMode.srcIn, String? semanticsLabel, bool excludeFromSemantics = false, + SvgTheme theme = const SvgTheme(), + ColorFilter? colorFilter, Clip clipBehavior = Clip.hardEdge, - bool cacheColorFilter = false, - SvgTheme? theme, + @deprecated Color? color, + @deprecated BlendMode colorBlendMode = BlendMode.srcIn, + @deprecated bool cacheColorFilter = false, }) { return SvgPicture.asset( _assetName, @@ -743,13 +753,13 @@ class SvgGenImage { alignment: alignment, allowDrawingOutsideViewBox: allowDrawingOutsideViewBox, placeholderBuilder: placeholderBuilder, - color: color, - colorBlendMode: colorBlendMode, semanticsLabel: semanticsLabel, excludeFromSemantics: excludeFromSemantics, + theme: theme, + color: color, + colorBlendMode: colorBlendMode, clipBehavior: clipBehavior, cacheColorFilter: cacheColorFilter, - theme: theme, ); } diff --git a/lib/app/generated/fonts.gen.dart b/lib/app/generated/fonts.gen.dart index b0695b15..b33be75e 100644 --- a/lib/app/generated/fonts.gen.dart +++ b/lib/app/generated/fonts.gen.dart @@ -5,7 +5,7 @@ // coverage:ignore-file // ignore_for_file: type=lint -// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal +// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use class FontFamily { FontFamily._(); diff --git a/lib/app/l10n/arb/app_cs.arb b/lib/app/l10n/arb/app_cs.arb index adf5f008..5752a4cf 100644 --- a/lib/app/l10n/arb/app_cs.arb +++ b/lib/app/l10n/arb/app_cs.arb @@ -190,6 +190,7 @@ "math_game_answer_button": "Jaký je výsledek?", "message_text": "Text zprávy", "minutes": "Minuty", + "month": "Měsíc", "months": "Měsíce", "mood": "Nálada", "mood_bad_sad": "Smutně", @@ -303,5 +304,29 @@ "title_entry_hint_text": "Pojmenuj zápis", "title_entry_label": "Název zápisu", "universities": "Vysokoškolské poradny", - "university_contacts_description": "Vysokoškolské poradny zpravidla vyžadují předchozí objednání." + "university_contacts_description": "Vysokoškolské poradny zpravidla vyžadují předchozí objednání.", + "year": "Rok", + "emotions": "Emoce", + "select_your_emotions": "Vyberte své emoce", + "select_up_to_9_emotions": "Můžete vybrat maximálně 9 emocí!", + "summary_of_your_moment": "Shrnutí vašeho okamžiku", + "describe_what_happened": "Popiš, co se stalo...", + "add_a_new_emotion": "Přidat novou emoci", + "type_a_new_emotion": "Zadejte novou emoci", + "emotion_too_long": "Emoce je příliš dlouhá (max. 12 znaků)", + "mood_chart": "Graf nálad", + "mood_heatmap": "Mapa nálad", + "mood_statistics": "Statistiky nálad", + "mood_entries": "Záznamy nálad", + "search": "Hledat", + "search_by_summary": "Hledat podle souhrnu", + "enter_part_of_summary": "Zadejte část souhrnu vašeho požadovaného souhrnu", + "search_by_emotions": "Hledat podle emocí", + "no_description_provided": "Žádný popis není k dispozici", + "no_mood_entries": "Pro tyto podmínky nejsou k dispozici žádné záznamy nálady.", + "dark_mode_on": "Tmavý režim je ZAPNUT", + "dark_mode_off": "Tmavý režim je VYPNUT", + "description": "Popis", + "confirm_delete": "Jste si jisti, že chcete tento záznam nálady smazat?", + "enter_summary": "Zadejte krátké shrnutí" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_de.arb b/lib/app/l10n/arb/app_de.arb index e3c3e850..bb3febb4 100644 --- a/lib/app/l10n/arb/app_de.arb +++ b/lib/app/l10n/arb/app_de.arb @@ -185,6 +185,7 @@ "main_partners": "Wichtigste Partner", "make_call": "Rufen Sie an.", "math": "Rechnen­", + "month": "Monat", "math_annouce_correct_answer": "Correct answer", "math_annouce_incorrect_answer": "Incorrect answer", "math_game_answer_button": "Was ist das Ergebnis?", @@ -303,5 +304,29 @@ "title_entry_hint_text": "Name des Eintrags", "title_entry_label": "Titel des Eintrags", "universities": "Universitäten", - "university_contacts_description": "Die Beratungsstellen der Hochschulen verlangen in der Regel einen Termin." + "university_contacts_description": "Die Beratungsstellen der Hochschulen verlangen in der Regel einen Termin.", + "year": "Jahr", + "emotions": "Emotionen", + "select_your_emotions": "Wählen Sie Ihre Emotionen aus", + "select_up_to_9_emotions": "Sie können nur bis zu 9 Emotionen auswählen!", + "summary_of_your_moment": "Zusammenfassung deines Moments", + "describe_what_happened": "Beschreibe, was passiert ist...", + "add_a_new_emotion": "Eine neue Emotion hinzufügen", + "type_a_new_emotion": "Geben Sie eine neue Emotion ein", + "emotion_too_long": "Emotion ist zu lang (max. 12 Zeichen)", + "mood_chart": "Stimmungsdiagramm", + "mood_heatmap": "Stimmungswärmebild", + "mood_statistics": "Stimmungsstatistik", + "mood_entries": "Stimmungseinträge", + "search": "Suche", + "search_by_summary": "Suche nach Zusammenfassung", + "enter_part_of_summary": "Geben Sie einen Teil der Zusammenfassung Ihrer gewünschten Zusammenfassung ein", + "search_by_emotions": "Suche nach Emotionen", + "no_description_provided": "Keine Beschreibung vorhanden", + "no_mood_entries": "Für diese Bedingungen sind keine Stimmungseinträge vorhanden.", + "dark_mode_on": "Dunkler Modus ist AKTIVIERT", + "dark_mode_off": "Dunkler Modus ist DEAKTIVIERT", + "description": "Beschreibung", + "confirm_delete": "Sind Sie sicher, dass Sie diesen Stimmungseintrag löschen möchten?", + "enter_summary": "Geben Sie eine kurze Zusammenfassung ein" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_en.arb b/lib/app/l10n/arb/app_en.arb index d7ecd49d..14d1a1f1 100644 --- a/lib/app/l10n/arb/app_en.arb +++ b/lib/app/l10n/arb/app_en.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Name the entry", "title_entry_label": "Title of the entry", "universities": "Universities", - "university_contacts_description": "College counseling centers usually require an appointment." + "university_contacts_description": "College counseling centers usually require an appointment.", + "month": "Month", + "year": "Year", + "emotions": "Emotions", + "select_your_emotions": "Select your emotions", + "select_up_to_9_emotions": "You can select up to 9 emotions only!", + "summary_of_your_moment": "Summary of your moment", + "describe_what_happened": "Describe what happened...", + "add_a_new_emotion": "Add a New Emotion", + "type_a_new_emotion": "Type a new emotion", + "emotion_too_long": "Emotion is too long (max 12 characters)", + "mood_chart": "Mood Chart", + "mood_heatmap": "Mood Heatmap", + "mood_statistics": "Mood Statistics", + "mood_entries": "Mood Entries", + "search": "Search", + "search_by_summary": "Search by summary", + "enter_part_of_summary": "Enter a part of summary of your desired summary", + "search_by_emotions": "Search by emotions", + "no_description_provided": "No description provided", + "no_mood_entries": "There are no mood entries for these conditions.", + "dark_mode_on": "Dark Mode is ON", + "dark_mode_off": "Dark Mode is OFF", + "description": "Description", + "confirm_delete": "Are you sure you want to delete this mood entry?", + "enter_summary": "Enter a short summary" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_es.arb b/lib/app/l10n/arb/app_es.arb index 9f74dad4..d4d4e07e 100644 --- a/lib/app/l10n/arb/app_es.arb +++ b/lib/app/l10n/arb/app_es.arb @@ -206,6 +206,7 @@ "mood_tracked_success_snackbar": "El estado de ánimo se registró con éxito", "mood_welcome_title": "Hola, ¿cómo te sientes hoy?", "my_contacts": "Mis contactos", + "month": "Mes", "my_contacts_header": "Aquí puedo guardar mis propios contactos y enviarles un correo electrónico, sms o llamarlos.", "my_records": "Mis registros", "my_contacts_names_example": "Nombre", @@ -303,5 +304,29 @@ "title_entry_hint_text": "Nombre de la entrada", "title_entry_label": "Título de la entrada", "universities": "Asesoramiento universitario", - "university_contacts_description": "Los centros de asesoramiento universitario suelen requerir cita previa." + "university_contacts_description": "Los centros de asesoramiento universitario suelen requerir cita previa.", + "year": "Año", + "emotions": "Emociones", + "select_your_emotions": "Selecciona tus emociones", + "select_up_to_9_emotions": "¡Solo puedes seleccionar hasta 9 emociones!", + "summary_of_your_moment": "Resumen de tu momento", + "describe_what_happened": "Describe lo que pasó...", + "add_a_new_emotion": "Agregar una nueva emoción", + "type_a_new_emotion": "Escribe una nueva emoción", + "emotion_too_long": "La emoción es demasiado larga (máximo 12 caracteres)", + "mood_chart": "Gráfico de estado de ánimo", + "mood_heatmap": "Mapa de calor del estado de ánimo", + "mood_statistics": "Estadísticas del estado de ánimo", + "mood_entries": "Entradas de estado de ánimo", + "search": "Buscar", + "search_by_summary": "Buscar por resumen", + "enter_part_of_summary": "Ingrese una parte del resumen de su resumen deseado", + "search_by_emotions": "Buscar por emociones", + "no_description_provided": "No hay descripción disponible", + "no_mood_entries": "No hay entradas de estado de ánimo para estas condiciones.", + "dark_mode_on": "El modo oscuro está ACTIVADO", + "dark_mode_off": "El modo oscuro está DESACTIVADO", + "description": "Descripción", + "confirm_delete": "¿Estás seguro de que quieres eliminar esta entrada de estado de ánimo?", + "enter_summary": "Introduce un breve resumen" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_fr.arb b/lib/app/l10n/arb/app_fr.arb index 9752ff74..0f8fb8d8 100644 --- a/lib/app/l10n/arb/app_fr.arb +++ b/lib/app/l10n/arb/app_fr.arb @@ -190,6 +190,7 @@ "math_game_answer_button": "Quel est le résultat ?", "message_text": "Texte du message", "minutes": "Minutes", + "month": "Mois", "months": "Mois", "mood": "Mood", "mood_bad_sad": "Triste", @@ -303,5 +304,29 @@ "title_entry_hint_text": "Nommez l'entrée", "title_entry_label": "Titre de l'entrée", "universities": "Universités", - "university_contacts_description": "Les centres de conseil universitaire exigent généralement un rendez-vous." + "university_contacts_description": "Les centres de conseil universitaire exigent généralement un rendez-vous.", + "year": "Année", + "emotions": "Émotions", + "select_your_emotions": "Sélectionnez vos émotions", + "select_up_to_9_emotions": "Vous ne pouvez sélectionner que jusqu'à 9 émotions !", + "summary_of_your_moment": "Résumé de votre moment", + "describe_what_happened": "Décrivez ce qui s'est passé...", + "add_a_new_emotion": "Ajouter une nouvelle émotion", + "type_a_new_emotion": "Saisissez une nouvelle émotion", + "emotion_too_long": "L'émotion est trop longue (max. 12 caractères)", + "mood_chart": "Graphique d'humeur", + "mood_heatmap": "Carte thermique d'humeur", + "mood_statistics": "Statistiques d'humeur", + "mood_entries": "Entrées d'humeur", + "search": "Rechercher", + "search_by_summary": "Rechercher par résumé", + "enter_part_of_summary": "Entrez une partie du résumé de votre résumé souhaité", + "search_by_emotions": "Rechercher par émotions", + "no_description_provided": "Aucune description fournie", + "no_mood_entries": "Il n'y a pas d'entrées d'humeur pour ces conditions.", + "dark_mode_on": "Le mode sombre est ACTIVÉ", + "dark_mode_off": "Le mode sombre est DÉSACTIVÉ", + "description": "Description", + "confirm_delete": "Êtes-vous sûr de vouloir supprimer cette entrée de l'humeur ?", + "enter_summary": "Entrez un bref résumé" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_it.arb b/lib/app/l10n/arb/app_it.arb index 13a4c964..e4c35779 100644 --- a/lib/app/l10n/arb/app_it.arb +++ b/lib/app/l10n/arb/app_it.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Nome della voce", "title_entry_label": "Titolo della voce", "universities": "Università", - "university_contacts_description": "I centri di consulenza universitaria di solito richiedono un appuntamento." + "university_contacts_description": "I centri di consulenza universitaria di solito richiedono un appuntamento.", + "month": "Mese", + "year": "Anno", + "emotions": "Emozioni", + "select_your_emotions": "Seleziona le tue emozioni", + "select_up_to_9_emotions": "Puoi selezionare al massimo 9 emozioni!", + "summary_of_your_moment": "Sommario del tuo momento", + "describe_what_happened": "Descrivi cosa è successo...", + "add_a_new_emotion": "Aggiungi una nuova emozione", + "type_a_new_emotion": "Digita una nuova emozione", + "emotion_too_long": "L'emozione è troppo lunga (max 12 caratteri)", + "mood_chart": "Grafico dell'umore", + "mood_heatmap": "Mappa termica dell'umore", + "mood_statistics": "Statistiche dell'umore", + "mood_entries": "Voci dell'umore", + "search": "Cerca", + "search_by_summary": "Cerca per riassunto", + "enter_part_of_summary": "Inserisci una parte del riassunto del tuo riassunto desiderato", + "search_by_emotions": "Cerca per emozioni", + "no_description_provided": "Nessuna descrizione fornita", + "no_mood_entries": "Non ci sono voci di umore per queste condizioni.", + "dark_mode_on": "Modalità scura ATTIVA", + "dark_mode_off": "Modalità scura DISATTIVA", + "description": "Descrizione", + "confirm_delete": "Sei sicuro di voler eliminare questa voce di umore?", + "enter_summary": "Inserisci un breve riassunto" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_ja.arb b/lib/app/l10n/arb/app_ja.arb index 11f63082..a252b840 100644 --- a/lib/app/l10n/arb/app_ja.arb +++ b/lib/app/l10n/arb/app_ja.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Name the entry", "title_entry_label": "Title of the entry", "universities": "Universities", - "university_contacts_description": "College counseling centers usually require an appointment." + "university_contacts_description": "College counseling centers usually require an appointment.", + "month": "Month", + "year": "Year", + "emotions": "Emotions", + "select_your_emotions": "Select your emotions", + "select_up_to_9_emotions": "You can select up to 9 emotions only!", + "summary_of_your_moment": "Summary of your moment", + "describe_what_happened": "Describe what happened...", + "add_a_new_emotion": "Add a New Emotion", + "type_a_new_emotion": "Type a new emotion", + "emotion_too_long": "Emotion is too long (max 12 characters)", + "mood_chart": "Mood Chart", + "mood_heatmap": "Mood Heatmap", + "mood_statistics": "Mood Statistics", + "mood_entries": "Mood Entries", + "search": "Search", + "search_by_summary": "Search by summary", + "enter_part_of_summary": "Enter a part of summary of your desired summary", + "search_by_emotions": "Search by emotions", + "no_description_provided": "No description provided", + "no_mood_entries": "There are no mood entries for these conditions.", + "dark_mode_on": "Dark Mode is ON", + "dark_mode_off": "Dark Mode is OFF", + "description": "Description", + "confirm_delete": "Are you sure you want to delete this mood entry?", + "enter_summary": "Enter a short summary" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_nb.arb b/lib/app/l10n/arb/app_nb.arb index 3087fba4..18c99b94 100644 --- a/lib/app/l10n/arb/app_nb.arb +++ b/lib/app/l10n/arb/app_nb.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Navngi oppføringen", "title_entry_label": "Tittel på oppføringen", "universities": "Universitetsrådgivning", - "university_contacts_description": "Høgskolerådgivningssentre krever vanligvis en avtale." + "university_contacts_description": "Høgskolerådgivningssentre krever vanligvis en avtale.", + "month": "Måned", + "year": "År", + "emotions": "Følelser", + "select_your_emotions": "Velg dine følelser", + "select_up_to_9_emotions": "Du kan bare velge opptil 9 følelser!", + "summary_of_your_moment": "Oppsummering av øyeblikket ditt", + "describe_what_happened": "Beskriv hva som skjedde...", + "add_a_new_emotion": "Legg til en ny følelse", + "type_a_new_emotion": "Skriv inn en ny følelse", + "emotion_too_long": "Følelsen er for lang (maks. 12 tegn)", + "mood_chart": "Humørkart", + "mood_heatmap": "Humørvarmekart", + "mood_statistics": "Humørstatistikk", + "mood_entries": "Humørposter", + "search": "Søk", + "search_by_summary": "Søk etter sammendrag", + "enter_part_of_summary": "Skriv inn en del av sammendraget for ønsket sammendrag", + "search_by_emotions": "Søk etter følelser", + "no_description_provided": "Ingen beskrivelse tilgjengelig", + "no_mood_entries": "Det er ingen humørposter for disse forholdene.", + "dark_mode_on": "Mørk modus er PÅ", + "dark_mode_off": "Mørk modus er AV", + "description": "Beskrivelse", + "confirm_delete": "Er du sikker på at du vil slette denne stemningsoppføringen?", + "enter_summary": "Skriv inn en kort oppsummering" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_pl.arb b/lib/app/l10n/arb/app_pl.arb index f31c3316..f4d6fe47 100644 --- a/lib/app/l10n/arb/app_pl.arb +++ b/lib/app/l10n/arb/app_pl.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Nazwa wpisu", "title_entry_label": "Tytuł wpisu", "universities": "Uniwersytety", - "university_contacts_description": "Uniwersyteckie centra doradcze zazwyczaj wymagają umówienia się na spotkanie." + "university_contacts_description": "Uniwersyteckie centra doradcze zazwyczaj wymagają umówienia się na spotkanie.", + "month": "Miesiąc", + "year": "Rok", + "emotions": "Emocje", + "select_your_emotions": "Wybierz swoje emocje", + "select_up_to_9_emotions": "Możesz wybrać maksymalnie 9 emocji!", + "summary_of_your_moment": "Podsumowanie twojego chwili", + "describe_what_happened": "Opisz, co się stało...", + "add_a_new_emotion": "Dodaj nową emocję", + "type_a_new_emotion": "Wprowadź nową emocję", + "emotion_too_long": "Emocja jest za długa (maks. 12 znaków)", + "mood_chart": "Wykres nastroju", + "mood_heatmap": "Mapa cieplna nastroju", + "mood_statistics": "Statystyki nastroju", + "mood_entries": "Wpisy nastroju", + "search": "Szukaj", + "search_by_summary": "Szukaj według podsumowania", + "enter_part_of_summary": "Wprowadź część podsumowania twojego pożądanego podsumowania", + "search_by_emotions": "Szukaj według emocji", + "no_description_provided": "Brak dostępnego opisu", + "no_mood_entries": "Brak wpisów nastroju dla tych warunków.", + "dark_mode_on": "Tryb ciemny jest WŁĄCZONY", + "dark_mode_off": "Tryb ciemny jest WYŁĄCZONY", + "description": "Opis", + "confirm_delete": "Czy na pewno chcesz usunąć ten wpis nastroju?", + "enter_summary": "Wprowadź krótkie podsumowanie" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_pt.arb b/lib/app/l10n/arb/app_pt.arb index d9ca1753..22059df5 100644 --- a/lib/app/l10n/arb/app_pt.arb +++ b/lib/app/l10n/arb/app_pt.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Name the entry", "title_entry_label": "Title of the entry", "universities": "Universities", - "university_contacts_description": "College counseling centers usually require an appointment." + "university_contacts_description": "College counseling centers usually require an appointment.", + "month": "Month", + "year": "Year", + "emotions": "Emotions", + "select_your_emotions": "Select your emotions", + "select_up_to_9_emotions": "You can select up to 9 emotions only!", + "summary_of_your_moment": "Summary of your moment", + "describe_what_happened": "Describe what happened...", + "add_a_new_emotion": "Add a New Emotion", + "type_a_new_emotion": "Type a new emotion", + "emotion_too_long": "Emotion is too long (max 12 characters)", + "mood_chart": "Mood Chart", + "mood_heatmap": "Mood Heatmap", + "mood_statistics": "Mood Statistics", + "mood_entries": "Mood Entries", + "search": "Search", + "search_by_summary": "Search by summary", + "enter_part_of_summary": "Enter a part of summary of your desired summary", + "search_by_emotions": "Search by emotions", + "no_description_provided": "No description provided", + "no_mood_entries": "There are no mood entries for these conditions.", + "dark_mode_on": "Dark Mode is ON", + "dark_mode_off": "Dark Mode is OFF", + "description": "Description", + "confirm_delete": "Are you sure you want to delete this mood entry?", + "enter_summary": "Enter a short summary" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_pt_PT.arb b/lib/app/l10n/arb/app_pt_PT.arb index 6c740b49..8cd190d6 100644 --- a/lib/app/l10n/arb/app_pt_PT.arb +++ b/lib/app/l10n/arb/app_pt_PT.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Name the entry", "title_entry_label": "Title of the entry", "universities": "Universities", - "university_contacts_description": "College counseling centers usually require an appointment." + "university_contacts_description": "College counseling centers usually require an appointment.", + "month": "Month", + "year": "Year", + "emotions": "Emotions", + "select_your_emotions": "Select your emotions", + "select_up_to_9_emotions": "You can select up to 9 emotions only!", + "summary_of_your_moment": "Summary of your moment", + "describe_what_happened": "Describe what happened...", + "add_a_new_emotion": "Add a New Emotion", + "type_a_new_emotion": "Type a new emotion", + "emotion_too_long": "Emotion is too long (max 12 characters)", + "mood_chart": "Mood Chart", + "mood_heatmap": "Mood Heatmap", + "mood_statistics": "Mood Statistics", + "mood_entries": "Mood Entries", + "search": "Search", + "search_by_summary": "Search by summary", + "enter_part_of_summary": "Enter a part of summary of your desired summary", + "search_by_emotions": "Search by emotions", + "no_description_provided": "No description provided", + "no_mood_entries": "There are no mood entries for these conditions.", + "dark_mode_on": "Dark Mode is ON", + "dark_mode_off": "Dark Mode is OFF", + "description": "Description", + "confirm_delete": "Are you sure you want to delete this mood entry?", + "enter_summary": "Enter a short summary" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_ru.arb b/lib/app/l10n/arb/app_ru.arb index 651f791e..253ecf87 100644 --- a/lib/app/l10n/arb/app_ru.arb +++ b/lib/app/l10n/arb/app_ru.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Name the entry", "title_entry_label": "Title of the entry", "universities": "Психологические службы в ВУЗах", - "university_contacts_description": "College counseling centers usually require an appointment." + "university_contacts_description": "College counseling centers usually require an appointment.", + "month": "Месяц", + "year": "Год", + "emotions": "Эмоции", + "select_your_emotions": "Выберите ваши эмоции", + "select_up_to_9_emotions": "Вы можете выбрать только до 9 эмоций!", + "summary_of_your_moment": "Сводка вашего момента", + "describe_what_happened": "Опишите, что произошло...", + "add_a_new_emotion": "Добавить новую эмоцию", + "type_a_new_emotion": "Введите новую эмоцию", + "emotion_too_long": "Эмоция слишком длинная (максимум 12 символов)", + "mood_chart": "График настроения", + "mood_heatmap": "Тепловая карта настроения", + "mood_statistics": "Статистика настроения", + "mood_entries": "Записи настроения", + "search": "Поиск", + "search_by_summary": "Поиск по сводке", + "enter_part_of_summary": "Введите часть сводки вашей желаемой сводки", + "search_by_emotions": "Поиск по эмоциям", + "no_description_provided": "Описание отсутствует", + "no_mood_entries": "Для этих условий нет записей настроения.", + "dark_mode_on": "Тёмный режим ВКЛ", + "dark_mode_off": "Тёмный режим ВЫКЛ", + "description": "Описание", + "confirm_delete": "Вы уверены, что хотите удалить эту запись настроения?", + "enter_summary": "Введите короткое описание" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_sk.arb b/lib/app/l10n/arb/app_sk.arb index 4a80768a..6fbb19b0 100644 --- a/lib/app/l10n/arb/app_sk.arb +++ b/lib/app/l10n/arb/app_sk.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Pomenuj zápis", "title_entry_label": "Názov zápisu", "universities": "Univerzity", - "university_contacts_description": "Vysokoškolské poradne spravidla vyžadujú predchádzajúce objednanie." + "university_contacts_description": "Vysokoškolské poradne spravidla vyžadujú predchádzajúce objednanie.", + "month": "Mesiac", + "year": "Rok", + "emotions": "Emócie", + "select_your_emotions": "Vyberte svoje emócie", + "select_up_to_9_emotions": "Môžete vybrať maximálne 9 emócií!", + "summary_of_your_moment": "Súhrn vášho okamihu", + "describe_what_happened": "Popíš, čo sa stalo...", + "add_a_new_emotion": "Pridať novú emóciu", + "type_a_new_emotion": "Napíšte novú emóciu", + "emotion_too_long": "Emócia je príliš dlhá (max. 12 znakov)", + "mood_chart": "Graf nálady", + "mood_heatmap": "Tepelná mapa nálady", + "mood_statistics": "Štatistiky nálady", + "mood_entries": "Záznamy nálady", + "search": "Hľadať", + "search_by_summary": "Hľadať podľa súhrnu", + "enter_part_of_summary": "Zadajte časť súhrnu vášho požadovaného súhrnu", + "search_by_emotions": "Hľadať podľa emocíí", + "no_description_provided": "Žiadny popis nie je k dispozícii", + "no_mood_entries": "Pre tieto podmienky nie sú k dispozícii žiadne záznamy nálady.", + "dark_mode_on": "Tmavý režim je ZAPNUTÝ", + "dark_mode_off": "Tmavý režim je VYPNUTÝ", + "description": "Popis", + "confirm_delete": "Ste si istý, že chcete odstrániť tento záznam nálady?", + "enter_summary": "Zadajte krátke zhrnutie" } \ No newline at end of file diff --git a/lib/app/l10n/arb/app_uk.arb b/lib/app/l10n/arb/app_uk.arb index c36009fa..84923692 100644 --- a/lib/app/l10n/arb/app_uk.arb +++ b/lib/app/l10n/arb/app_uk.arb @@ -303,5 +303,30 @@ "title_entry_hint_text": "Назвіть запис", "title_entry_label": "Назва запису", "universities": "Університетські консультаційні центри", - "university_contacts_description": "Консультаційні центри коледжів зазвичай вимагають попереднього запису." + "university_contacts_description": "Консультаційні центри коледжів зазвичай вимагають попереднього запису.", + "month": "Місяць", + "year": "Рік", + "emotions": "Емоції", + "select_your_emotions": "Виберіть свої емоції", + "select_up_to_9_emotions": "Ви можете вибрати не більше 9 емоцій!", + "summary_of_your_moment": "Огляд вашого моменту", + "describe_what_happened": "Опиши, що сталося...", + "add_a_new_emotion": "Додати нову емоцію", + "type_a_new_emotion": "Введіть нову емоцію", + "emotion_too_long": "Емоція занадто довга (макс. 12 символів)", + "mood_chart": "Графік настрою", + "mood_heatmap": "Теплова карта настрою", + "mood_statistics": "Статистика настрою", + "mood_entries": "Записи настрою", + "search": "Пошук", + "search_by_summary": "Пошук за підсумком", + "enter_part_of_summary": "Введіть частину підсумку вашого бажаного підсумку", + "search_by_emotions": "Пошук за емоціями", + "no_description_provided": "Опис відсутній", + "no_mood_entries": "Для цих умов записи настрою відсутні.", + "dark_mode_on": "Темний режим УВІМКНЕНО", + "dark_mode_off": "Темний режим ВИМКНЕНО", + "description": "Опис", + "confirm_delete": "Ви впевнені, що хочете видалити цей запис настрою?", + "enter_summary": "Введіть короткий опис" } \ No newline at end of file diff --git a/lib/app/router/routes.g.dart b/lib/app/router/routes.g.dart index 2f56fb16..cd94af53 100644 --- a/lib/app/router/routes.g.dart +++ b/lib/app/router/routes.g.dart @@ -6,11 +6,11 @@ part of 'routes.dart'; // GoRouterGenerator // ************************************************************************** -List get $appRoutes => [ +List get $appRoutes => [ $mainRoute, ]; -GoRoute get $mainRoute => GoRouteData.$route( +RouteBase get $mainRoute => GoRouteData.$route( path: '/', factory: $MainRouteExtension._fromState, routes: [ @@ -307,7 +307,7 @@ extension $MainRouteExtension on MainRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -323,7 +323,7 @@ extension $NotificationSettingsRouteExtension on NotificationSettingsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -338,7 +338,7 @@ extension $SponsorsRouteExtension on SponsorsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -353,7 +353,7 @@ extension $AboutAppRouteExtension on AboutAppRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -368,7 +368,7 @@ extension $ExportRouteExtension on ExportRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -384,7 +384,7 @@ extension $LanguagesRouteExtension on LanguagesRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -399,7 +399,7 @@ extension $MathGameRouteExtension on MathGameRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -415,7 +415,7 @@ extension $BreathingExercisesRouteExtension on BreathingExercisesRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -433,7 +433,7 @@ extension $BreathingGameRouteExtension on BreathingGameRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -449,7 +449,7 @@ extension $BalanceGameRouteExtension on BalanceGameRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -465,7 +465,7 @@ extension $BalloonsGameRouteExtension on BalloonsGameRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -483,7 +483,7 @@ extension $RelaxationRouteExtension on RelaxationRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -499,7 +499,7 @@ extension $RelaxationsListRouteExtension on RelaxationsListRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -514,7 +514,7 @@ extension $ContactsRouteExtension on ContactsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -530,7 +530,7 @@ extension $CrisisMessageRouteExtension on CrisisMessageRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -546,7 +546,7 @@ extension $PhoneContactsRouteExtension on PhoneContactsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -562,7 +562,7 @@ extension $CrisisCenterContactsRouteExtension on CrisisCenterContactsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -578,7 +578,7 @@ extension $ChatContactsRouteExtension on ChatContactsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -594,7 +594,7 @@ extension $UniversityContactsRouteExtension on UniversityContactsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -610,7 +610,7 @@ extension $MyContactsRecordsRouteExtension on MyContactsRecordsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -626,7 +626,7 @@ extension $EmailCounsellingRouteExtension on EmailCounsellingRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -642,7 +642,7 @@ extension $DepressionRouteExtension on DepressionRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -658,7 +658,7 @@ extension $DepressionTipsAppRouteExtension on DepressionTipsAppRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -674,7 +674,7 @@ extension $DepressionActivityPlanRouteExtension on DepressionActivityPlanRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -691,7 +691,7 @@ extension $DepressionNiceMadeHappyRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -708,7 +708,7 @@ extension $DepressionPraiseMyAchievementsRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -724,7 +724,7 @@ extension $AnxietyAppRouteExtension on AnxietyAppRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -740,7 +740,7 @@ extension $AnxietyTipsAppRouteExtension on AnxietyTipsAppRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -755,7 +755,7 @@ extension $SelfHarmRouteExtension on SelfHarmRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -771,7 +771,7 @@ extension $SelfHarmTipsRouteExtension on SelfHarmTipsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -787,7 +787,7 @@ extension $SelfHarmHelpedRouteExtension on SelfHarmHelpedRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -803,7 +803,7 @@ extension $SelfHarmPlanRouteExtension on SelfHarmPlanRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -819,7 +819,7 @@ extension $SelfHarmTimerRouteExtension on SelfHarmTimerRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -835,7 +835,7 @@ extension $MyRecordsRouteExtension on MyRecordsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -851,7 +851,7 @@ extension $MoodTrackRouteExtension on MoodTrackRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -867,7 +867,7 @@ extension $MoodPickerRouteExtension on MoodPickerRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -883,7 +883,7 @@ extension $MoodRecordsRouteExtension on MoodRecordsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -899,7 +899,7 @@ extension $SearchMoodEntryRouteExtension on SearchMoodEntryRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -915,7 +915,7 @@ extension $MoodEntryDetailRouteExtension on MoodEntryDetailRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -931,7 +931,7 @@ extension $MyRecordsSleepTrackRouteExtension on MyRecordsSleepTrackRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -947,7 +947,7 @@ extension $MyRecordsDiaryRecordsRouteExtension on MyRecordsDiaryRecordsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -963,7 +963,7 @@ extension $MyRecordsDiaryAddRouteExtension on MyRecordsDiaryAddRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -979,7 +979,7 @@ extension $MyRecordsDiaryDetailRouteExtension on MyRecordsDiaryDetailRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -995,7 +995,7 @@ extension $MyRecordsDiaryEditRouteExtension on MyRecordsDiaryEditRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1012,7 +1012,7 @@ extension $MyRecordsJournalRecordsRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1028,7 +1028,7 @@ extension $MyRecordsJournalDetailRouteExtension on MyRecordsJournalDetailRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1045,7 +1045,7 @@ extension $MyRecordsFoodRecordsListRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1063,7 +1063,7 @@ extension $MyRecordsFoodRecordsDetailMenuListRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1081,7 +1081,7 @@ extension $MyRecordsFoodRecordsDetailMenuDetailRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1097,7 +1097,7 @@ extension $SuicidalThoughtsRouteExtension on SuicidalThoughtsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1113,7 +1113,7 @@ extension $SuicidalThoughtsPlanRouteExtension on SuicidalThoughtsPlanRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1130,7 +1130,7 @@ extension $SuicidalThoughtsReasonsNoRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1146,7 +1146,7 @@ extension $EatingDisorderRouteExtension on EatingDisorderRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1162,7 +1162,7 @@ extension $EatingDisorderTipsRouteExtension on EatingDisorderTipsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1179,7 +1179,7 @@ extension $EatingDisorderTipsFigureAppRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1196,7 +1196,7 @@ extension $EatingDisorderTipsRemorseAppRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1213,7 +1213,7 @@ extension $EatingDisorderTipsOvereatAppRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1230,7 +1230,7 @@ extension $EatingDisorderTipsVomitAppRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1247,7 +1247,7 @@ extension $EatingDisorderTipsFailAppRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1264,7 +1264,7 @@ extension $EatingDisorderTipsGeneralAppRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1280,7 +1280,7 @@ extension $EatingDisorderTasksRouteExtension on EatingDisorderTasksRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1297,7 +1297,7 @@ extension $EatingDisorderFoodCreativeRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1314,7 +1314,7 @@ extension $EatingDisorderFoodMotivationRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1331,7 +1331,7 @@ extension $EatingDisorderFoodChallengesRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1348,7 +1348,7 @@ extension $EatingDisorderLikeOnMyselfRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1365,7 +1365,7 @@ extension $EatingDisorderFoodILikeRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1382,7 +1382,7 @@ extension $EatingDisorderFoodAfraidOfRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1398,7 +1398,7 @@ extension $EatingDisorderSamplesRouteExtension on EatingDisorderSamplesRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1415,7 +1415,7 @@ extension $EatingDisorderDistractionsRouteExtension void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1430,7 +1430,7 @@ extension $MealPlanRouteExtension on MealPlanRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); @@ -1446,7 +1446,7 @@ extension $EatingDisorderContactsRouteExtension on EatingDisorderContactsRoute { void go(BuildContext context) => context.go(location); - void push(BuildContext context) => context.push(location); + Future push(BuildContext context) => context.push(location); void pushReplacement(BuildContext context) => context.pushReplacement(location); diff --git a/lib/app/theme/colors.dart b/lib/app/theme/colors.dart index bab24100..7884c18b 100644 --- a/lib/app/theme/colors.dart +++ b/lib/app/theme/colors.dart @@ -42,6 +42,13 @@ class NepanikarColors { 900: Color(0xff280446), //100% }; + static const _heatMapColors = { + 1: Color(0xffA72C2C), + 2: Color(0xffC78B31), + 3: Color(0xffDCC678), + 4: Color(0xffA6AC5A), + 5: Color(0xff49A3BF) + }; //Dark mode colors static const primaryD = Color(0xff280446); diff --git a/lib/app/theme/dark_theme.dart b/lib/app/theme/dark_theme.dart index 99e2e1f8..78866ea0 100644 --- a/lib/app/theme/dark_theme.dart +++ b/lib/app/theme/dark_theme.dart @@ -12,14 +12,14 @@ class darkTheme { }) => ThemeData( brightness: Brightness.dark, + primaryColor: NepanikarColors.primaryD, + scaffoldBackgroundColor: NepanikarColors.primaryD, pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: CupertinoPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), }, ), - primaryColor: NepanikarColors.primary, - scaffoldBackgroundColor: NepanikarColors.primaryD, fontFamily: fontFamily, colorScheme: const ColorScheme.dark().copyWith( primary: NepanikarColors.primary, @@ -46,9 +46,9 @@ class darkTheme { backgroundColor: MaterialStateProperty.resolveWith( (states) { if (states.contains(MaterialState.disabled)) { - return NepanikarColors.primarySwatch.shade500; + return NepanikarColors.primarySwatch.shade700; } - return NepanikarColors.primary; + return NepanikarColors.primarySwatch.shade700; }, ), foregroundColor: MaterialStateProperty.all(Colors.white), @@ -59,7 +59,7 @@ class darkTheme { ), outlinedButtonTheme: OutlinedButtonThemeData( style: _buttonStyle.copyWith( - backgroundColor: MaterialStateProperty.all(Colors.white), + backgroundColor: MaterialStateProperty.all(NepanikarColors.primarySwatch.shade700), foregroundColor: MaterialStateProperty.resolveWith( (states) { if (states.contains(MaterialState.disabled)) { @@ -71,14 +71,6 @@ class darkTheme { textStyle: MaterialStateProperty.all( const TextStyle(color: NepanikarColors.primary, fontWeight: FontWeight.w900), ), - side: MaterialStateProperty.resolveWith( - (states) { - if (states.contains(MaterialState.disabled)) { - return BorderSide(color: NepanikarColors.primarySwatch.shade500, width: 2.0); - } - return const BorderSide(color: NepanikarColors.primary, width: 2.0); - }, - ), ), ), unselectedWidgetColor: const Color(0xffA083B8), @@ -177,8 +169,9 @@ class darkTheme { selectedItemColor: Colors.white, unselectedItemColor: Colors.white, ), - - + textSelectionTheme: const TextSelectionThemeData( + cursorColor: NepanikarColors.white, // Set cursor color to white here + ), ); } diff --git a/lib/app/theme/theme.dart b/lib/app/theme/theme.dart index bea52dc5..66a1a41d 100644 --- a/lib/app/theme/theme.dart +++ b/lib/app/theme/theme.dart @@ -10,14 +10,15 @@ class NepanikarTheme { required String? fontFamily, }) => ThemeData( + primaryColor: NepanikarColors.primary, + scaffoldBackgroundColor: const Color(0xffFBF6FF), + brightness: Brightness.light, pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: CupertinoPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), }, ), - primaryColor: NepanikarColors.primary, - scaffoldBackgroundColor: const Color(0xffFBF6FF), fontFamily: fontFamily, colorScheme: const ColorScheme.light().copyWith( primary: NepanikarColors.primary, diff --git a/lib/games/breathing/breathing_game_screen.dart b/lib/games/breathing/breathing_game_screen.dart index 26d90317..c2bbfa06 100644 --- a/lib/games/breathing/breathing_game_screen.dart +++ b/lib/games/breathing/breathing_game_screen.dart @@ -4,6 +4,7 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/games/breathing/shape_painter.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; enum BreathingGameShape { @@ -166,9 +167,10 @@ class _BreathingGameScreenState extends State with TickerPr @override Widget build(BuildContext context) { final painterWidth = MediaQuery.of(context).size.width - 96; + final backgroundColor = customColorsBasedOnDarkMode(context, NepanikarColors.primaryD, NepanikarColors.primary); return Scaffold( appBar: AppBar(title: Text(context.l10n.breath)), - backgroundColor: NepanikarColors.primary, + backgroundColor: backgroundColor, body: SafeArea( child: Stack( children: [ diff --git a/lib/helpers/color_helpers.dart b/lib/helpers/color_helpers.dart index 7f1bf799..77755e6b 100644 --- a/lib/helpers/color_helpers.dart +++ b/lib/helpers/color_helpers.dart @@ -1,24 +1,28 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/theme/colors.dart'; -bool isDarkModeFunction(BuildContext context){ +bool isDarkmode(BuildContext context){ return Theme.of(context).brightness == Brightness.dark; } Color? svgColorBasedOnDarkMode(BuildContext context) { - return isDarkModeFunction(context) ? NepanikarColors.white : null; + return isDarkmode(context) ? NepanikarColors.white : null; +} + +Color? textColorBasedOnDarkMode(BuildContext context) { + return customColorsBasedOnDarkMode(context, NepanikarColors.white, null); } Color? pdfColorBasedOnDarkMode(BuildContext context) { - return isDarkModeFunction(context) ? NepanikarColors.primarySwatch.shade700 : null; + return isDarkmode(context) ? NepanikarColors.primarySwatch.shade700 : null; } Color? longTileColorBasedOnDarkMode(BuildContext context){ - return isDarkModeFunction(context) ? NepanikarColors.containerD : null; + return isDarkmode(context) ? NepanikarColors.containerD : null; } Color? customColorsBasedOnDarkMode(BuildContext context, Color? darkModeColor1, Color? lightModeColor2){ - return isDarkModeFunction(context) ? darkModeColor1 : lightModeColor2; + return isDarkmode(context) ? darkModeColor1 : lightModeColor2; } Color? backgroundColorsBasedOnDarkMode(BuildContext context){ diff --git a/lib/main.dart b/lib/main.dart index 27a4d85b..ce52b1f0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,7 +6,7 @@ import 'package:nepanikar/app/theme/dark_theme.dart'; import 'package:nepanikar/app/theme/theme.dart'; import 'package:nepanikar/helpers/localization_helpers.dart'; import 'package:nepanikar/providers/mood_chart_filter_provider.dart'; -import 'package:nepanikar/providers/mood_entry_provider.dart'; +import 'package:nepanikar/providers/mood_heatmap_filter_provider.dart'; import 'package:nepanikar/providers/mood_state_provider.dart'; import 'package:nepanikar/services/db/my_records/emotions_dao.dart'; import 'package:nepanikar/services/db/my_records/mood_track_dao.dart'; @@ -20,9 +20,6 @@ import 'package:provider/provider.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await setup(); - - final userSettingsDao = registry.get(); - runApp(const Nepanikar()); } @@ -35,10 +32,8 @@ class Nepanikar extends StatelessWidget { GoRouter get _goRouter => registry.get(); UserSettingsDao get _userSettingsDao => registry.get(); - EmotionsDao get _emotionsDao => registry.get(); - - + MoodTrackDao get _trackDao => registry.get(); @override Widget build(BuildContext context) { @@ -47,20 +42,19 @@ class Nepanikar extends StatelessWidget { return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => MoodChartFilterProvider()), - ChangeNotifierProvider(create: (_) => MoodState(_emotionsDao)), - ChangeNotifierProvider(create: (_) => MoodEntryProvider()), + ChangeNotifierProvider(create: (_) => MoodState(_emotionsDao, _trackDao)), + ChangeNotifierProvider(create: (_) => MoodHeatmapFilterProvider()), ], child: StreamBuilder( stream: _userSettingsDao.localeStream, builder: (_, snapshot) { - final locale = snapshot.data; return StreamBuilder( stream: _userSettingsDao.themeModeStream, builder: (context, snapshot){ final themeMode = snapshot.data ?? ThemeMode.system; - return MaterialApp.router( + debugShowCheckedModeBanner: false, title: _getAppNameFromLocale(locale), theme: NepanikarTheme.getThemeData( fontFamily: locale?.languageCode == NepanikarLanguages.uk.languageCode diff --git a/lib/providers/mood_chart_filter_provider.dart b/lib/providers/mood_chart_filter_provider.dart index e112aebb..bcdcc7fe 100644 --- a/lib/providers/mood_chart_filter_provider.dart +++ b/lib/providers/mood_chart_filter_provider.dart @@ -141,21 +141,25 @@ class MoodChartFilterProvider extends ChangeNotifier { extension MoodChartFilterExt on Iterable { Map filterByDateRange(DateTimeRange dateRange) { - final allValuesMap = { - for (final moodTrack in this) moodTrack.date: moodTrack, - }; - - final start = dateRange.start.toUtcDate(); - final end = dateRange.end.toUtcDate(); - final dateRangeValues = List.generate( - end.difference(start).inDays + 1, - (i) => start.add(Duration(days: i)), - ); + final startOfDay = DateTime(dateRange.start.year, dateRange.start.month, dateRange.start.day); + final endOfDay = DateTime(dateRange.end.year, dateRange.end.month, dateRange.end.day); + + final moodTracks = map((moodTrack) { + var originalDate = moodTrack.date; + var normalizedDate = DateTime(originalDate.year, originalDate.month, originalDate.day); + // Assuming your MoodTrack class has a copyWith method + return moodTrack.copyWith(date: normalizedDate); + }).toList(); + // Filter MoodTracks based on normalized date final filteredMap = {}; - for (final date in dateRangeValues) { - final dayMoodTrack = allValuesMap[date]; - filteredMap[date] = dayMoodTrack; + for (final moodTrack in moodTracks) { + final moodTrackDate = DateTime(moodTrack.date.year, moodTrack.date.month, moodTrack.date.day); + if (moodTrackDate.isAtSameMomentAs(startOfDay) || + (moodTrackDate.isAfter(startOfDay) && moodTrackDate.isBefore(endOfDay)) || + moodTrackDate.isAtSameMomentAs(endOfDay)) { + filteredMap[moodTrack.date] = moodTrack; + } } return filteredMap; diff --git a/lib/providers/mood_entry_provider.dart b/lib/providers/mood_entry_provider.dart deleted file mode 100644 index a60d4667..00000000 --- a/lib/providers/mood_entry_provider.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:nepanikar/services/db/my_records/mood_track_model.dart'; - -class MoodEntryProvider with ChangeNotifier { - MoodTrack? _selectedMoodEntry; - - MoodTrack? get selectedMoodEntry => _selectedMoodEntry; - - void selectMoodEntry(MoodTrack moodEntry) { - _selectedMoodEntry = moodEntry; - notifyListeners(); - } - - void clearSelection() { - _selectedMoodEntry = null; - notifyListeners(); - } -} diff --git a/lib/providers/mood_heatmap_filter_provider.dart b/lib/providers/mood_heatmap_filter_provider.dart new file mode 100644 index 00000000..ac2064d9 --- /dev/null +++ b/lib/providers/mood_heatmap_filter_provider.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:nepanikar/app/l10n/ext.dart'; + +import 'package:nepanikar/providers/mood_chart_filter_provider.dart'; + + + +enum HeatmapFilter{ + month, + year; + + static HeatmapFilter initial = HeatmapFilter.month; + + String getLabel(BuildContext context) { + switch (this) { + case HeatmapFilter.month: + return context.l10n.month; + case HeatmapFilter.year: + return context.l10n.year; + } + } + + DateTimeRange getDateTimeRange({DateRangeSwitch? dateRangeSwitch, DateTimeRange? customDateRange}) { + final now = DateTime.now(); + DateTime start; + DateTime end; + + DateTimeRange shiftMonths(DateTime startDate, int months) { + final adjustedStart = DateTime(startDate.year, startDate.month + months); + final endOfMonth = DateTime(adjustedStart.year, adjustedStart.month + 1, 0); // Correctly finds the end of the target month + + return DateTimeRange( + start: adjustedStart, + end: endOfMonth, + ); + } + + if (customDateRange == null && dateRangeSwitch == null) { + // Initialization logic for the current period + switch (this) { + case HeatmapFilter.month: + start = DateTime(now.year, now.month, 1); + end = DateTime(now.year, now.month + 1, 0); + break; + case HeatmapFilter.year: + start = DateTime(now.year, 1, 1); + end = DateTime(now.year, 12, 31); + break; + default: + start = now; + end = now; + } + } else if (customDateRange != null) { + // Use the custom date range if provided + start = customDateRange.start; + end = customDateRange.end; + } else { + + start = now; + end = now; + } + + switch (this) { + case HeatmapFilter.month: + if ( dateRangeSwitch == DateRangeSwitch.previous) { + return shiftMonths(start, -1); // Shift 1 month back for previous or initialize + } + else if(dateRangeSwitch == null){ + return DateTimeRange(start: start, end: end); + } + else { + return shiftMonths(end, 1); // Shift 1 month forward for next + } + case HeatmapFilter.year: + if (dateRangeSwitch == DateRangeSwitch.previous) { + return shiftMonths(start, -12); // Shift 12 months back for previous or initialize + } + else if(dateRangeSwitch == null){ + return DateTimeRange(start: start, end: end); + } + else { + return shiftMonths(end, 12); // Shift 12 months forward for next + } + default: + return DateTimeRange(start: start, end: end); + } + } +} + +class MoodHeatmapFilterProvider extends ChangeNotifier{ + MoodHeatmapFilterProvider() + : _activeFilter = HeatmapFilter.initial, + _activeDateRange = HeatmapFilter.initial.getDateTimeRange(); + + HeatmapFilter _activeFilter; + HeatmapFilter get activeFilter => _activeFilter; + + DateTimeRange _activeDateRange; + DateTimeRange get activeDateRange => _activeDateRange; + + void setFilter(HeatmapFilter filter) { + _activeFilter = filter; + _activeDateRange = filter.getDateTimeRange(); + notifyListeners(); + } + + void setActiveDateRange(DateTimeRange dateRange) { + _activeDateRange = dateRange; + notifyListeners(); + } + + void shiftDateRange(DateRangeSwitch direction) { + _activeDateRange = _activeFilter.getDateTimeRange(dateRangeSwitch: direction, customDateRange: _activeDateRange); + notifyListeners(); + } + + bool get canShiftDateRangeNext { + final now = DateTime.now(); + final nextRange = _activeFilter.getDateTimeRange(dateRangeSwitch: DateRangeSwitch.next, customDateRange: _activeDateRange); + return nextRange.start.isBefore(now) || nextRange.start.isAtSameMomentAs(now); + } +} + diff --git a/lib/providers/mood_state_provider.dart b/lib/providers/mood_state_provider.dart index aef835dd..2b8bc2d5 100644 --- a/lib/providers/mood_state_provider.dart +++ b/lib/providers/mood_state_provider.dart @@ -4,24 +4,60 @@ import 'package:nepanikar/services/db/my_records/mood_track_dao.dart'; // Adjust import 'package:nepanikar/services/db/my_records/mood_track_model.dart'; class MoodState with ChangeNotifier { - MoodState(this._emotionsDao) { - _loadEmotions(); // Load emotions when the MoodState is initialized + MoodState( + this._emotionsDao, + this._trackDao, + ) { + _loadEmotions(); } Mood? _activeMood; List _emotions = []; - final EmotionsDao _emotionsDao; + bool _editing = false; + final MoodTrackDao _trackDao; + MoodTrack? _selectedMoodEntry; + List _moodEntries = []; + + MoodTrack? get selectedMoodEntry => _selectedMoodEntry; + + List get moodEntries => _moodEntries; List get emotions => _emotions; Mood? get activeMood => _activeMood; + bool get editing => _editing; + void setActiveMood(Mood mood) { _activeMood = mood; notifyListeners(); } + void selectMoodEntry(MoodTrack moodEntry) { + _selectedMoodEntry = moodEntry; + notifyListeners(); + } + + void clearSelection() { + _selectedMoodEntry = null; + notifyListeners(); + } + + void init() { + fetchMoodEntries(); + } + + List fetchMoodTracksInRange(DateTime start, DateTime end) { + return _moodEntries.where((entry) => entry.date.isAfter(start) && entry.date.isBefore(end)).toList(); + } + + void setEditing(bool value){ + _editing = value; + notifyListeners(); + } + + Future _loadEmotions() async { _emotions = await _emotionsDao.getEmotions(); notifyListeners(); @@ -33,7 +69,33 @@ class MoodState with ChangeNotifier { return succeed; } + Future saveMoodTrack(String summary, String description, List emotions, Mood mood) async { + await _trackDao.saveMoodTrack( + mood, + emotions, + summary, + description, + ); + await fetchMoodEntries(); + } + + Future updateMoodTrack(DateTime date, String summary, String description, List emotions, Mood mood) async { + await _trackDao.updateMoodTrack(date, summary, description, emotions, mood); + await fetchMoodEntries(); + } + + Future fetchMoodEntries() async { + _moodEntries = await _trackDao.getAllMoodTracks(); + notifyListeners(); + } + Future deleteEmotion(String emotion) async { await _emotionsDao.deleteEmotion(emotion); } + + Future deleteMoodTrack(MoodTrack moodTrack) async{ + await _trackDao.deleteMoodTrackByDate(moodTrack.date); + _moodEntries.remove(moodTrack); + notifyListeners(); + } } diff --git a/lib/screens/contacts/chat_contacts_screen.dart b/lib/screens/contacts/chat_contacts_screen.dart index 9d58770c..fe4aa57f 100644 --- a/lib/screens/contacts/chat_contacts_screen.dart +++ b/lib/screens/contacts/chat_contacts_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/services/db/user_settings/user_settings_dao.dart'; diff --git a/lib/screens/contacts/crisis_message_screen.dart b/lib/screens/contacts/crisis_message_screen.dart index 6078fe4e..24b52860 100644 --- a/lib/screens/contacts/crisis_message_screen.dart +++ b/lib/screens/contacts/crisis_message_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/services/db/my_contacts/my_contact_crisis_message_dao.dart'; import 'package:nepanikar/utils/registry.dart'; import 'package:nepanikar/widgets/nepanikar_button.dart'; @@ -79,6 +80,7 @@ class _CrisisMessageContentState extends State { required String hintText, required int minLines, required VoidCallback? onSaved, + required BuildContext context, bool readyOnly = false, bool autoFocus = false, FocusNode? focusNode, @@ -87,6 +89,7 @@ class _CrisisMessageContentState extends State { textController ..text = initialValue ..selection = TextSelection.fromPosition(TextPosition(offset: initialValue.length)); + final textColor = textColorBasedOnDarkMode(context); return Column( children: [ Align( @@ -95,6 +98,7 @@ class _CrisisMessageContentState extends State { labelText, style: NepanikarFonts.bodySmallMedium.copyWith( fontWeight: FontWeight.w700, + color: textColor, ), ), ), @@ -148,6 +152,7 @@ class _CrisisMessageContentState extends State { children: [ _buildForm( textController: _addressEmailController, + context: context, initialValue: contactAddress, labelText: context.l10n.my_contacts_numbers_example, hintText: context.l10n.custom_write, @@ -166,6 +171,7 @@ class _CrisisMessageContentState extends State { const SizedBox(height: 4), _buildForm( textController: _messageTextController, + context: context, focusNode: _messageFocusNode, initialValue: message, labelText: context.l10n.message_text, diff --git a/lib/screens/contacts/eating_disorder_contacts_screen.dart b/lib/screens/contacts/eating_disorder_contacts_screen.dart index 754bb36f..0bc1cfd6 100644 --- a/lib/screens/contacts/eating_disorder_contacts_screen.dart +++ b/lib/screens/contacts/eating_disorder_contacts_screen.dart @@ -5,6 +5,7 @@ import 'package:linkify/linkify.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/contact_action_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/services/db/user_settings/user_settings_dao.dart'; @@ -44,8 +45,9 @@ class EatingDisorderContactsScreen extends StatelessWidget { @override Widget build(BuildContext context) { + final textColor = customColorsBasedOnDarkMode(context, NepanikarColors.white, NepanikarColors.primary); final linkifiedTextStyle = NepanikarFonts.bodyBlack.copyWith( - color: NepanikarColors.primary, + color: textColor, decoration: TextDecoration.underline, ); diff --git a/lib/screens/contacts/region_contacts_screen.dart b/lib/screens/contacts/region_contacts_screen.dart index c9c1b162..e8bc743a 100644 --- a/lib/screens/contacts/region_contacts_screen.dart +++ b/lib/screens/contacts/region_contacts_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/services/db/user_settings/user_settings_dao.dart'; import 'package:nepanikar/utils/contacts_data_manager.dart'; import 'package:nepanikar/utils/registry.dart'; @@ -84,17 +85,19 @@ class _RegionContactsScreenState extends State { } } - Widget _buildRegionHeader(String header) { + Widget _buildRegionHeader(String header, Color? textColor) { return Text( header, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor, ), ); } @override Widget build(BuildContext context) { + final textColor = textColorBasedOnDarkMode(context); return NepanikarScreenWrapper( appBarTitle: widget.appBarTitle, isModuleList: false, @@ -104,7 +107,10 @@ class _RegionContactsScreenState extends State { if (widget.regionContacts.length > 1) ...[ Text( context.l10n.select_region_dropdown_label, - style: NepanikarFonts.bodySmallMedium.copyWith(fontWeight: FontWeight.w700), + style: NepanikarFonts.bodySmallMedium.copyWith( + fontWeight: FontWeight.w700, + color: textColor, + ), ), const SizedBox(height: 5), NepanikarDropdown.outlined( @@ -118,7 +124,10 @@ class _RegionContactsScreenState extends State { if (_activeDropdownMenuItem != null) ...[ if (widget.regionContacts.length > 1) ...[ const SizedBox(height: 20), - _buildRegionHeader(_activeDropdownMenuItem!.region), + _buildRegionHeader( + _activeDropdownMenuItem!.region, + textColor, + ), ], ..._activeDropdownMenuItem!.contacts.map( (u) => Padding( diff --git a/lib/screens/home/eating_disorder/eating_disorder_distractions_screen.dart b/lib/screens/home/eating_disorder/eating_disorder_distractions_screen.dart index b887791f..de79ec56 100644 --- a/lib/screens/home/eating_disorder/eating_disorder_distractions_screen.dart +++ b/lib/screens/home/eating_disorder/eating_disorder_distractions_screen.dart @@ -8,6 +8,7 @@ import 'package:nepanikar/games/balloons/balloons_game_screen.dart'; import 'package:nepanikar/games/breathing/breathing_exercises_screen.dart'; import 'package:nepanikar/games/math/math_game_screen.dart'; import 'package:nepanikar/games/relaxation/relaxations_list_screen.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/services/db/user_settings/user_settings_dao.dart'; import 'package:nepanikar/utils/registry.dart'; import 'package:nepanikar/widgets/long_tile.dart'; @@ -28,39 +29,38 @@ class EatingDisorderDistractionsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final svgColor = svgColorBasedOnDarkMode(context); + final isDarkMode = Theme.of(context).brightness == Brightness.dark; final modules = [ LongTile( text: context.l10n.math, - image: Assets.illustrations.games.math.math.svg(), + image: Assets.illustrations.games.math.math.svg(color: svgColor), onTap: () => context.push(const MathGameRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.game_balls, - image: Assets.illustrations.games.balloons.baloons.svg(), + image: Assets.illustrations.games.balloons.baloons.svg(color: svgColor), onTap: () => context.push(const BalloonsGameRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.game_balance, - image: Assets.illustrations.games.swing.swing.svg(), + image: Assets.illustrations.games.swing.swing.svg(color: svgColor), onTap: () => context.push(const BalanceGameRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.breath, - image: Assets.illustrations.modules.breathing.svg(), + image: Assets.illustrations.modules.breathing.svg(color: svgColor), onTap: () => context.push(const BreathingExercisesRoute().location), isDarkMode: isDarkMode, ), if (['cs', 'sk'].contains(_userSettingsDao.locale.languageCode)) LongTile( text: context.l10n.relaxation, - image: Assets.illustrations.modules.relaxation.svg(), + image: Assets.illustrations.modules.relaxation.svg(color: svgColor), onTap: () => context.push(const RelaxationsListRoute().location), isDarkMode: isDarkMode, ), diff --git a/lib/screens/home/eating_disorder/eating_disorder_samples_screen.dart b/lib/screens/home/eating_disorder/eating_disorder_samples_screen.dart index 69a2bdb4..3a9a9742 100644 --- a/lib/screens/home/eating_disorder/eating_disorder_samples_screen.dart +++ b/lib/screens/home/eating_disorder/eating_disorder_samples_screen.dart @@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/screens/home/eating_disorder/meal_plan_screen.dart'; import 'package:nepanikar/utils/meal_plan_config.dart'; import 'package:nepanikar/widgets/long_tile.dart'; @@ -21,14 +22,13 @@ class EatingDisordersSamplesScreen extends StatelessWidget { @override Widget build(BuildContext context) { - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final svgColor = svgColorBasedOnDarkMode(context); + final isDarkMode = Theme.of(context).brightness == Brightness.dark; final modules = [ LongTile( text: context.l10n.food_breakfast, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push( Uri( path: const MealPlanRoute().location, @@ -42,7 +42,7 @@ class EatingDisordersSamplesScreen extends StatelessWidget { ), LongTile( text: '${context.l10n.food_am_snack} ', - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push( Uri( path: const MealPlanRoute().location, @@ -56,7 +56,7 @@ class EatingDisordersSamplesScreen extends StatelessWidget { ), LongTile( text: '${context.l10n.food_lunch} ', - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push( Uri( path: const MealPlanRoute().location, @@ -70,7 +70,7 @@ class EatingDisordersSamplesScreen extends StatelessWidget { ), LongTile( text: context.l10n.food_pm_snack, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push( Uri( path: const MealPlanRoute().location, @@ -84,7 +84,7 @@ class EatingDisordersSamplesScreen extends StatelessWidget { ), LongTile( text: context.l10n.food_dinner, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push( Uri( path: const MealPlanRoute().location, diff --git a/lib/screens/home/eating_disorder/eating_disorder_screen.dart b/lib/screens/home/eating_disorder/eating_disorder_screen.dart index ecb609f3..a9c806ba 100644 --- a/lib/screens/home/eating_disorder/eating_disorder_screen.dart +++ b/lib/screens/home/eating_disorder/eating_disorder_screen.dart @@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/screens/contacts/eating_disorder_contacts_screen.dart'; import 'package:nepanikar/screens/home/eating_disorder/eating_disorder_distractions_screen.dart'; import 'package:nepanikar/screens/home/eating_disorder/eating_disorder_samples_screen.dart'; @@ -39,40 +40,38 @@ class EatingDisorderScreen extends StatelessWidget { @override Widget build(BuildContext context) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final svgColor = svgColorBasedOnDarkMode(context); + final isDarkMode = Theme.of(context).brightness == Brightness.dark; final modules = [ LongTile( text: context.l10n.food_tips, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderTipsRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_tasks, - image: Assets.illustrations.modules.homework.svg(), + image: Assets.illustrations.modules.homework.svg(color: svgColor), onTap: () => context.push(const EatingDisorderTasksRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_dishes, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderSamplesRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.distraction, - image: Assets.illustrations.games.math.math.svg(), + image: Assets.illustrations.games.math.math.svg(color: svgColor), onTap: () => context.push(const EatingDisorderDistractionsRoute().location), isDarkMode: isDarkMode, ), if (shouldShowContactsTile) LongTile( text: context.l10n.food_contact, - image: Assets.illustrations.contacts.phones.svg(), + image: Assets.illustrations.contacts.phones.svg(color: svgColor), onTap: () => context.push(const EatingDisorderContactsRoute().location), isDarkMode: isDarkMode, ), diff --git a/lib/screens/home/eating_disorder/eating_disorder_tasks_screen.dart b/lib/screens/home/eating_disorder/eating_disorder_tasks_screen.dart index 07c8be2f..8ea76a03 100644 --- a/lib/screens/home/eating_disorder/eating_disorder_tasks_screen.dart +++ b/lib/screens/home/eating_disorder/eating_disorder_tasks_screen.dart @@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/screens/home/eating_disorder/tasks/eating_disorder_food_afraid_of_screen.dart'; import 'package:nepanikar/screens/home/eating_disorder/tasks/eating_disorder_food_challenges_screen.dart'; import 'package:nepanikar/screens/home/eating_disorder/tasks/eating_disorder_food_creative_screen.dart'; @@ -24,45 +25,43 @@ class EatingDisorderTasksScreen extends StatelessWidget { @override Widget build(BuildContext context) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final svgColor = svgColorBasedOnDarkMode(context); final modules = [ LongTile( text: context.l10n.food_tasks_creative, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderFoodCreativeRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_tasks_motivation, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderFoodMotivationRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_tasks_challenge, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderFoodChallengesRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_tasks_like, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderLikeOnMyselfRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_tasks_food_like, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderFoodILikeRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_tasks_afraid, - image: Assets.illustrations.modules.eatingDisorder.svg(), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), onTap: () => context.push(const EatingDisorderFoodAfraidOfRoute().location), isDarkMode: isDarkMode, ), diff --git a/lib/screens/home/eating_disorder/eating_disorder_tips_screen.dart b/lib/screens/home/eating_disorder/eating_disorder_tips_screen.dart index 73c88be2..99633efb 100644 --- a/lib/screens/home/eating_disorder/eating_disorder_tips_screen.dart +++ b/lib/screens/home/eating_disorder/eating_disorder_tips_screen.dart @@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/screens/home/eating_disorder/tips/eating_disorder_tips_fail.dart'; import 'package:nepanikar/screens/home/eating_disorder/tips/eating_disorder_tips_figure.dart'; import 'package:nepanikar/screens/home/eating_disorder/tips/eating_disorder_tips_general.dart'; @@ -24,46 +25,50 @@ class EatingDisorderTipsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final svgColor = svgColorBasedOnDarkMode(context); + final isDarkMode = Theme.of(context).brightness == Brightness.dark; final modules = [ LongTile( text: context.l10n.food_figure, - image: Assets.illustrations.modules.figure.svg(), - onTap: () => context.push(const EatingDisorderTipsFigureAppRoute().location), + image: Assets.illustrations.modules.figure.svg(color: svgColor), + onTap: () => + context.push(const EatingDisorderTipsFigureAppRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_remorse, - image: Assets.illustrations.modules.eatingDisorder.svg(), - onTap: () => context.push(const EatingDisorderTipsRemorseAppRoute().location), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), + onTap: () => + context.push(const EatingDisorderTipsRemorseAppRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_overeat, - image: Assets.illustrations.modules.eatingDisorder.svg(), - onTap: () => context.push(const EatingDisorderTipsOvereatAppRoute().location), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), + onTap: () => + context.push(const EatingDisorderTipsOvereatAppRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_vomit, - image: Assets.illustrations.modules.eatingDisorder.svg(), - onTap: () => context.push(const EatingDisorderTipsVomitAppRoute().location), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), + onTap: () => + context.push(const EatingDisorderTipsVomitAppRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_fail, - image: Assets.illustrations.modules.eatingDisorder.svg(), - onTap: () => context.push(const EatingDisorderTipsFailAppRoute().location), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), + onTap: () => + context.push(const EatingDisorderTipsFailAppRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.food_misc, - image: Assets.illustrations.modules.eatingDisorder.svg(), - onTap: () => context.push(const EatingDisorderTipsGeneralAppRoute().location), + image: Assets.illustrations.modules.eatingDisorder.svg(color: svgColor), + onTap: () => + context.push(const EatingDisorderTipsGeneralAppRoute().location), isDarkMode: isDarkMode, ), ]; diff --git a/lib/screens/home/eating_disorder/meal_plan_screen.dart b/lib/screens/home/eating_disorder/meal_plan_screen.dart index c47741d2..a48cf0f4 100644 --- a/lib/screens/home/eating_disorder/meal_plan_screen.dart +++ b/lib/screens/home/eating_disorder/meal_plan_screen.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/utils/meal_plan_config.dart'; import 'package:nepanikar/widgets/nepanikar_screen_wrapper.dart'; @@ -29,11 +31,12 @@ class MealPlanScreen extends StatelessWidget { @override Widget build(BuildContext context) { final meals = getMealsById(context, id ?? ''); + final backgroundColor = customColorsBasedOnDarkMode(context, NepanikarColors.containerD, NepanikarColors.white); return NepanikarScreenWrapper( appBarTitle: title.toString(), children: [ Material( - color: Colors.white, + color: backgroundColor, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.symmetric( @@ -55,7 +58,7 @@ class MealPlanScreen extends StatelessWidget { ), Text( '${meal.title?.trim()}', - style: NepanikarFonts.title3, + style: NepanikarFonts.title3.copyWith(color: textColorBasedOnDarkMode(context)), ), if (meal.description != null) Padding( diff --git a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_fail.dart b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_fail.dart index 672963c6..2f5bf095 100644 --- a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_fail.dart +++ b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_fail.dart @@ -4,6 +4,8 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/widgets/tips_carousel_body.dart'; +import '../../../../helpers/color_helpers.dart'; + class EatingDisorderTipsFailAppRoute extends GoRouteData { const EatingDisorderTipsFailAppRoute(); @@ -16,6 +18,7 @@ class EatingDisorderTipsFailScreen extends StatelessWidget { @override Widget build(BuildContext context) { final strings = context.l10n.food_fail_text.split('\n'); + final textColor = textColorBasedOnDarkMode(context); return Scaffold( appBar: AppBar(title: Text(context.l10n.food_fail)), @@ -31,6 +34,7 @@ class EatingDisorderTipsFailScreen extends StatelessWidget { textAlign: TextAlign.center, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor, ), ), ), diff --git a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_figure.dart b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_figure.dart index 7883a154..292a5e62 100644 --- a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_figure.dart +++ b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_figure.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/widgets/tips_carousel_body.dart'; class EatingDisorderTipsFigureAppRoute extends GoRouteData { @@ -16,6 +17,7 @@ class EatingDisorderTipsFigureAppScreen extends StatelessWidget { @override Widget build(BuildContext context) { final strings = context.l10n.food_figure_text.split('\n'); + final textColor = textColorBasedOnDarkMode(context); return Scaffold( appBar: AppBar(title: Text(context.l10n.food_figure)), @@ -31,6 +33,7 @@ class EatingDisorderTipsFigureAppScreen extends StatelessWidget { textAlign: TextAlign.center, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor ), ), ), diff --git a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_general.dart b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_general.dart index 94957874..432dbb12 100644 --- a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_general.dart +++ b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_general.dart @@ -4,6 +4,8 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/widgets/tips_carousel_body.dart'; +import '../../../../helpers/color_helpers.dart'; + class EatingDisorderTipsGeneralAppRoute extends GoRouteData { const EatingDisorderTipsGeneralAppRoute(); @@ -16,6 +18,7 @@ class EatingDisorderTipsGeneralScreen extends StatelessWidget { @override Widget build(BuildContext context) { final strings = context.l10n.food_misc_text.split('\n'); + final textColor = textColorBasedOnDarkMode(context); return Scaffold( appBar: AppBar(title: Text(context.l10n.food_misc)), @@ -31,6 +34,7 @@ class EatingDisorderTipsGeneralScreen extends StatelessWidget { textAlign: TextAlign.center, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor, ), ), ), diff --git a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_overeat.dart b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_overeat.dart index 64734787..5fe41680 100644 --- a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_overeat.dart +++ b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_overeat.dart @@ -4,6 +4,8 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/widgets/tips_carousel_body.dart'; +import '../../../../helpers/color_helpers.dart'; + class EatingDisorderTipsOvereatAppRoute extends GoRouteData { const EatingDisorderTipsOvereatAppRoute(); @@ -16,7 +18,7 @@ class EatingDisorderTipsOvereatScreen extends StatelessWidget { @override Widget build(BuildContext context) { final strings = context.l10n.food_overeat_text.split('\n'); - + final textColor = textColorBasedOnDarkMode(context); return Scaffold( appBar: AppBar(title: Text(context.l10n.food_overeat)), body: SafeArea( @@ -31,6 +33,7 @@ class EatingDisorderTipsOvereatScreen extends StatelessWidget { textAlign: TextAlign.center, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor ), ), ), diff --git a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_remorse.dart b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_remorse.dart index 1d974137..d5c75d86 100644 --- a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_remorse.dart +++ b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_remorse.dart @@ -4,6 +4,8 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/widgets/tips_carousel_body.dart'; +import '../../../../helpers/color_helpers.dart'; + class EatingDisorderTipsRemorseAppRoute extends GoRouteData { const EatingDisorderTipsRemorseAppRoute(); @@ -16,6 +18,7 @@ class EatingDisorderTipsRemorseScreen extends StatelessWidget { @override Widget build(BuildContext context) { final strings = context.l10n.food_tips_text.split('\n'); + final textColor = textColorBasedOnDarkMode(context); return Scaffold( appBar: AppBar(title: Text(context.l10n.food_remorse)), @@ -31,6 +34,7 @@ class EatingDisorderTipsRemorseScreen extends StatelessWidget { textAlign: TextAlign.center, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor, ), ), ), diff --git a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_vomit.dart b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_vomit.dart index f3bc4798..fb467d03 100644 --- a/lib/screens/home/eating_disorder/tips/eating_disorder_tips_vomit.dart +++ b/lib/screens/home/eating_disorder/tips/eating_disorder_tips_vomit.dart @@ -4,6 +4,8 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/widgets/tips_carousel_body.dart'; +import '../../../../helpers/color_helpers.dart'; + class EatingDisorderTipsVomitAppRoute extends GoRouteData { const EatingDisorderTipsVomitAppRoute(); @@ -16,7 +18,7 @@ class EatingDisorderTipsVomitScreen extends StatelessWidget { @override Widget build(BuildContext context) { final strings = context.l10n.food_vomit_text.split('\n'); - + final textColor = textColorBasedOnDarkMode(context); return Scaffold( appBar: AppBar(title: Text(context.l10n.food_vomit)), body: SafeArea( @@ -31,6 +33,7 @@ class EatingDisorderTipsVomitScreen extends StatelessWidget { textAlign: TextAlign.center, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor, ), ), ), diff --git a/lib/screens/home/my_records/diary/my_records_diary_add_screen.dart b/lib/screens/home/my_records/diary/my_records_diary_add_screen.dart index 7e7957d2..0235c2b8 100644 --- a/lib/screens/home/my_records/diary/my_records_diary_add_screen.dart +++ b/lib/screens/home/my_records/diary/my_records_diary_add_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/screens/home/my_records/diary/my_records_diary_detail_screen.dart'; import 'package:nepanikar/services/db/my_records/diary/my_records_diary_dao.dart'; diff --git a/lib/screens/home/my_records/diary/my_records_diary_detail_screen.dart b/lib/screens/home/my_records/diary/my_records_diary_detail_screen.dart index 31929625..65b26824 100644 --- a/lib/screens/home/my_records/diary/my_records_diary_detail_screen.dart +++ b/lib/screens/home/my_records/diary/my_records_diary_detail_screen.dart @@ -8,6 +8,7 @@ import 'package:intl/intl.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/screen_resolution_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/screens/home/my_records/diary/my_records_diary_edit_screen.dart'; @@ -57,6 +58,7 @@ class MyRecordsDiaryDetailScreen extends StatelessWidget { @override Widget build(BuildContext context) { + final textColor = textColorBasedOnDarkMode(context); final locale = Localizations.localeOf(context); final screenSize = context.screenSize; final analytics = registry.get(); @@ -76,15 +78,19 @@ class MyRecordsDiaryDetailScreen extends StatelessWidget { DateFormat.yMd(locale.languageCode).format(diaryRecord.dateTime), style: NepanikarFonts.bodySmallHeavy.copyWith( fontWeight: FontWeight.w700, + color: textColor, ), ), const SizedBox(height: 16), Text( diaryRecord.title, - style: NepanikarFonts.title3.copyWith(fontWeight: FontWeight.w700), + style: NepanikarFonts.title3.copyWith( + fontWeight: FontWeight.w700, + color: textColor + ), ), const SizedBox(height: 16), - Text(diaryRecord.text, style: NepanikarFonts.bodyRoman), + Text(diaryRecord.text, style: NepanikarFonts.bodyRoman.copyWith(color: textColor)), SizedBox(height: screenSize.height * 0.2), NepanikarButton( text: context.l10n.edit, diff --git a/lib/screens/home/my_records/journal/my_records_detail_journal_screen.dart b/lib/screens/home/my_records/journal/my_records_detail_journal_screen.dart index 4bef9535..6c73f64e 100644 --- a/lib/screens/home/my_records/journal/my_records_detail_journal_screen.dart +++ b/lib/screens/home/my_records/journal/my_records_detail_journal_screen.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/services/db/my_records/journal/my_records_journal_dao.dart'; import 'package:nepanikar/services/db/my_records/journal/my_records_journal_record_model.dart'; @@ -116,7 +117,8 @@ class _MyRecordsJournalDetailScreenState extends State FocusScope.of(context).unfocus(), @@ -153,7 +155,7 @@ class _MyRecordsJournalDetailScreenState extends State extends StatefulWidget { class _MoodEntryDetailState extends State> { - T get _trackDao => registry.get(); - - void _deleteMoodEntry(BuildContext context, MoodTrack moodEntry) async { - // Confirm the deletion with the user + Future _deleteMoodEntry(BuildContext context, MoodTrack moodEntry) async { final bool confirmDelete = await showDialog( context: context, builder: (BuildContext context) { return AlertDialog( backgroundColor: NepanikarColors.containerD, - title: const Text('Confirm'), - content: const Text('Are you sure you want to delete this mood entry?'), + title: Text(context.l10n.submit), + content: Text(context.l10n.confirm_delete), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), - child: const Text( - 'Cancel', - style: TextStyle( + child: Text( + context.l10n.cancel, + style: const TextStyle( color: NepanikarColors.white, ) , ), ), TextButton( onPressed: () => Navigator.of(context).pop(true), - child: const Text( - 'Delete', - style: TextStyle( + child: Text( + context.l10n.delete_record, + style: const TextStyle( color: NepanikarColors.white, ) , ), @@ -68,35 +65,27 @@ class _MoodEntryDetailState ); }, ) ?? false; // The dialog returns null if dismissed - - // If the deletion is confirmed, proceed to delete the mood entry - if (confirmDelete) { - await _trackDao.deleteMoodTrackByDate(moodEntry.date); - Navigator.of(context).pop(); // Go back to the previous screen - } + return confirmDelete; } - - - - @override Widget build(BuildContext context) { final moodEntry = - Provider.of(context).selectedMoodEntry!; + Provider.of(context).selectedMoodEntry!; final textStyleColor = customColorsBasedOnDarkMode( context, NepanikarColors.white, - NepanikarColors.primaryD, + NepanikarColors.primary, ); final formattedDate = DateFormat('d. MMM. yyyy HH:mm').format(moodEntry.date); + return Scaffold( appBar: AppBar( title: Text( formattedDate, - style: NepanikarFonts.title2.copyWith(color: textStyleColor), + style: NepanikarFonts.title2.copyWith(color: NepanikarColors.white), ), ), body: SafeArea( @@ -125,7 +114,7 @@ class _MoodEntryDetailState child: Opacity( opacity: 0.2, child: Text( - 'Emotions', + context.l10n.emotions, style: TextStyle( color: textStyleColor, fontSize: 50, @@ -137,18 +126,21 @@ class _MoodEntryDetailState Positioned( top: 110, right: 10, - child: ChosenEmotionsWidget( - initialEmotions: moodEntry.emotions ?? [], - deleteEmotionEnabled: false, - ), + child: Container( + width: 220, + child: ChosenEmotionsWidget( + initialEmotions: moodEntry.emotions ?? [], + deleteEmotionEnabled: false, + ), + ) ), Positioned( right: 3, - top: 220, + top: 240, child: Opacity( opacity: 0.2, child: Text( - 'Title', + context.l10n.title_entry_label, style: TextStyle( // Replace with your style color: textStyleColor, @@ -159,16 +151,16 @@ class _MoodEntryDetailState ), ), Positioned( - right: 30, - left: 70, - top: 270, + right: 15, + left: 60, + top: 290, child: Container( alignment: Alignment.centerRight, child: Text( moodEntry.summary!, style: TextStyle( - color: NepanikarColors.primarySwatch.shade600, - fontSize: 20, + color: NepanikarColors.primarySwatch.shade700, + fontSize: 25, fontWeight: FontWeight.bold, ), softWrap: true, @@ -191,7 +183,7 @@ class _MoodEntryDetailState child: Opacity( opacity: 0.2, child: Text( - 'Description', + context.l10n.description, style: TextStyle( // Replace with your style color: textStyleColor, @@ -208,7 +200,7 @@ class _MoodEntryDetailState child: Text( moodEntry.description!, style: TextStyle( - color: NepanikarColors.primarySwatch.shade600, + color: NepanikarColors.primarySwatch.shade700, fontSize: 15, fontWeight: FontWeight.w500, ), @@ -235,8 +227,16 @@ class _MoodEntryDetailState width: 130, // Set the width of the SizedBox to define the size of the button height: 60, // Set the height of the SizedBox to define the size of the button child: ElevatedButton( - onPressed: () { - _deleteMoodEntry(context, moodEntry); + onPressed: () async { + final success = await _deleteMoodEntry(context, moodEntry); + if(success){ + if(mounted){ + await Provider.of(context, listen: false).deleteMoodTrack(moodEntry); + } + } + if(mounted){ + Navigator.of(context).pop(); + } }, style: ElevatedButton.styleFrom( backgroundColor: NepanikarColors.deleteButton, @@ -254,7 +254,8 @@ class _MoodEntryDetailState height: 60, child: ElevatedButton( onPressed: () { - + Provider.of(context, listen: false).setEditing(true); + context.push(const MoodPickerRoute().location); }, style: ElevatedButton.styleFrom( backgroundColor: NepanikarColors.baseButtonD, diff --git a/lib/screens/home/my_records/mood/mood_picker_screen.dart b/lib/screens/home/my_records/mood/mood_picker_screen.dart index baea2534..918ae82f 100644 --- a/lib/screens/home/my_records/mood/mood_picker_screen.dart +++ b/lib/screens/home/my_records/mood/mood_picker_screen.dart @@ -3,19 +3,18 @@ import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:multi_select_flutter/multi_select_flutter.dart'; import 'package:nepanikar/app/l10n/ext.dart'; +import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/date_helpers.dart'; -import 'package:nepanikar/main.dart'; import 'package:nepanikar/providers/mood_state_provider.dart'; +import 'package:nepanikar/screens/home/my_records/mood/mood_records_screen.dart'; import 'package:nepanikar/services/db/my_records/mood_track_dao.dart'; import 'package:nepanikar/services/db/my_records/mood_track_model.dart'; -import 'package:nepanikar/services/notifications/notifications_service.dart'; import 'package:nepanikar/utils/registry.dart'; import 'package:nepanikar/widgets/mood/chosen_emotions.dart'; import 'package:nepanikar/widgets/mood/mood_picker.dart'; -import 'package:nepanikar/widgets/nepanikar_button.dart'; import 'package:provider/provider.dart'; class MoodPickerRoute extends GoRouteData { @@ -30,9 +29,11 @@ class MoodPickerScreen extends StatefulWidget { const MoodPickerScreen({ super.key, this.showBottomNavbar = true, + this.isEditing = false, }); final bool showBottomNavbar; + final bool isEditing; @override State> createState() => _MoodPickerScreenState(); @@ -52,41 +53,31 @@ class _MoodPickerScreenState String summary = ''; - String? description = ''; + String description = ''; + + MoodTrack? selectedMoodTrack; + + Mood? currentMood; final TextEditingController _newEmotionController = TextEditingController(); - void _showAddNewEmotionDialog() { - showDialog( - context: context, - builder: (ctx) { - return AlertDialog( - title: Text('Add a New Emotion'), - content: TextField( - controller: _newEmotionController, - decoration: InputDecoration( - hintText: 'Type a new emotion', - ), - ), - actions: [ - TextButton( - child: Text('Cancel'), - onPressed: () { - Navigator.of(ctx).pop(); - }, - ), - TextButton( - child: Text('Add'), - onPressed: () { - final updatedEmotions = _addNewEmotion(); - _onEmotionsUpdated(updatedEmotions); - context.pop(); - }, - ), - ], - ); - }, - ); + @override + void initState() { + super.initState(); + final moodState = Provider.of(context, listen: false); + + if (moodState.editing && moodState.selectedMoodEntry != null) { + selectedMoodTrack = moodState.selectedMoodEntry; + summary = selectedMoodTrack!.summary ?? ''; + description = selectedMoodTrack!.description ?? ''; + selectedEmotions = List.from(selectedMoodTrack!.emotions ?? []); + currentMood = selectedMoodTrack!.mood; + } else { + summary = ''; + description = ''; + selectedEmotions = []; + currentMood = null; + } } List _addNewEmotion() { @@ -118,22 +109,31 @@ class _MoodPickerScreenState @override Widget build(BuildContext context) { - emotions = Provider.of(context).emotions; + final moodState = Provider.of(context, listen: false); + emotions = moodState.emotions; final items = emotions .map((emotion) => MultiSelectItem(emotion, emotion)) .toList(); - Mood? currentMood = Provider.of(context).activeMood; + String formattedDateForEditing = ''; + + if (!moodState.editing) { + currentMood = Provider.of(context).activeMood; + } else { + formattedDateForEditing = DateFormat('d. MMM. yyyy HH:mm') + .format(moodState.selectedMoodEntry!.date); + } - const pageSidePadding = 24.0; - const pageHorizontalPadding = - EdgeInsets.symmetric(horizontal: pageSidePadding); + const pageHorizontalPadding = EdgeInsets.symmetric(horizontal: 24.0); final formattedDate = DateFormat('d. MMM. yyyy HH:mm').format(_now); //Colors final textStyleColor = customColorsBasedOnDarkMode( - context, NepanikarColors.white, NepanikarColors.primaryD,); + context, + NepanikarColors.white, + NepanikarColors.primaryD, + ); final containerColor = customColorsBasedOnDarkMode( context, NepanikarColors.containerD, NepanikarColors.white); @@ -142,7 +142,7 @@ class _MoodPickerScreenState return Scaffold( appBar: AppBar( title: Text( - formattedDate, + moodState.editing ? formattedDateForEditing : formattedDate, style: NepanikarFonts.title2.copyWith(color: NepanikarColors.white), ), ), @@ -155,13 +155,11 @@ class _MoodPickerScreenState child: StreamBuilder( stream: _trackDao.latestMoodTrackStream, builder: (_, snapshot) { - final latestMoodTrack = snapshot.data; return MoodPicker( activeMood: currentMood, autoSizeTitle: false, showLabels: false, onPick: (mood) async { - final l10n = context.l10n; currentMood = mood; }, ); @@ -174,11 +172,10 @@ class _MoodPickerScreenState child: TextFormField( initialValue: summary, decoration: InputDecoration( - labelText: 'Summary', + labelText: context.l10n.title_entry_label, labelStyle: TextStyle( - color: NepanikarColors.primarySwatch.shade400, - fontWeight: FontWeight.bold - ), + color: NepanikarColors.primarySwatch.shade400, + fontWeight: FontWeight.bold), fillColor: containerColor, filled: true, enabledBorder: OutlineInputBorder( @@ -187,7 +184,7 @@ class _MoodPickerScreenState ), borderRadius: BorderRadius.circular(8.0), ), - hintText: 'Enter a short summary', + hintText: context.l10n.enter_summary, hintStyle: TextStyle( color: NepanikarColors.primarySwatch.shade400, fontWeight: FontWeight.bold, @@ -212,28 +209,27 @@ class _MoodPickerScreenState searchable: true, items: items, backgroundColor: containerColor, - title: Text("Emotions"), + title: Text(context.l10n.emotions), decoration: BoxDecoration( - color: containerColor, - borderRadius: const BorderRadius.all(Radius.circular(40)), - border: Border.all( - color: NepanikarColors.containerD - ) - ), + color: containerColor, + borderRadius: + const BorderRadius.all(Radius.circular(40)), + border: + Border.all(color: NepanikarColors.containerD)), selectedColor: textStyleColor, - selectedItemsTextStyle: TextStyle( + selectedItemsTextStyle: TextStyle( color: textStyleColor, ), unselectedColor: textStyleColor, - itemsTextStyle: TextStyle( + itemsTextStyle: TextStyle( color: textStyleColor, ), - buttonIcon: Icon( + buttonIcon: Icon( Icons.arrow_drop_down, color: textStyleColor, ), buttonText: Text( - "Select Your Emotions", + context.l10n.select_your_emotions, style: TextStyle( color: NepanikarColors.primarySwatch.shade400, fontWeight: FontWeight.bold, @@ -241,17 +237,24 @@ class _MoodPickerScreenState ), ), onConfirm: (results) { - _onEmotionsUpdated(results.cast()); + if (results.length <= 9) { + _onEmotionsUpdated(results.cast()); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.l10n.select_up_to_9_emotions))); + } }, cancelText: Text( - "CANCEL", + context.l10n.cancel, style: TextStyle( color: textStyleColor, fontSize: 16, ), ), confirmText: Text( - "CONFIRM", + context.l10n.submit, style: TextStyle( color: textStyleColor, fontSize: 16, @@ -259,15 +262,22 @@ class _MoodPickerScreenState ), initialValue: selectedEmotions, chipDisplay: MultiSelectChipDisplay.none(), + validator: (values) { + if (values != null && values.length > 9) { + return context.l10n.select_up_to_9_emotions; + } + return null; + }, ), ), IconButton( - icon: Icon( + icon: Icon( Icons.add_circle_outline, size: 30, color: textStyleColor, ), - onPressed: _showAddNewEmotionDialog, + onPressed: () => + _showAddNewEmotionDialog(containerColor!), ), ], ), @@ -286,26 +296,27 @@ class _MoodPickerScreenState textAlignVertical: TextAlignVertical.top, keyboardType: TextInputType.multiline, decoration: InputDecoration( - labelText: 'Summary of your moment', - labelStyle: TextStyle( - color: NepanikarColors.primarySwatch.shade400, - fontWeight: FontWeight.bold, - ), - enabledBorder: OutlineInputBorder( - borderSide: const BorderSide( - color: NepanikarColors.containerD, - ), - borderRadius: BorderRadius.circular(8.0), - ), - filled: true, - fillColor: containerColor, - alignLabelWithHint: true, - hintText: 'Describe what happened...', - hintStyle: TextStyle( - color: NepanikarColors.primarySwatch.shade400, - fontWeight: FontWeight.bold, + labelText: context.l10n.summary_of_your_moment, + labelStyle: TextStyle( + color: NepanikarColors.primarySwatch.shade400, + fontWeight: FontWeight.bold, + ), + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide( + color: NepanikarColors.containerD, ), - floatingLabelBehavior: FloatingLabelBehavior.never), + borderRadius: BorderRadius.circular(8.0), + ), + filled: true, + fillColor: containerColor, + alignLabelWithHint: true, + hintText: context.l10n.describe_what_happened, + hintStyle: TextStyle( + color: NepanikarColors.primarySwatch.shade400, + fontWeight: FontWeight.bold, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + ), onChanged: (value) { setState(() { description = value; @@ -319,14 +330,26 @@ class _MoodPickerScreenState width: 600, child: ElevatedButton( onPressed: () async { - await _trackDao.saveMood( - currentMood!, - selectedEmotions, - summary, - description, - ); + if (moodState.editing) { + await Provider.of(context, listen: false) + .updateMoodTrack( + selectedMoodTrack!.date, + summary, + description, + selectedEmotions, + currentMood!, + ); + } else { + await Provider.of(context, listen: false) + .saveMoodTrack( + summary, + description, + selectedEmotions, + currentMood!, + ); + } if (mounted) { - context.pop(); + await context.push(const MoodRecordsRoute().location); } }, style: ElevatedButton.styleFrom( @@ -336,19 +359,86 @@ class _MoodPickerScreenState ), padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20), ), - child: const Text( - 'Save', - style: TextStyle( - color: Colors.white, - fontSize: 25, + child: Text( + context.l10n.save, + style: TextStyle( + color: Colors.white, + fontSize: 25, ), // Text color ), ), ), + const SizedBox( + height: 15, + ) ], ), ), ), ); } + + void _showAddNewEmotionDialog(Color containerColor) { + showDialog( + context: context, + builder: (ctx) { + return AlertDialog( + backgroundColor: containerColor, + title: Text(context.l10n.add_a_new_emotion), + content: TextField( + cursorColor: NepanikarColors.primarySwatch.shade600, + controller: _newEmotionController, + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: NepanikarColors.primarySwatch.shade600, + ), + ), + hintText: context.l10n.type_a_new_emotion, + ), + onChanged: (value) { + if (value.length > 12) { + setState(() {}); + } + }, + ), + actions: [ + TextButton( + child: Text( + context.l10n.cancel, + style: TextStyle( + color: customColorsBasedOnDarkMode(context, NepanikarColors.white, NepanikarColors.primary), + ), + ), + onPressed: () { + Navigator.of(ctx).pop(); + }, + ), + TextButton( + child: Text( + context.l10n.submit, + style: TextStyle( + color: customColorsBasedOnDarkMode(context, NepanikarColors.white, NepanikarColors.primary), + ), + ), + onPressed: () { + if (_newEmotionController.text.length > 12) { + ScaffoldMessenger.of(ctx).showSnackBar( + SnackBar( + content: Text(context.l10n.emotion_too_long), + duration: Duration(seconds: 2), + ), + ); + } else { + final updatedEmotions = _addNewEmotion(); + _onEmotionsUpdated(updatedEmotions); + Navigator.of(ctx).pop(); + } + }, + ), + ], + ); + }, + ); + } } diff --git a/lib/screens/home/my_records/mood/mood_records_screen.dart b/lib/screens/home/my_records/mood/mood_records_screen.dart index fe799777..446bebd1 100644 --- a/lib/screens/home/my_records/mood/mood_records_screen.dart +++ b/lib/screens/home/my_records/mood/mood_records_screen.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -10,16 +12,19 @@ import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/date_helpers.dart'; import 'package:nepanikar/providers/mood_chart_filter_provider.dart'; -import 'package:nepanikar/providers/mood_entry_provider.dart'; +import 'package:nepanikar/providers/mood_heatmap_filter_provider.dart'; +import 'package:nepanikar/providers/mood_state_provider.dart'; import 'package:nepanikar/screens/home/my_records/mood/mood_entry_detail_screen.dart'; import 'package:nepanikar/screens/home/my_records/mood/search_mood_entry.dart'; +import 'package:nepanikar/screens/home/my_records/my_records_screen.dart'; import 'package:nepanikar/services/db/my_records/mood_track_dao.dart'; import 'package:nepanikar/services/db/my_records/mood_track_model.dart'; import 'package:nepanikar/services/notifications/notifications_service.dart'; import 'package:nepanikar/utils/registry.dart'; import 'package:nepanikar/widgets/mood/mood_chart.dart'; -import 'package:nepanikar/widgets/mood/mood_data_table.dart'; import 'package:nepanikar/widgets/mood/mood_entry_card.dart'; +import 'package:nepanikar/widgets/mood/mood_heatmap.dart'; +import 'package:nepanikar/widgets/mood/mood_picker.dart'; import 'package:nepanikar/widgets/nepanikar_date_range_picker.dart'; import 'package:nepanikar/widgets/nepanikar_dropdown.dart'; import 'package:provider/provider.dart'; @@ -43,177 +48,291 @@ class MoodRecordsScreen extends StatefulWidget { class _MoodRecordsScreenState extends State> { - final _showChart = true; - int _entryCount = 5; + int _currentPageIndex = 0; DateTime get _now => getNowDateTimeLocal(); - T get _trackDao => registry.get(); - NotificationsService get _notificationsService => registry.get(); + final PageController _pageController = PageController(); - Future _loadMoreEntries() async { - setState(() { - _entryCount += 5; - }); - } - + //////////////// Build Method ////////////////////////// @override Widget build(BuildContext context) { - const pageSidePadding = 24.0; - const pageHorizontalPadding = - EdgeInsets.symmetric(horizontal: pageSidePadding); - //Colors final containerColor = customColorsBasedOnDarkMode( - context, NepanikarColors.containerD, NepanikarColors.white,); + context, + NepanikarColors.containerD, + NepanikarColors.white, + ); final textColor = customColorsBasedOnDarkMode( context, NepanikarColors.white, Colors.black); + final arrowCanShiftColor = customColorsBasedOnDarkMode( + context, + NepanikarColors.white, + null, + ); + return Scaffold( - appBar: AppBar( - actions: [ - IconButton( - icon: ExcludeSemantics( - child: Assets.icons.notificationBell.svg(color: Colors.white)), - tooltip: context.l10n.notifications, - onPressed: _notificationsService.checkPermission, - ), - ], - ), - body: SafeArea( - child: SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 15), - Card( - color: containerColor, - child: StreamBuilder>( - stream: _trackDao.allMoodTracksStream, - builder: (_, snapshot) { - final allMoodTrackData = snapshot.data ?? []; - final firstMoodTrackDate = - allMoodTrackData.firstOrNull?.date; - return Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 10), - child: Text( - "Mood Chart", - style: NepanikarFonts.bodyBlack.copyWith( - color: textColor, fontSize: 30), + appBar: _appBarForPageIndex(_currentPageIndex), + body: PageView( + controller: _pageController, + onPageChanged: (index) { + setState(() { + _currentPageIndex = index; + }); + }, + children: [ + /////////////////////////////////////FIRST-VIEW/////////////////////////////////// + /// MOOD STATISTICS /////////////// + SingleChildScrollView( + child: Column( + children: [ + const SizedBox( + height: 15, + ), + Card( + color: containerColor, + child: Consumer( + builder: (_, provider, child) { + final allMoodTrackData = provider.moodEntries; + final firstMoodTrackDate = + allMoodTrackData.firstOrNull?.date; + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 10), + child: Text( + context.l10n.mood_chart, + style: NepanikarFonts.bodyBlack + .copyWith(color: textColor, fontSize: 30), + ), ), - ), - Padding( + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 24), + child: _buildChartWithFilters( + context, + allMoodTrackData: allMoodTrackData, + firstMoodTrackDate: firstMoodTrackDate, + ), + ) + ], + ); + }, + ), + ), + const SizedBox( + height: 30, + ), + Card( + color: containerColor, + child: Consumer( + builder: (_, provider, child) { + final allMoodTrackData = provider.moodEntries; + + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 10), + child: Text( + context.l10n.mood_heatmap, + style: NepanikarFonts.bodyBlack + .copyWith(color: textColor, fontSize: 30), + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 24), + child: _createHeatMap(context, + moodTracks: allMoodTrackData), + ) + ], + ); + }, + ), + ), + const SizedBox( + height: 30, + ), + ], + ), + ), + /////////////////////////////////////SECOND-VIEW//////////////////////////////// + //////// MOOD ENTRIES /////////////// + SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 15,), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: MoodPicker( + onPick: (mood) async { + final l10n = context.l10n; + unawaited(_notificationsService.rescheduleNotifications(l10n)); + }, + ), + ), + Consumer( + builder: (_, provider, child) { + if (_entryCount > provider.moodEntries.length) { + _entryCount = provider.moodEntries.length; + } + final moodEntries = + provider.moodEntries.reversed.take(_entryCount).toList(); + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: moodEntries.length, + itemBuilder: (context, index) { + final moodEntry = moodEntries[index]; + return Padding( padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 24), - child: _buildChartWithFilters( - context, - allMoodTrackData: allMoodTrackData, - firstMoodTrackDate: firstMoodTrackDate, + vertical: 10, horizontal: 16), + child: MoodEntryCard( + dateTime: DateFormat('d. MMM yyyy, HH:mm') + .format(moodEntry.date), + moodIcon: moodEntry.mood.icon, + moodDescription: moodEntry.summary ?? '', + onTap: () { + Provider.of(context, listen: false) + .selectMoodEntry(moodEntry); + context.push(const MoodEntryDetailRoute().location); + }, + moodColor: null, ), - ) - ], + ); + }, ); }, ), - ), - const SizedBox(height: 10), - Stack( - children: [ - // Center the title within the stack - Align( - alignment: Alignment.center, - child: Text( - "Mood Entries", - style: NepanikarFonts.title2.copyWith(color: textColor), - ), - ), - Positioned( - right: 20, - top: -8, - child: IconButton( - icon: Icon(Icons.search, size: 35,), - onPressed: () { - context.push(const SearchMoodEntryRoute().location); - }, - ), - ), - ], - ), - StreamBuilder>( - stream: _trackDao.mostRecentMoodsTrackStream(_entryCount), - builder: (_, snapshot) { - final moodEntries = snapshot.data ?? []; - return ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: moodEntries.length, - itemBuilder: (context, index) { - final moodEntry = moodEntries[index]; - return Padding( - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), - child: MoodEntryCard( - dateTime: DateFormat('d. MMM yyyy, HH:mm') - .format(moodEntry.date), - moodIcon: moodEntry.mood.icon, - moodDescription: - moodEntry.summary ?? 'No description provided', - onTap: () { - Provider.of( - context, listen: false).selectMoodEntry( - moodEntry); - context.push(const MoodEntryDetailRoute().location); - }, - moodColor: null, - ), - ); - }, - ); - }, - ), - FutureBuilder( - future: _trackDao.getCountMoodTracks(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - // Show a loading indicator or some placeholder while waiting - return CircularProgressIndicator(); // or some other widget - } - if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData) { - final totalMoodCount = snapshot.data!; - if (_entryCount < totalMoodCount) { + Consumer( + builder: (context, provider, child) { + final moodEntriesCount = provider.moodEntries.length; + if (_entryCount < moodEntriesCount) { return SizedBox( height: 40, width: 70, child: ElevatedButton( - onPressed: _loadMoreEntries, - // Your method to load more entries + onPressed: () async { + setState(() { + _entryCount += 5; + }); + }, style: ElevatedButton.styleFrom( shape: StadiumBorder(), ), - child: Icon(Icons.more_horiz, size: 40), + child: const Icon(Icons.more_horiz, size: 40), ), ); + } else { + // You can return an empty Container or SizedBox if there's nothing to show + return SizedBox(); } - } - return Text("nothing"); - }, - ), - const SizedBox(height: 10,) - ], - ), - ), + }, + ) + ], + ), + ) + ], ), ); } - Widget _buildChartWithFilters(BuildContext context, { + //////////////////// App Bar Function ////////////////////////////// + + AppBar _appBarForPageIndex(int index) { + switch (index) { + //Mood Chart AppBar + case 0: + return AppBar( + title: Text( + context.l10n.mood_statistics, + style: NepanikarFonts.title2.copyWith(color: NepanikarColors.white), + ), + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + context.push(const MyRecordsRoute().location); + }, + ), + actions: [ + IconButton( + icon: Icon(Icons.arrow_forward_ios), + tooltip: context.l10n.notifications, + onPressed: () { + setState(() { + _currentPageIndex += 1; + _pageController.jumpToPage(_currentPageIndex); + }); + }, + ), + IconButton( + icon: ExcludeSemantics( + child: + Assets.icons.notificationBell.svg(color: Colors.white)), + tooltip: context.l10n.notifications, + onPressed: _notificationsService.checkPermission, + ), + ], + ); + //Mood Entries AppBar + case 1: + return AppBar( + title: Text( + context.l10n.mood_entries, + style: NepanikarFonts.title2.copyWith(color: NepanikarColors.white), + ), + leading: Container( + margin: const EdgeInsets.only(left: 40), + child: IconButton( + icon: const Icon(Icons.arrow_back_ios), + onPressed: () { + setState(() { + _currentPageIndex -= 1; + _pageController.jumpToPage(_currentPageIndex); + }); + }, + ), + ), + actions: [ + IconButton( + icon: const Icon( + Icons.search, + size: 35, + ), + onPressed: () { + context.push(const SearchMoodEntryRoute().location); + }, + ), + ], + ); + default: + return AppBar(title: Text('Page $index')); + } + } + + /////////////////////// Build Chart Function ////////////////////////////// + Widget _buildChartWithFilters( + BuildContext context, { required List allMoodTrackData, required DateTime? firstMoodTrackDate, }) { + + final containerColor = customColorsBasedOnDarkMode( + context, + NepanikarColors.containerD, + NepanikarColors.white, + ); + + final arrowCanShiftColor = customColorsBasedOnDarkMode( + context, + NepanikarColors.white, + null, + ); + return Consumer( builder: (_, moodChartFilterProvider, __) { final svgColor = svgColorBasedOnDarkMode(context); @@ -240,40 +359,34 @@ class _MoodRecordsScreenState onPick: moodChartFilterProvider.setFilter, ), ), - SizedBox(height: _showChart ? 32 : 16), - if (_showChart) - MoodChart( - moodTrackData: filteredData, - moodLabelBuilder: (m) => m.getSemanticsLabel(context), - ) - else - MoodDataTable( - moodTrackData: filteredData, - moodLabelBuilder: (m) => m.getSemanticsLabel(context), - ), + const SizedBox(height: 32), + MoodChart( + moodTrackData: filteredData, + moodLabelBuilder: (m) => m.getSemanticsLabel(context), + ), const SizedBox(height: 32), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( icon: ExcludeSemantics( - child: Assets.icons.navigation.arrowLeft - .svg(color: svgColor)), + child: + Assets.icons.navigation.arrowLeft.svg(color: svgColor), + ), tooltip: context.l10n.filter_previous_time_period, - onPressed: () => - moodChartFilterProvider - .shiftDateRange(DateRangeSwitch.previous), + onPressed: () => moodChartFilterProvider + .shiftDateRange(DateRangeSwitch.previous), ), Expanded( child: NepanikarDateRangePicker( firstDate: firstMoodTrackDate == null ? currDateRangeStart ?? - DateTime(_now.year, _now.month - 1) + DateTime(_now.year, _now.month - 1) : currDateRangeStart != null && - firstMoodTrackDate.isBefore(currDateRangeStart) - ? firstMoodTrackDate - : currDateRangeStart ?? - DateTime(_now.year, _now.month - 1), + firstMoodTrackDate.isBefore(currDateRangeStart) + ? firstMoodTrackDate + : currDateRangeStart ?? + DateTime(_now.year, _now.month - 1), lastDate: getNowDateTimeLocal(), activeRange: moodChartFilterProvider.customDateRange, onPick: moodChartFilterProvider.setCustomDateRange, @@ -283,14 +396,13 @@ class _MoodRecordsScreenState icon: ExcludeSemantics( child: Assets.icons.navigation.arrowRight.svg( // color: !canShiftNextDateRange ? Colors.grey : null, - color: !canShiftNextDateRange ? null : svgColor, + color: !canShiftNextDateRange ? containerColor : arrowCanShiftColor, ), ), tooltip: context.l10n.filter_next_time_period, onPressed: !canShiftNextDateRange ? null - : () => - moodChartFilterProvider + : () => moodChartFilterProvider .shiftDateRange(DateRangeSwitch.next), ), ], @@ -301,4 +413,57 @@ class _MoodRecordsScreenState }, ); } + + /////////////////////////////Build Heat Map Function ////////////////////////////// + Widget _createHeatMap(BuildContext context, + {required List moodTracks}) { + return Consumer(builder: (_, provider, __) { + final activeFilter = provider.activeFilter; + final dateRange = provider.activeDateRange; + final averageScoreForDay = calculateAverageMoodScores(moodTracks); + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: NepanikarDropdown( + items: HeatmapFilter.values, + activeItem: activeFilter, + labelBuilder: (item) => item.getLabel(context), + onPick: provider.setFilter, + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20), + child: MoodHeatmap( + heatmapType: activeFilter, + dateRange: dateRange, + moodScores: averageScoreForDay, + ), + ) + ], + ); + }); + } + + //////////////////////////////////////////////////////////////////////////////// + //Helper function to calculate average score for each requested day of date range + Map calculateAverageMoodScores(List moodTracks) { + final Map> dayToScores = {}; + // Group scores by date + for (final track in moodTracks) { + final date = DateTime(track.date.year, track.date.month, track.date.day); + final score = track.mood.getMoodScore(track.mood); + dayToScores.putIfAbsent(date, () => []).add(score); + } + + // Calculate average score for each date + final Map dayToAverageScore = {}; + dayToScores.forEach((date, scores) { + final averageScore = scores.reduce((a, b) => a + b) / scores.length; + dayToAverageScore[date] = averageScore.round(); + }); + + return dayToAverageScore; + } } diff --git a/lib/screens/home/my_records/mood/mood_track_screen.dart b/lib/screens/home/my_records/mood/mood_track_screen.dart index 41917267..4f29195c 100644 --- a/lib/screens/home/my_records/mood/mood_track_screen.dart +++ b/lib/screens/home/my_records/mood/mood_track_screen.dart @@ -5,11 +5,14 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; +import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/date_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/providers/mood_chart_filter_provider.dart'; +import 'package:nepanikar/screens/home/my_records/my_records_sleep_track_screen.dart'; import 'package:nepanikar/services/db/my_records/mood_track_dao.dart'; import 'package:nepanikar/services/db/my_records/mood_track_model.dart'; import 'package:nepanikar/services/notifications/notifications_service.dart'; @@ -71,6 +74,7 @@ class _MoodTrackScreenState extends State extends State extends State extends State>( stream: _trackDao.allMoodTracksStream, builder: (_, snapshot) { @@ -159,6 +167,20 @@ class _MoodTrackScreenState extends State allMoodTrackData, required DateTime? firstMoodTrackDate, }) { + final containerColor = customColorsBasedOnDarkMode( + context, + NepanikarColors.containerD, + NepanikarColors.white, + ); + + final arrowCanShiftColor = customColorsBasedOnDarkMode( + context, + NepanikarColors.white, + null, + ); + + final svgColor = svgColorBasedOnDarkMode(context); + return Consumer( builder: (_, moodChartFilterProvider, __) { final activeFilter = moodChartFilterProvider.activeFilter; @@ -200,7 +222,7 @@ class _MoodTrackScreenState extends State moodChartFilterProvider.shiftDateRange(DateRangeSwitch.previous), ), @@ -220,7 +242,7 @@ class _MoodTrackScreenState extends State extends StatefulWidget { State> createState() => _SearchMoodEntryState(); } -class _SearchMoodEntryState -extends State> { +class _SearchMoodEntryState extends State> { + + T get _trackDao => registry.get(); + + final TextEditingController _searchController = TextEditingController(); + + List _selectedEmotions = []; + List _emotions = []; + String _summaryToSearch = ''; + List _searchResults = []; + bool search = false; + + final multiSelectKey = GlobalKey(); + + + + void _onEmotionsUpdated(List updatedEmotions) { + setState(() { + _selectedEmotions = updatedEmotions; + }); + } + + void _search() async { + final results = await _trackDao.searchMoodTracks(_summaryToSearch, _selectedEmotions); + setState(() { + _searchResults = results; + }); + } + + @override + void dispose() { + // Dispose the controller when the state is disposed + _searchController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { + _emotions = Provider.of(context).emotions; + final items = _emotions + .map((emotion) => MultiSelectItem(emotion, emotion)) + .toList(); + + const pageHorizontalPadding = EdgeInsets.symmetric(horizontal: 24.0, vertical: 12); + + ValueKey uniqueKey = ValueKey(_selectedEmotions.length); + + + final textStyleColor = customColorsBasedOnDarkMode( + context, + NepanikarColors.white, + NepanikarColors.primaryD, + ); + final containerColor = customColorsBasedOnDarkMode( + context, NepanikarColors.containerD, NepanikarColors.white); return Scaffold( appBar: AppBar( title: Text( - "Search", + context.l10n.search, style: NepanikarFonts.title2.copyWith(color: NepanikarColors.white), ), ), - body: Column( - children: [ - - ], + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: pageHorizontalPadding, + child: Column( + key: ValueKey(_selectedEmotions.length), + children: [ + const SizedBox( + height: 20, + ), + TextFormField( + controller: _searchController, + decoration: InputDecoration( + labelText: context.l10n.search_by_summary, + labelStyle: TextStyle( + color: NepanikarColors.primarySwatch.shade400, + fontWeight: FontWeight.bold, + ), + fillColor: containerColor, + filled: true, + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide( + color: NepanikarColors.containerD, + ), + borderRadius: BorderRadius.circular(8.0), + ), + hintText: context.l10n.enter_part_of_summary, + hintStyle: TextStyle( + color: NepanikarColors.primarySwatch.shade400, + fontWeight: FontWeight.w500, + fontSize: 14 + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + ), + onChanged: (value) { + setState(() { + _summaryToSearch = value; + }); + }, + ), + const SizedBox( + height: 15, + ), + Container( + key: uniqueKey, + child: MultiSelectDialogField( + searchable: true, + items: items, + backgroundColor: containerColor, + title: Text(context.l10n.emotions), + decoration: BoxDecoration( + color: containerColor, + borderRadius: const BorderRadius.all(Radius.circular(40)), + border: Border.all( + color: NepanikarColors.containerD, + ), + ), + selectedColor: textStyleColor, + selectedItemsTextStyle: TextStyle( + color: textStyleColor, + ), + unselectedColor: textStyleColor, + itemsTextStyle: TextStyle( + color: textStyleColor, + ), + buttonIcon: Icon( + Icons.arrow_drop_down, + color: textStyleColor, + ), + buttonText: Text( + context.l10n.search_by_emotions, + style: TextStyle( + color: NepanikarColors.primarySwatch.shade400, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + onConfirm: (results) { + _onEmotionsUpdated(results.cast()); + }, + cancelText: Text( + context.l10n.cancel, + style: TextStyle( + color: textStyleColor, + fontSize: 16, + ), + ), + confirmText: Text( + context.l10n.submit, + style: TextStyle( + color: textStyleColor, + fontSize: 16, + ), + ), + initialValue: _selectedEmotions, + chipDisplay: MultiSelectChipDisplay.none(), + ), + ), + const SizedBox( + height: 5, + ), + ChosenEmotionsWidget( + initialEmotions: _selectedEmotions, + onEmotionsUpdated: _onEmotionsUpdated, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: pageHorizontalPadding, + width: 150, + height: 70, + child: ElevatedButton( + onPressed: () { + search = true; + FocusScope.of(context).unfocus(); + _search(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: NepanikarColors.containerD, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + ), + child: Text( + context.l10n.search, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), // Text color + ), + ), + ), + Container( + padding: pageHorizontalPadding, + width: 150, + height: 70, + child: ElevatedButton( + onPressed: (){ + setState(() { + _searchController.clear(); + _summaryToSearch = ''; + search = false; + _selectedEmotions.clear(); + FocusScope.of(context).unfocus(); + _searchResults.clear(); + }); + }, + style: ElevatedButton.styleFrom( + backgroundColor: NepanikarColors.deleteButton, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + ), + child: Text( + context.l10n.clear_button, + style: const TextStyle( + color: Colors.white, + fontSize: 14, + ), // Text color + ), + ), + ), + ], + ), + const SizedBox(height: 10,), + if (search) ... [ + if (_searchResults.isNotEmpty) ... [ + Text( + context.l10n.mood_entries, + style: NepanikarFonts.title2.copyWith(color: textStyleColor), + ), + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: _searchResults.length, // Use the length of the search results list + itemBuilder: (context, index) { + final moodEntry = _searchResults[index]; // Access the search result item + return Padding( + padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), + child: MoodEntryCard( + dateTime: DateFormat('d. MMM yyyy, HH:mm').format(moodEntry.date), + moodIcon: moodEntry.mood.icon, + moodDescription: moodEntry.summary ?? context.l10n.no_description_provided, + onTap: () { + Provider.of(context, listen: false).selectMoodEntry(moodEntry); + context.push(const MoodEntryDetailRoute().location); + }, + moodColor: null, + ), + ); + }, + ), + ] + else ... [ + Text( + context.l10n.no_mood_entries, + style: NepanikarFonts.title2.copyWith(color: textStyleColor), + ), + ] + ] + ], + ), + ), + ), ), ); } - -} \ No newline at end of file +} diff --git a/lib/screens/home/my_records/my_records_screen.dart b/lib/screens/home/my_records/my_records_screen.dart index a390ad47..1fb60a1a 100644 --- a/lib/screens/home/my_records/my_records_screen.dart +++ b/lib/screens/home/my_records/my_records_screen.dart @@ -5,6 +5,7 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/helpers/color_helpers.dart'; +import 'package:nepanikar/providers/mood_heatmap_filter_provider.dart'; import 'package:nepanikar/screens/home/my_records/diary/my_records_diary_records_screen.dart'; import 'package:nepanikar/screens/home/my_records/food_records/my_records_food_records_list_screen.dart'; import 'package:nepanikar/screens/home/my_records/journal/my_records_journal_records_screen.dart'; @@ -13,6 +14,7 @@ import 'package:nepanikar/screens/home/my_records/mood/mood_track_screen.dart'; import 'package:nepanikar/screens/home/my_records/my_records_sleep_track_screen.dart'; import 'package:nepanikar/widgets/long_tile.dart'; import 'package:nepanikar/widgets/nepanikar_screen_wrapper.dart'; +import 'package:provider/provider.dart'; class MyRecordsRoute extends GoRouteData { const MyRecordsRoute(); @@ -38,7 +40,10 @@ class MyRecordsScreen extends StatelessWidget { text: context.l10n.depression_mood, image: Assets.illustrations.modules.moodTracker.svg(color: svgColor), // onTap: () => context.push(const MoodTrackRoute().location), - onTap: () => context.push(const MoodRecordsRoute().location), + onTap: () { + Provider.of(context, listen: false).setFilter(HeatmapFilter.initial); + context.push(const MoodRecordsRoute().location); + }, isDarkMode: isDarkMode, ), LongTile( diff --git a/lib/screens/home/self_harm/self_harm_screen.dart b/lib/screens/home/self_harm/self_harm_screen.dart index 2d6fd012..94e6eb6c 100644 --- a/lib/screens/home/self_harm/self_harm_screen.dart +++ b/lib/screens/home/self_harm/self_harm_screen.dart @@ -4,6 +4,7 @@ import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/games/breathing/breathing_exercises_screen.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/screens/home/self_harm/self_harm_helped_screen.dart'; import 'package:nepanikar/screens/home/self_harm/self_harm_plan_screen.dart'; import 'package:nepanikar/screens/home/self_harm/self_harm_timer_screen.dart'; @@ -23,39 +24,37 @@ class SelfHarmScreen extends StatelessWidget { @override Widget build(BuildContext context) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final svgColor = svgColorBasedOnDarkMode(context); final modules = [ LongTile( text: context.l10n.self_harm_tips, - image: Assets.illustrations.modules.whatCanHelpMe.svg(), + image: Assets.illustrations.modules.whatCanHelpMe.svg(color: svgColor), onTap: () => context.push(const SelfHarmTipsRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.self_harm_helped, - image: Assets.illustrations.modules.whatHelpedMe.svg(), + image: Assets.illustrations.modules.whatHelpedMe.svg(color: svgColor), onTap: () => context.push(const SelfHarmHelpedRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.plan, - image: Assets.illustrations.modules.emergencyPlan.svg(), + image: Assets.illustrations.modules.emergencyPlan.svg(color: svgColor), onTap: () => context.push(const SelfHarmPlanRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.self_harm_timer, - image: Assets.illustrations.modules.successTracker.svg(), + image: Assets.illustrations.modules.successTracker.svg(color: svgColor), onTap: () => context.push(const SelfHarmTimerRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.breath, - image: Assets.illustrations.modules.breathing.svg(), + image: Assets.illustrations.modules.breathing.svg(color: svgColor), onTap: () => context.push(const BreathingExercisesRoute().location), isDarkMode: isDarkMode, ), diff --git a/lib/screens/home/self_harm/self_harm_timer_screen.dart b/lib/screens/home/self_harm/self_harm_timer_screen.dart index c7198107..52a60d18 100644 --- a/lib/screens/home/self_harm/self_harm_timer_screen.dart +++ b/lib/screens/home/self_harm/self_harm_timer_screen.dart @@ -7,6 +7,7 @@ import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/localization_helpers.dart'; import 'package:nepanikar/screens/main/contacts_screen.dart'; import 'package:nepanikar/services/db/self_harm/self_harm_timer_dao.dart'; @@ -32,7 +33,8 @@ class SelfHarmTimerScreen extends StatelessWidget { DateTime get _now => DateTime.now(); - String _getMotivationTitle(BuildContext context, Duration diffFromStartDateTime) { + String _getMotivationTitle(BuildContext context, + Duration diffFromStartDateTime) { final l10n = context.l10n; if (diffFromStartDateTime.inMinutes < 60) { return l10n.self_harm_timer_begin; @@ -56,8 +58,10 @@ class SelfHarmTimerScreen extends StatelessWidget { @override Widget build(BuildContext context) { final analytics = registry.get(); + final cardColor = customColorsBasedOnDarkMode(context, NepanikarColors.containerD, null); + final backgroundColor = customColorsBasedOnDarkMode(context, NepanikarColors.primaryD, NepanikarColors.primary); return Scaffold( - backgroundColor: NepanikarColors.primary, + backgroundColor: backgroundColor, appBar: AppBar(title: Text(context.l10n.self_harm_timer)), body: SafeArea( child: Padding( @@ -66,19 +70,23 @@ class SelfHarmTimerScreen extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Card( + color: cardColor, child: Padding( padding: const EdgeInsets.all(24), child: TimerBuilder.periodic( const Duration(seconds: 30), builder: (_) { return StreamBuilder( - stream: _selfHarmTimerDao.selfHarmTimerStartDateTimeStream, + stream: _selfHarmTimerDao + .selfHarmTimerStartDateTimeStream, builder: (_, snapshot) { final startDateTime = snapshot.data; final isTimerRunning = startDateTime != null; bool showSecsTimer() { final durationInSec = - isTimerRunning ? _now.difference(startDateTime).inSeconds : null; + isTimerRunning ? _now + .difference(startDateTime) + .inSeconds : null; return durationInSec != null && durationInSec < 60; } @@ -87,7 +95,9 @@ class SelfHarmTimerScreen extends StatelessWidget { children: [ if (isTimerRunning) ...[ _buildCardTitle( - _getMotivationTitle(context, _now.difference(startDateTime)), + _getMotivationTitle( + context, _now.difference(startDateTime)), + context, ), const Padding( padding: EdgeInsets.symmetric(vertical: 16), @@ -100,7 +110,9 @@ class SelfHarmTimerScreen extends StatelessWidget { return _buildTimeTextSection( context, dateTimeRange: isTimerRunning - ? DateTimeRange(start: startDateTime.toLocal(), end: _now) + ? DateTimeRange( + start: startDateTime.toLocal(), + end: _now) : null, showSeconds: showSecsTimer(), ); @@ -110,8 +122,10 @@ class SelfHarmTimerScreen extends StatelessWidget { if (!isTimerRunning) NepanikarButton( onTap: () async { - await _selfHarmTimerDao.startSelfHarmTimer(); - unawaited(analytics.logEvent(name: 'start_self_harm_timer')); + await _selfHarmTimerDao + .startSelfHarmTimer(); + unawaited(analytics.logEvent( + name: 'start_self_harm_timer')); }, expandToContentWidth: true, text: context.l10n.start, @@ -124,24 +138,33 @@ class SelfHarmTimerScreen extends StatelessWidget { onPrimaryBtnTap: (context) async { final goRouter = GoRouter.of(context); final l10n = context.l10n; - await _selfHarmTimerDao.stopSelfHarmTimer(); - await _selfHarmTimerDao.startSelfHarmTimer(); + await _selfHarmTimerDao + .stopSelfHarmTimer(); + await _selfHarmTimerDao + .startSelfHarmTimer(); unawaited( - analytics.logEvent(name: 'restart_self_harm_timer'), + analytics.logEvent( + name: 'restart_self_harm_timer'), ); if (context.mounted) { context.showOkCancelNepanikarDialog( text: l10n.need_help, onPrimaryBtnTap: (context) => - goRouter.push(const ContactsRoute().location), + goRouter.push( + const ContactsRoute() + .location), primaryBtnLabel: l10n.mood_help_yes, - secondaryBtnLabel: l10n.mood_help_no, - defaultAction: DialogDefaultAction.both, + secondaryBtnLabel: l10n + .mood_help_no, + defaultAction: DialogDefaultAction + .both, ); } }, - primaryBtnLabel: context.l10n.mood_help_yes, - secondaryBtnLabel: context.l10n.mood_help_no, + primaryBtnLabel: context.l10n + .mood_help_yes, + secondaryBtnLabel: context.l10n + .mood_help_no, ); }, expandToContentWidth: true, @@ -157,12 +180,16 @@ class SelfHarmTimerScreen extends StatelessWidget { ), const SizedBox(height: 24), Card( + color: cardColor, child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ - _buildCardTitle(context.l10n.self_harm_record), + _buildCardTitle( + context.l10n.self_harm_record, + context, + ), const Padding( padding: EdgeInsets.symmetric(vertical: 16), child: NepanikarHorizontalDivider(), @@ -170,7 +197,8 @@ class SelfHarmTimerScreen extends StatelessWidget { StreamBuilder( stream: _selfHarmTimerDao.selfHarmTimerRecordStream, builder: (_, snapshot) { - return _buildTimeTextSection(context, dateTimeRange: snapshot.data); + return _buildTimeTextSection( + context, dateTimeRange: snapshot.data); }, ), ], @@ -184,26 +212,28 @@ class SelfHarmTimerScreen extends StatelessWidget { ); } - Widget _buildCardTitle(String text) { + Widget _buildCardTitle(String text, BuildContext context) { + final textColor = customColorsBasedOnDarkMode(context, NepanikarColors.white, NepanikarColors.dark); return Align( child: Text( text, textAlign: TextAlign.center, style: NepanikarFonts.title3.copyWith( fontWeight: FontWeight.w900, - color: NepanikarColors.dark, + color: textColor, ), ), ); } - Widget _buildTimeTextSection( - BuildContext context, { + Widget _buildTimeTextSection(BuildContext context, { DateTimeRange? dateTimeRange, bool showSeconds = false, }) { - final valueTextStyle = NepanikarFonts.title2.copyWith(color: NepanikarColors.primary); - final labelTextStyle = NepanikarFonts.bodyRoman.copyWith(color: NepanikarColors.primary); + final textColor = customColorsBasedOnDarkMode( + context, NepanikarColors.white, NepanikarColors.primary); + final valueTextStyle = NepanikarFonts.title2.copyWith(color: textColor); + final labelTextStyle = NepanikarFonts.bodyRoman.copyWith(color: textColor); Widget buildTextSpan({ required String value, @@ -216,7 +246,9 @@ class SelfHarmTimerScreen extends StatelessWidget { TextSpan(text: value, style: valueTextStyle), TextSpan( text: ' $label', - style: labelStyleSameAsValueStyle ? valueTextStyle : labelTextStyle, + style: labelStyleSameAsValueStyle + ? valueTextStyle + : labelTextStyle, ), ], ), diff --git a/lib/screens/home/self_harm/self_harm_tips_screen.dart b/lib/screens/home/self_harm/self_harm_tips_screen.dart index f47e59b2..d68f2f05 100644 --- a/lib/screens/home/self_harm/self_harm_tips_screen.dart +++ b/lib/screens/home/self_harm/self_harm_tips_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/widgets/tips_carousel_body.dart'; class SelfHarmTipsRoute extends GoRouteData { @@ -16,6 +17,7 @@ class SelfHarmTipsScreen extends StatelessWidget { @override Widget build(BuildContext context) { final strings = context.l10n.self_harm_tips_13.split('\n'); + final textColor = textColorBasedOnDarkMode(context); return Scaffold( appBar: AppBar(title: Text(context.l10n.depression_help)), @@ -33,6 +35,7 @@ class SelfHarmTipsScreen extends StatelessWidget { textAlign: TextAlign.center, style: NepanikarFonts.title2.copyWith( fontWeight: FontWeight.w900, + color: textColor, ), ), const SizedBox( @@ -41,7 +44,9 @@ class SelfHarmTipsScreen extends StatelessWidget { Text( strings.elementAt(index * 2 + 1), textAlign: TextAlign.center, - style: NepanikarFonts.bodyRoman, + style: NepanikarFonts.bodyRoman.copyWith( + color: textColor, + ), ), ], ), diff --git a/lib/screens/home/suicidal_thoughts/suicidal_thoughts_screen.dart b/lib/screens/home/suicidal_thoughts/suicidal_thoughts_screen.dart index feae7f81..e707a0be 100644 --- a/lib/screens/home/suicidal_thoughts/suicidal_thoughts_screen.dart +++ b/lib/screens/home/suicidal_thoughts/suicidal_thoughts_screen.dart @@ -4,6 +4,7 @@ import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/games/breathing/breathing_exercises_screen.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/screens/home/suicidal_thoughts/suicidal_thoughts_plan_screen.dart'; import 'package:nepanikar/screens/home/suicidal_thoughts/suicidal_thoughts_reasons_no_screen.dart'; import 'package:nepanikar/widgets/long_tile.dart'; @@ -21,27 +22,25 @@ class SuicidalThoughtsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final svgColor = svgColorBasedOnDarkMode(context); + final isDarkMode = Theme.of(context).brightness == Brightness.dark; final modules = [ LongTile( text: context.l10n.plan, - image: Assets.illustrations.modules.emergencyPlan.svg(), + image: Assets.illustrations.modules.emergencyPlan.svg(color: svgColor), onTap: () => context.push(const SuicidalThoughtsPlanRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.reasons, - image: Assets.illustrations.modules.reaseonsWhyNot.svg(), + image: Assets.illustrations.modules.reaseonsWhyNot.svg(color: svgColor), onTap: () => context.push(const SuicidalThoughtsReasonsNoRoute().location), isDarkMode: isDarkMode, ), LongTile( text: context.l10n.breath, - image: Assets.illustrations.modules.breathing.svg(), + image: Assets.illustrations.modules.breathing.svg(color: svgColor), onTap: () => context.push(const BreathingExercisesRoute().location), isDarkMode: isDarkMode, ), diff --git a/lib/screens/main/main_screen.dart b/lib/screens/main/main_screen.dart index 57251873..8df10035 100644 --- a/lib/screens/main/main_screen.dart +++ b/lib/screens/main/main_screen.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/colors.dart'; +import 'package:nepanikar/providers/mood_state_provider.dart'; import 'package:nepanikar/screens/home/my_records/my_records_screen.dart'; import 'package:nepanikar/screens/main/contacts_screen.dart'; import 'package:nepanikar/screens/main/home_screen.dart'; @@ -12,7 +14,7 @@ import 'package:nepanikar/services/db/user_settings/user_settings_dao.dart'; import 'package:nepanikar/utils/contacts_data_manager.dart'; import 'package:nepanikar/utils/registry.dart'; import 'package:nepanikar/widgets/bottom_navbar_item.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; class MainPageExtra { @@ -61,6 +63,8 @@ class _MainScreenState extends State { void initState() { super.initState(); FlutterNativeSplash.remove(); + final moodState = Provider.of(context, listen: false); + moodState.init(); setState(() { _selectedIndex = widget.extra?.initIndex ?? _selectedIndex; }); @@ -127,11 +131,7 @@ class _MainScreenState extends State { currentIndex: _selectedIndex, showUnselectedLabels: true, type: BottomNavigationBarType.fixed, - //backgroundColor: const Color(0xAAFAF4FF), - backgroundColor: isDarkMode ? NepanikarColors.containerD : const Color(0xAAFAF4FF), elevation: 0, - //selectedItemColor: NepanikarColors.primarySwatch.shade800, - //unselectedItemColor: NepanikarColors.primarySwatch.shade700, onTap: _onItemTapped, ), ); diff --git a/lib/screens/main/settings_screen.dart b/lib/screens/main/settings_screen.dart index f0ec3896..01284e92 100644 --- a/lib/screens/main/settings_screen.dart +++ b/lib/screens/main/settings_screen.dart @@ -38,7 +38,8 @@ class _SettingsScreenState extends State { DatabaseService get _databaseService => registry.get(); - NotificationsService get _notificationsService => registry.get(); + NotificationsService get _notificationsService => + registry.get(); @override Widget build(BuildContext context) { @@ -48,9 +49,10 @@ class _SettingsScreenState extends State { final svgColor = svgColorBasedOnDarkMode(context); final pdfColor = pdfColorBasedOnDarkMode(context); final colorForDarkModeButton = customColorsBasedOnDarkMode( - context, - NepanikarColors.white, - NepanikarColors.primary,); + context, + NepanikarColors.white, + NepanikarColors.primary, + ); return NepanikarScreenWrapper( appBarTitle: context.l10n.settings, @@ -63,8 +65,9 @@ class _SettingsScreenState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(16), child: Material( - color: isDarkMode ? - NepanikarColors.containerD : NepanikarColors.white, + color: isDarkMode + ? NepanikarColors.containerD + : NepanikarColors.white, borderRadius: BorderRadius.circular(16), child: Column( children: [ @@ -87,12 +90,14 @@ class _SettingsScreenState extends State { final l10n = context.l10n; await _databaseService.clearAll(); await _databaseService.preloadDefaultData(l10n); - await _notificationsService.cancelAllScheduledNotifications(); + await _notificationsService + .cancelAllScheduledNotifications(); if (mounted) { context.hideCurrentSnackBar(); context.showSuccessSnackbar( text: context.l10n.delete_success, - leading: Assets.icons.checkmarks.checkCircular.svg(), + leading: + Assets.icons.checkmarks.checkCircular.svg(), ); } }, @@ -116,15 +121,10 @@ class _SettingsScreenState extends State { _SettingsMenuItem( leading: Assets.icons.donate.svg(color: svgColor), text: context.l10n.support_us, - onTap: () async { - final url = Uri.parse( - 'https://www.darujme.cz/projekt/1203622', - ); - await launchUrl(url, mode: LaunchMode.externalApplication); - }, + onTap: () => launchUrLink(AppConstants.nepanikarDonate) ), _SettingsMenuItem( - leading: Assets.icons.exportData.svg(color: svgColor), + leading: Assets.icons.exportData.svg(color: svgColor), onTap: () { context.push(const ExportRoute().location); }, @@ -142,22 +142,25 @@ class _SettingsScreenState extends State { ), _SettingsMenuItem( leading: Icon( - isDarkMode ? Icons.wb_sunny : Icons.brightness_3, - color: colorForDarkModeButton,), - text: isDarkMode ? "Dark Mode is ON" : "Dark Mode is OFF", - onTap: () async{ - final newThemeMode = isDarkMode ? ThemeMode.light : ThemeMode.dark; + isDarkMode ? Icons.wb_sunny : Icons.brightness_3, + color: colorForDarkModeButton, + ), + text: isDarkMode + ? context.l10n.dark_mode_on : context.l10n.dark_mode_off, + onTap: () async { + final newThemeMode = + isDarkMode ? ThemeMode.light : ThemeMode.dark; await userSettingsDao.saveThemeMode(newThemeMode); setState(() { isDarkMode = !isDarkMode; }); }, - ), _SettingsMenuItem( leading: Icon( Icons.shield_outlined, - color: isDarkMode ? Colors.white : NepanikarColors.primary, + color: + isDarkMode ? Colors.white : NepanikarColors.primary, ), text: context.l10n.support, onTap: () => context.push(const SponsorsRoute().location), @@ -165,24 +168,29 @@ class _SettingsScreenState extends State { Container( decoration: BoxDecoration( border: Border( - top: isDarkMode ? BorderSide(color: NepanikarColors.primarySwatch.shade700) : BorderSide(color: Color(0xffF2F2F5)), + top: isDarkMode + ? BorderSide( + color: NepanikarColors.primarySwatch.shade700) + : BorderSide(color: Color(0xffF2F2F5)), ), ), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16), + padding: const EdgeInsets.symmetric( + horizontal: 20.0, vertical: 16), child: Row( children: [ ExcludeSemantics( child: Text( context.l10n.follow_us, - style: NepanikarFonts.bodySmallMedium.copyWith(fontSize: 15, color: svgColor), - + style: NepanikarFonts.bodySmallMedium + .copyWith(fontSize: 15, color: svgColor), ), ), const Spacer(), SemanticsWidgetButton( label: '${context.l10n.follow_us}: Web', - onTap: () => launchUrLink(AppConstants.nepanikarWeb), + onTap: () => + launchUrLink(AppConstants.nepanikarWeb), child: Assets.icons.globe.svg(color: svgColor), ), const SizedBox(width: 27), @@ -246,7 +254,6 @@ class _SettingsMenuItem extends StatelessWidget { @override Widget build(BuildContext context) { - final currentTheme = Theme.of(context); final isDarkMode = currentTheme.brightness == Brightness.dark; final textColor = svgColorBasedOnDarkMode(context); @@ -254,8 +261,13 @@ class _SettingsMenuItem extends StatelessWidget { return Container( decoration: BoxDecoration( border: Border( - top: isDarkMode ? (hideTopSeparator ? BorderSide.none : BorderSide(color: NepanikarColors.primarySwatch.shade700)) - : (hideTopSeparator ? BorderSide.none : const BorderSide(color: Color(0xffF2F2F5))), + top: isDarkMode + ? (hideTopSeparator + ? BorderSide.none + : BorderSide(color: NepanikarColors.primarySwatch.shade700)) + : (hideTopSeparator + ? BorderSide.none + : const BorderSide(color: Color(0xffF2F2F5))), ), ), child: InkWell( @@ -277,7 +289,8 @@ class _SettingsMenuItem extends StatelessWidget { Flexible( child: Text( text, - style: NepanikarFonts.bodySmallMedium.copyWith(fontSize: 15, color: textColor), + style: NepanikarFonts.bodySmallMedium + .copyWith(fontSize: 15, color: textColor), ), ), ], @@ -290,7 +303,7 @@ class _SettingsMenuItem extends StatelessWidget { color: isDarkMode ? Colors.white : const Color(0xffCDD1D5), ), ), - if(trailing != null) trailing!, + if (trailing != null) trailing!, ], ), ), diff --git a/lib/services/db/database_service.dart b/lib/services/db/database_service.dart index cee9a12c..77b63f26 100644 --- a/lib/services/db/database_service.dart +++ b/lib/services/db/database_service.dart @@ -314,6 +314,7 @@ class DatabaseService { } } + // Clear old config files so that this migration is not done somehow again. await _clearAndBackupOldAppConfigData(androidConfigFile: androidConfigFile); } diff --git a/lib/services/db/my_records/mood_track_dao.dart b/lib/services/db/my_records/mood_track_dao.dart index 46151c43..7984aedd 100644 --- a/lib/services/db/my_records/mood_track_dao.dart +++ b/lib/services/db/my_records/mood_track_dao.dart @@ -34,7 +34,7 @@ class MoodTrackDao with CustomFilters { static const _storeKeyName = 'my_records_mood_track'; - Future saveMood(Mood mood, List? emotions, String summary, + Future saveMoodTrack(Mood mood, List? emotions, String summary, String? description) async { final dateTimeToSave = DateTime.now(); final date = DateTime.utc( @@ -61,6 +61,55 @@ class MoodTrackDao with CustomFilters { await _store.addAll(_db, items.map((m) => m.toJson()).toList()); } + Future saveSleepTrack(Mood mood) async { + final dateTimeToSave = DateTime.now(); + final date = DateTime.utc( + dateTimeToSave.year, dateTimeToSave.month, dateTimeToSave.day); + final moodTrack = MoodTrack(mood: mood, date: date); + final json = moodTrack.toJson(); + await _store + .findFirst(_db, finder: Finder(filter: getDateEqualsFilter(date))) + .then((record) async { + if (record == null) { + debugPrint( + 'MoodTrackDao: Not found mood for today - adding new: $json'); + await _store.add(_db, json); + } else { + debugPrint('MoodTrackDao: Found for today, updating to: $json'); + await _store.record(record.key).put(_db, json); + } + }); + } + + Future updateMoodTrack(DateTime date, String summary, + String description, List emotions, Mood mood,) async { + try { + final dateString = date.toIso8601String(); + + final recordFinder = Finder(filter: Filter.equals('date', dateString)); + final existingRecord = await _store.findFirst(_db, finder: recordFinder); + + if (existingRecord != null) { + final updatedMoodTrack = MoodTrack( + mood: mood, + date: date, + emotions: emotions, + summary: summary, + description: description, + ); + final json = updatedMoodTrack.toJson(); + + await _store.record(existingRecord.key).update(_db, json); + debugPrint('MoodTrackDao: Updated mood track: $json'); + } else { + debugPrint('MoodTrackDao: No record found to update for date: $dateString'); + } + } catch (e) { + debugPrint('Error updating mood track: $e'); + rethrow; + } + } + Stream> get allMoodTracksStream => _store .query(finder: Finder(sortOrders: [SortOrder(FilterKeys.date)])) .onSnapshots(_db) @@ -124,59 +173,34 @@ class MoodTrackDao with CustomFilters { return moodTracks.length; } - Future addEmotion(String emotion) async { - final emotions = await getEmotions(); - var mutableEmotionsList = List.from(emotions.cast()); - if (!mutableEmotionsList.contains(emotion)) { - mutableEmotionsList.add(emotion); - await _store.record(emotionsKey).put(_db, {'emotions': mutableEmotionsList}); - debugPrint('MoodTrackDao: Added new mood track: $emotion'); - return true; - } - return false; - } - - Future initEmotions() async { - final existingEmotions = await getEmotions(); - if (existingEmotions.isEmpty) { - for (final String emotion in defaultEmotions) { - await addEmotion(emotion); - } - } - } + Future> searchMoodTracks( + String summary, List emotions) async { + final filters = []; - Future> getEmotions() async { - final record = await _store.record(emotionsKey).get(_db); - if (record is Map) { - final emotionsList = record['emotions']; - if (emotionsList is List) { - return List.from(emotionsList.cast()); - } + if (summary.isNotEmpty) { + filters.add(Filter.matches('summary', summary)); } - return []; - } - //TODO urobit odstranovanie a updatovanie emocii + case ked su emocie pouzivane - Future deleteEmotion() async { - final record = await _store.record(emotionsKey).get(_db); - final emotionsList = await getEmotions(); - var mutableEmotionsList = List.from(emotionsList.cast()); - final emotion = "Ridiculous"; - - if (mutableEmotionsList.contains(emotion)) { - mutableEmotionsList.remove(emotion); - await _store - .record(emotionsKey) - .put(_db, {'emotions': mutableEmotionsList}); + if (emotions.isNotEmpty) { + filters.add( + Filter.custom((record) { + final recordEmotions = + (record['emotions'] as List?)?.cast() ?? []; + return emotions.every(recordEmotions.contains); + }), + ); } - } - Future deleteRecord(int key) async { - final finder = Finder(filter: Filter.byKey(key)); - await _store.delete( - _db, - finder: finder, + final finder = Finder( + filter: filters.isNotEmpty ? Filter.and(filters) : null, + sortOrders: [SortOrder('date', false)], ); + + final snapshots = await _store.find(_db, finder: finder); + + return snapshots + .map((snapshot) => MoodTrack.fromJson(snapshot.value)) + .toList(); } Future doOldVersionMigration( @@ -209,6 +233,22 @@ class MoodTrackDao with CustomFilters { } } + Future deleteMoodTracksWithNoDescriptionOrSummary() async { + final filter = Filter.or([ + Filter.isNull('description'), + Filter.isNull('summary'), + ]); + + final finder = Finder(filter: filter); + + await _store.delete( + _db, + finder: finder, + ); + + debugPrint('Deleted MoodTracks with null description or summary'); + } + Future deleteMoodTrackByDate(DateTime date) async { final finder = Finder(filter: Filter.equals('date', date.toIso8601String())); diff --git a/lib/services/db/my_records/mood_track_model.dart b/lib/services/db/my_records/mood_track_model.dart index 685b336f..ecf28b0a 100644 --- a/lib/services/db/my_records/mood_track_model.dart +++ b/lib/services/db/my_records/mood_track_model.dart @@ -76,6 +76,23 @@ enum Mood { } } + int getMoodScore(Mood mood){ + switch (mood) { + case Mood.sad: + return 1; + case Mood.bad: + return 2; + case Mood.okay: + return 3; + case Mood.good: + return 4; + case Mood.happy: + return 5; + default: + return 0; // Or throw an exception as appropriate + } + } + static Mood? fromInteger(int value) => Mood.values.safeElementAtOrNull(value); } diff --git a/lib/services/db/my_records/my_records_sleep_track_dao.dart b/lib/services/db/my_records/my_records_sleep_track_dao.dart index 96dbe57a..eefb3933 100644 --- a/lib/services/db/my_records/my_records_sleep_track_dao.dart +++ b/lib/services/db/my_records/my_records_sleep_track_dao.dart @@ -1,6 +1,8 @@ import 'package:nepanikar/services/db/my_records/mood_track_dao.dart'; import 'package:nepanikar/utils/registry.dart'; +import 'mood_track_model.dart'; + class MyRecordsSleepTrackDao extends MoodTrackDao { MyRecordsSleepTrackDao({ required super.dbService, diff --git a/lib/widgets/contacts/chat_contact_tile.dart b/lib/widgets/contacts/chat_contact_tile.dart index bde504ed..52e04d61 100644 --- a/lib/widgets/contacts/chat_contact_tile.dart +++ b/lib/widgets/contacts/chat_contact_tile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/contact_action_helpers.dart'; import 'package:nepanikar/widgets/long_tile.dart'; import 'package:nepanikar_contacts_gen/nepanikar_contacts_gen.dart'; @@ -32,11 +33,8 @@ class ChatContactTile extends StatelessWidget { } Widget _buildSubListContact(BuildContext context, ChatContactSubList contact) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; - + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final textColor = textColorBasedOnDarkMode(context); final subContactsLength = contact.subChatContacts.length; final isSingleSubList = subContactsLength == 1; return GestureDetector( @@ -47,10 +45,10 @@ class ChatContactTile extends StatelessWidget { child: LongTile( isDarkMode: isDarkMode, text: contact.title, - textTextStyle: _textTextStyle, + textTextStyle: _textTextStyle.copyWith(color: textColor), description: contact.subtitle, - descriptionTextStyle: _descriptionChatTextStyle, - image: ExcludeSemantics(child: Assets.illustrations.contacts.chat.svg()), + descriptionTextStyle: _descriptionChatTextStyle.copyWith(color: textColor), + image: ExcludeSemantics(child: Assets.illustrations.contacts.chat.svg(color: textColor)), trailing: const SizedBox.shrink(), onTap: null, subContent: Column( @@ -77,7 +75,7 @@ class ChatContactTile extends StatelessWidget { flex: 3, child: Text( _getShortUrlLink(subContact.url), - style: _charUrlTextStyle, + style: _charUrlTextStyle.copyWith(color: textColor), ), ), ], diff --git a/lib/widgets/contacts/my_contact_tile.dart b/lib/widgets/contacts/my_contact_tile.dart index a0bb774d..3dbb6d42 100644 --- a/lib/widgets/contacts/my_contact_tile.dart +++ b/lib/widgets/contacts/my_contact_tile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/colors.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/contact_action_helpers.dart'; import 'package:nepanikar/helpers/screen_resolution_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; @@ -58,6 +59,8 @@ class _MyContactTileState extends State { @override Widget build(BuildContext context) { + final tileColor = customColorsBasedOnDarkMode(context, NepanikarColors.containerD, null); + final textColor = textColorBasedOnDarkMode(context); final buttonStyle = TextButton.styleFrom( textStyle: const TextStyle( fontWeight: FontWeight.bold, @@ -66,6 +69,7 @@ class _MyContactTileState extends State { ); return Card( + color: tileColor, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), child: Column( @@ -76,7 +80,7 @@ class _MyContactTileState extends State { children: [ Padding( padding: const EdgeInsets.only(top: 12), - child: ExcludeSemantics(child: Assets.icons.userCircle.svg()), + child: ExcludeSemantics(child: Assets.icons.userCircle.svg(color: textColor)), ), Expanded( child: Column( @@ -133,12 +137,22 @@ class _MyContactTileState extends State { children: [ TextButton( style: buttonStyle, - child: Text(context.l10n.send_sms), + child: Text( + context.l10n.send_sms, + style: TextStyle( + color: textColor, + ), + ), onPressed: () => launchSmsNum(_contactAddressController.text), ), TextButton( style: buttonStyle, - child: Text(context.l10n.make_call), + child: Text( + context.l10n.make_call, + style: TextStyle( + color: textColor, + ), + ), onPressed: () => launchPhoneNum(_contactAddressController.text), ), ], diff --git a/lib/widgets/contacts/phone_contact_tile.dart b/lib/widgets/contacts/phone_contact_tile.dart index ffb5e61c..8321fcd6 100644 --- a/lib/widgets/contacts/phone_contact_tile.dart +++ b/lib/widgets/contacts/phone_contact_tile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/contact_action_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/widgets/long_tile.dart'; @@ -32,13 +33,11 @@ class PhoneContactTile extends StatelessWidget { } Widget _buildSingleContact(BuildContext context, PhoneContactSingle contact) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final textColorMode = textColorBasedOnDarkMode(context); final isPinned = contact.pinned; - final textColor = isPinned ? Colors.white : null; + final textColor = isPinned ? Colors.white : textColorMode; final isUrl = contact.tel.contains('http'); return LongTile( isDarkMode: isDarkMode, @@ -59,11 +58,8 @@ class PhoneContactTile extends StatelessWidget { } Widget _buildSubListContact(BuildContext context, PhoneContactSubList contact) { - - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - bool isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; - + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final textColorMode = textColorBasedOnDarkMode(context); final subContactsLength = contact.subPhoneContacts.length; final isSingleSubList = subContactsLength == 1; return GestureDetector( @@ -76,10 +72,10 @@ class PhoneContactTile extends StatelessWidget { child: LongTile( isDarkMode: isDarkMode, text: contact.title, - textTextStyle: _textTextStyle, + textTextStyle: _textTextStyle.copyWith(color: textColorMode), description: contact.subtitle, - descriptionTextStyle: _descriptionNumTextStyle, - image: ExcludeSemantics(child: Assets.illustrations.contacts.phones.svg()), + descriptionTextStyle: _descriptionNumTextStyle.copyWith(color: textColorMode), + image: ExcludeSemantics(child: Assets.illustrations.contacts.phones.svg(color: textColorMode)), trailing: const SizedBox.shrink(), onTap: null, subContent: Column( @@ -97,7 +93,7 @@ class PhoneContactTile extends StatelessWidget { : EdgeInsets.zero, child: Text( subContact.title, - style: Theme.of(context).textTheme.bodyMedium, + style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: textColorMode), ), ), ), @@ -105,7 +101,7 @@ class PhoneContactTile extends StatelessWidget { child: Text( subContact.tel, semanticsLabel: isUrl ? null : subContact.tel.spellOutNumFormat, - style: _phoneNumTextStyle, + style: _phoneNumTextStyle.copyWith(color: textColorMode), ), ), ], diff --git a/lib/widgets/contacts/region_item_contacts_list.dart b/lib/widgets/contacts/region_item_contacts_list.dart index 5a91122c..f8e80a64 100644 --- a/lib/widgets/contacts/region_item_contacts_list.dart +++ b/lib/widgets/contacts/region_item_contacts_list.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:linkify/linkify.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/contact_action_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/utils/custom_linkifiers.dart'; @@ -16,11 +17,11 @@ class RegionItemContactsList extends StatelessWidget { final RegionItemContact regionItemContact; - Widget _buildUniversityHeader() { + Widget _buildUniversityHeader(Color? textColor) { return Text( regionItemContact.name, style: NepanikarFonts.title3.copyWith( - color: NepanikarColors.primary, + color: textColor, fontWeight: FontWeight.w700, ), ); @@ -28,8 +29,9 @@ class RegionItemContactsList extends StatelessWidget { @override Widget build(BuildContext context) { + final textColor = textColorBasedOnDarkMode(context); final linkifiedTextStyle = NepanikarFonts.bodyBlack.copyWith( - color: NepanikarColors.primary, + color: textColor, decoration: TextDecoration.underline, ); return SelectionArea( @@ -37,7 +39,7 @@ class RegionItemContactsList extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - _buildUniversityHeader(), + _buildUniversityHeader(textColor), ...regionItemContact.contactAddresses.map((contact) { final linkifiedText = linkify( contact, diff --git a/lib/widgets/diary/diary_edit_content.dart b/lib/widgets/diary/diary_edit_content.dart index 94c78626..566f7d0a 100644 --- a/lib/widgets/diary/diary_edit_content.dart +++ b/lib/widgets/diary/diary_edit_content.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/screens/home/my_records/diary/my_records_diary_detail_screen.dart'; import 'package:nepanikar/services/db/my_records/diary/diary_record_model.dart'; import 'package:nepanikar/widgets/nepanikar_button.dart'; @@ -62,7 +63,11 @@ class _DiaryEditContentState extends State { @override Widget build(BuildContext context) { - final labelTextStyle = NepanikarFonts.bodySmallHeavy.copyWith(fontWeight: FontWeight.w700); + final textColor = textColorBasedOnDarkMode(context); + final labelTextStyle = NepanikarFonts.bodySmallHeavy.copyWith( + fontWeight: FontWeight.w700, + color: textColor, + ); return GestureDetector( onTapDown: (_) => FocusScope.of(context).unfocus(), diff --git a/lib/widgets/diary/diary_tile.dart b/lib/widgets/diary/diary_tile.dart index 2554053d..76d9d99a 100644 --- a/lib/widgets/diary/diary_tile.dart +++ b/lib/widgets/diary/diary_tile.dart @@ -23,6 +23,7 @@ class DiaryTile extends StatelessWidget { @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final svgColor = svgColorBasedOnDarkMode(context); final longTileColor = customColorsBasedOnDarkMode(context, NepanikarColors.containerD, NepanikarColors.primarySwatch.shade700); const textStyle = NepanikarFonts.bodyHeavy; @@ -30,7 +31,7 @@ class DiaryTile extends StatelessWidget { return LongTile( isDarkMode: isDarkMode, - image: Assets.illustrations.modules.myRecords.svg(), + image: Assets.illustrations.modules.myRecords.svg(color: svgColor), text: DateFormat.yMd(locale.languageCode).format(date), textTextStyle: textStyle.copyWith( fontSize: 12, diff --git a/lib/widgets/heatmap/data/heatmap_color.dart b/lib/widgets/heatmap/data/heatmap_color.dart new file mode 100644 index 00000000..f70eee3a --- /dev/null +++ b/lib/widgets/heatmap/data/heatmap_color.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; + +class HeatMapColor { + /// Default background color of every block. + static const Color defaultColor = Color(0xFFF8F9FA); +} diff --git a/lib/widgets/heatmap/data/heatmap_color_mode.dart b/lib/widgets/heatmap/data/heatmap_color_mode.dart new file mode 100644 index 00000000..30effe98 --- /dev/null +++ b/lib/widgets/heatmap/data/heatmap_color_mode.dart @@ -0,0 +1,4 @@ +enum ColorMode { + opacity, + color, +} diff --git a/lib/widgets/heatmap/heatmap.dart b/lib/widgets/heatmap/heatmap.dart new file mode 100644 index 00000000..f95f621f --- /dev/null +++ b/lib/widgets/heatmap/heatmap.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import './widget/heatmap_page.dart'; +import './widget/heatmap_color_tip.dart'; +import './data/heatmap_color_mode.dart'; +import './util/date_util.dart'; + +class HeatMap extends StatefulWidget { + /// The Date value of start day of heatmap. + /// + /// HeatMap shows the start day of [startDate]'s week. + /// + /// Default value is 1 year before of the [endDate]. + /// And if [endDate] is null, then set 1 year before of the [DateTime.now]. + final DateTime? startDate; + + /// The Date value of end day of heatmap. + /// + /// Default value is [DateTime.now]. + final DateTime? endDate; + + /// The datasets which fill blocks based on its value. + final Map? datasets; + + /// The color value of every block's default color. + final Color? defaultColor; + + /// The text color value of every blocks. + final Color? textColor; + + /// The double value of every block's size. + final double? size; + + /// The double value of every block's fontSize. + final double? fontSize; + + /// The colorsets which give the color value for its thresholds key value. + /// + /// Be aware that first Color is the maximum value if [ColorMode] is [ColorMode.opacity]. + /// Also colorsets must have at least one color. + final Map colorsets; + + /// ColorMode changes the color mode of blocks. + /// + /// [ColorMode.opacity] requires just one colorsets value and changes color + /// dynamically based on hightest value of [datasets]. + /// [ColorMode.color] changes colors based on [colorsets] thresholds key value. + /// + /// Default value is [ColorMode.opacity]. + final ColorMode colorMode; + + /// Function that will be called when a block is clicked. + /// + /// Parameter gives clicked [DateTime] value. + final Function(DateTime)? onClick; + + /// The margin value for every block. + final EdgeInsets? margin; + + /// The double value of every block's borderRadius. + final double? borderRadius; + + /// Show day text in every blocks if the value is true. + /// + /// Default value is false. + final bool? showText; + + /// Show color tip which represents the color range at the below. + /// + /// Default value is true. + final bool? showColorTip; + + /// Makes heatmap scrollable if the value is true. + /// + /// default value is false. + final bool scrollable; + + /// Widgets which shown at left and right side of colorTip. + /// + /// First value is the left side widget and second value is the right side widget. + /// Be aware that [colorTipHelper.length] have to greater or equal to 2. + /// Give null value makes default 'less' and 'more' [Text]. + final List? colorTipHelper; + + /// The integer value which represents the number of [HeatMapColorTip]'s tip container. + final int? colorTipCount; + + /// The double value of [HeatMapColorTip]'s tip container's size. + final double? colorTipSize; + + const HeatMap({ + Key? key, + required this.colorsets, + this.colorMode = ColorMode.opacity, + this.startDate, + this.endDate, + this.textColor, + this.size = 20, + this.fontSize, + this.onClick, + this.margin, + this.borderRadius, + this.datasets, + this.defaultColor, + this.showText = false, + this.showColorTip = true, + this.scrollable = false, + this.colorTipHelper, + this.colorTipCount, + this.colorTipSize, + }) : super(key: key); + + @override + State createState() => _HeatMap(); +} + +class _HeatMap extends State { + /// Put child into [SingleChildScrollView] so that user can scroll the widet horizontally. + Widget _scrollableHeatMap(Widget child) { + return widget.scrollable + ? SingleChildScrollView( + reverse: true, + child: child, + ) + : child; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Heatmap Widget. + _scrollableHeatMap(HeatMapPage( + endDate: widget.endDate ?? DateTime.now(), + startDate: widget.startDate ?? + DateUtil.oneYearBefore(widget.endDate ?? DateTime.now()), + colorMode: widget.colorMode, + size: widget.size, + fontSize: widget.fontSize, + datasets: widget.datasets, + defaultColor: widget.defaultColor, + textColor: widget.textColor, + colorsets: widget.colorsets, + borderRadius: widget.borderRadius, + onClick: widget.onClick, + margin: widget.margin, + showText: widget.showText, + )), + + // Show HeatMapColorTip if showColorTip is true. + if (widget.showColorTip == true) + HeatMapColorTip( + colorMode: widget.colorMode, + colorsets: widget.colorsets, + leftWidget: widget.colorTipHelper?[0], + rightWidget: widget.colorTipHelper?[1], + containerCount: widget.colorTipCount, + size: widget.colorTipSize, + ), + ], + ); + } +} diff --git a/lib/widgets/heatmap/heatmap_calendar.dart b/lib/widgets/heatmap/heatmap_calendar.dart new file mode 100644 index 00000000..bdfad1f4 --- /dev/null +++ b/lib/widgets/heatmap/heatmap_calendar.dart @@ -0,0 +1,248 @@ +import 'package:flutter/material.dart'; +import 'package:nepanikar/app/theme/colors.dart'; +import 'package:nepanikar/widgets/heatmap/data/heatmap_color_mode.dart'; +import 'package:nepanikar/widgets/heatmap/util/date_util.dart'; +import 'package:nepanikar/widgets/heatmap/util/widget_util.dart'; +import 'package:nepanikar/widgets/heatmap/widget/heatmap_calendar_page.dart'; +import 'package:nepanikar/widgets/heatmap/widget/heatmap_color_tip.dart'; + +class HeatMapCalendar extends StatefulWidget { + /// The datasets which fill blocks based on its value. + final Map? datasets; + + /// The color value of every block's default color. + final Color? defaultColor; + + /// The colorsets which give the color value for its thresholds key value. + /// + /// Be aware that first Color is the maximum value if [ColorMode] is [ColorMode.opacity]. + /// Also colorsets must have at least one color. + final Map colorsets; + + /// The double value of every block's borderRadius. + final double? borderRadius; + + /// The date values of initial year and month. + final DateTime? initDate; + + /// The double value of every block's size. + final double? size; + + /// The text color value of every blocks. + final Color? textColor; + + /// The double value of every block's fontSize. + final double? fontSize; + + /// The double value of month label's fontSize. + final double? monthFontSize; + + /// The double value of week label's fontSize. + final double? weekFontSize; + + /// The text color value of week labels. + final Color? weekTextColor; + + /// Make block size flexible if value is true. + /// + /// Default value is false. + final bool? flexible; + + /// The margin value for every block. + final EdgeInsets? margin; + + /// ColorMode changes the color mode of blocks. + /// + /// [ColorMode.opacity] requires just one colorsets value and changes color + /// dynamically based on hightest value of [datasets]. + /// [ColorMode.color] changes colors based on [colorsets] thresholds key value. + /// + /// Default value is [ColorMode.opacity]. + final ColorMode colorMode; + + /// Function that will be called when a block is clicked. + /// + /// Paratmeter gives clicked [DateTime] value. + final Function(DateTime)? onClick; + + /// Function that will be called when month is changed. + /// + /// Paratmeter gives [DateTime] value of current month. + final Function(DateTime)? onMonthChange; + + /// Show color tip which represents the color range at the below. + /// + /// Default value is true. + final bool? showColorTip; + + /// Widgets which shown at left and right side of colorTip. + /// + /// First value is the left side widget and second value is the right side widget. + /// Be aware that [colorTipHelper.length] have to greater or equal to 2. + /// Give null value makes default 'less' and 'more' [Text]. + final List? colorTipHelper; + + /// The integer value which represents the number of [HeatMapColorTip]'s tip container. + final int? colorTipCount; + + /// The double value of [HeatMapColorTip]'s tip container's size. + final double? colorTipSize; + + const HeatMapCalendar({ + Key? key, + required this.colorsets, + this.colorMode = ColorMode.opacity, + this.defaultColor, + this.datasets, + this.initDate, + this.size = 42, + this.fontSize, + this.monthFontSize, + this.textColor, + this.weekFontSize, + this.weekTextColor, + this.borderRadius, + this.flexible = false, + this.margin, + this.onClick, + this.onMonthChange, + this.showColorTip = true, + this.colorTipHelper, + this.colorTipCount, + this.colorTipSize, + }) : super(key: key); + + @override + State createState() => _HeatMapCalendar(); +} + +class _HeatMapCalendar extends State { + // The DateTime value of first day of the current month. + DateTime? _currentDate; + + @override + void initState() { + super.initState(); + setState(() { + // Set _currentDate value to first day of initialized date or + // today's month if widget.initDate is null. + _currentDate = + DateUtil.startDayOfMonth(widget.initDate ?? DateTime.now()); + }); + } + + void changeMonth(int direction) { + setState(() { + _currentDate = + DateUtil.changeMonth(_currentDate ?? DateTime.now(), direction); + }); + if (widget.onMonthChange != null) widget.onMonthChange!(_currentDate!); + } + + /// Header widget which shows left, right buttons and year/month text. + Widget _header() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Previous month button. + IconButton( + icon: const Icon( + Icons.arrow_back_ios, + size: 14, + ), + onPressed: () => changeMonth(-1), + ), + + // Text which shows the current year and month + Text( + DateUtil.MONTH_LABEL[_currentDate?.month ?? 0] + + ' ' + + (_currentDate?.year).toString(), + style: TextStyle( + fontSize: widget.monthFontSize ?? 12, + fontWeight: FontWeight.bold + ), + ), + + // Next month button. + IconButton( + icon: const Icon( + Icons.arrow_forward_ios, + size: 14, + ), + onPressed: () => changeMonth(1), + ), + ], + ); + } + + Widget _weekLabel() { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + for (String label in DateUtil.WEEK_LABEL.skip(1)) + WidgetUtil.flexibleContainer( + widget.flexible ?? false, + false, + Container( + margin: EdgeInsets.only( + left: widget.margin?.left ?? 2, + right: widget.margin?.right ?? 2), + width: widget.size ?? 42, + alignment: Alignment.center, + child: Text( + label, + style: TextStyle( + fontSize: widget.weekFontSize ?? 12, + color: NepanikarColors.white, + fontWeight: FontWeight.bold + ), + ), + ), + ), + ], + ); + } + + /// Expand width dynamically if [flexible] is true. + Widget _intrinsicWidth({ + required Widget child, + }) => + (widget.flexible ?? false) ? child : IntrinsicWidth(child: child); + + @override + Widget build(BuildContext context) { + return _intrinsicWidth( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _header(), + _weekLabel(), + HeatMapCalendarPage( + baseDate: _currentDate ?? DateTime.now(), + colorMode: widget.colorMode, + flexible: widget.flexible, + size: widget.size, + fontSize: widget.fontSize, + defaultColor: widget.defaultColor, + textColor: widget.textColor, + margin: widget.margin, + datasets: widget.datasets, + colorsets: widget.colorsets, + borderRadius: widget.borderRadius, + onClick: widget.onClick, + ), + if (widget.showColorTip == true) + HeatMapColorTip( + colorMode: widget.colorMode, + colorsets: widget.colorsets, + leftWidget: widget.colorTipHelper?[0], + rightWidget: widget.colorTipHelper?[1], + containerCount: widget.colorTipCount, + size: widget.colorTipSize, + ), + ], + ), + ); + } +} diff --git a/lib/widgets/heatmap/util/datasets_util.dart b/lib/widgets/heatmap/util/datasets_util.dart new file mode 100644 index 00000000..fa98e92a --- /dev/null +++ b/lib/widgets/heatmap/util/datasets_util.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import './date_util.dart'; + +class DatasetsUtil { + /// Filtering [datasets] where the key is on the same month of [referenceDate]. + static Map filterMonth( + Map? datasets, DateTime referenceDate) { + return Map.from(datasets ?? {}) + ..removeWhere( + (date, value) => + !(date.isAfter(DateUtil.startDayOfMonth(referenceDate)) && + date.isBefore(DateUtil.endDayOfMonth(referenceDate)) || + date == DateUtil.endDayOfMonth(referenceDate) || + date == DateUtil.startDayOfMonth(referenceDate)), + ); + } + + /// Get maximum value of [datasets]. + static int getMaxValue(Map? datasets) { + int result = 0; + + datasets?.forEach((date, value) { + if (value > result) { + result = value; + } + }); + + return result; + } + + /// Get color from [colorsets] using [dataValue]. + static Color? getColor(Map? colorsets, int? dataValue) { + int result = 0; + + colorsets?.forEach((key, value) { + if (key <= (dataValue ?? 0)) result = key; + }); + + return colorsets?[result]; + } +} diff --git a/lib/widgets/heatmap/util/date_util.dart b/lib/widgets/heatmap/util/date_util.dart new file mode 100644 index 00000000..555fcd9d --- /dev/null +++ b/lib/widgets/heatmap/util/date_util.dart @@ -0,0 +1,130 @@ +class DateUtil { + static const int DAYS_IN_WEEK = 7; + + static const List MONTH_LABEL = [ + '', + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ]; + + static const List SHORT_MONTH_LABEL = [ + '', + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ]; + + static const List WEEK_LABEL = [ + '', + 'Sun', + 'Mon', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + 'Sat', + ]; + + /// Get start day of month. + static DateTime startDayOfMonth(final DateTime referenceDate) => + DateTime(referenceDate.year, referenceDate.month, 1); + + /// Get last day of month. + static DateTime endDayOfMonth(final DateTime referenceDate) => + DateTime(referenceDate.year, referenceDate.month + 1, 0); + + /// Get exactly one year before of [referenceDate]. + static DateTime oneYearBefore(final DateTime referenceDate) => + DateTime(referenceDate.year - 1, referenceDate.month, referenceDate.day); + + /// Separate [referenceDate]'s month to List of every weeks. + static List> separatedMonth( + final DateTime referenceDate) { + DateTime _startDate = startDayOfMonth(referenceDate); + DateTime _endDate = DateTime(_startDate.year, _startDate.month, + _startDate.day + DAYS_IN_WEEK - _startDate.weekday % DAYS_IN_WEEK - 1); + DateTime _finalDate = endDayOfMonth(referenceDate); + List> _savedMonth = []; + + while (_startDate.isBefore(_finalDate) || _startDate == _finalDate) { + _savedMonth.add({_startDate: _endDate}); + _startDate = changeDay(_endDate, 1); + _endDate = changeDay( + _endDate, + endDayOfMonth(_endDate).day - _startDate.day >= DAYS_IN_WEEK + ? DAYS_IN_WEEK + : endDayOfMonth(_endDate).day - _startDate.day + 1); + } + return _savedMonth; + } + + /// Change day of [referenceDate]. + static DateTime changeDay(final DateTime referenceDate, final int dayCount) => + DateTime(referenceDate.year, referenceDate.month, + referenceDate.day + dayCount); + + /// Change month of [referenceDate]. + static DateTime changeMonth(final DateTime referenceDate, int monthCount) => + DateTime(referenceDate.year, referenceDate.month + monthCount, + referenceDate.day); + + //#region unused methods. + + // static int weekCount(final DateTime referenceDate) { + // return ((startDayOfMonth(referenceDate).weekday % DAYS_IN_WEEK + + // endDayOfMonth(referenceDate).day) / + // DAYS_IN_WEEK) + // .ceil(); + // } + + // static int weekPos(final DateTime referenceDate) => + // (referenceDate.day + + // startDayOfMonth(referenceDate).weekday % DAYS_IN_WEEK) ~/ + // DAYS_IN_WEEK; + + // static DateTime startDayOfWeek(final DateTime referenceDate) => + // weekPos(referenceDate) == 0 + // ? startDayOfMonth(referenceDate) + // : DateTime(referenceDate.year, referenceDate.month, + // referenceDate.day - referenceDate.weekday % DAYS_IN_WEEK); + + // static DateTime endDayOfWeek(final DateTime referenceDate) { + // return weekPos(referenceDate) != (weekCount(referenceDate) - 1) + // ? DateTime(referenceDate.year, referenceDate.month, + // referenceDate.day - referenceDate.weekday % DAYS_IN_WEEK + 6) + // : endDayOfMonth(referenceDate); + // } + + // static DateTime changeWeek(final DateTime referenceDate, int weekCount) => + // DateTime(referenceDate.year, referenceDate.month, + // 1 + DAYS_IN_WEEK * weekCount); + + // static bool compareDate(final DateTime first, final DateTime second) => + // first.year == second.year && + // first.month == second.month && + // first.day == second.day; + + // static bool isStartDayOfMonth(final DateTime referenceDate) => + // compareDate(referenceDate, startDayOfMonth(referenceDate)); + + //#endregion +} diff --git a/lib/widgets/heatmap/util/widget_util.dart b/lib/widgets/heatmap/util/widget_util.dart new file mode 100644 index 00000000..a17b6e2d --- /dev/null +++ b/lib/widgets/heatmap/util/widget_util.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class WidgetUtil { + /// Make [HeatMapContainer] flexible size if [isFlexible] is true. + static Widget flexibleContainer( + bool isFlexible, bool isSquare, Widget child) { + return isFlexible + ? Expanded( + child: isSquare + ? AspectRatio( + aspectRatio: 1, + child: child, + ) + : child, + ) + : child; + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_calendar_page.dart b/lib/widgets/heatmap/widget/heatmap_calendar_page.dart new file mode 100644 index 00000000..de4e16fb --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_calendar_page.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:nepanikar/widgets/heatmap/data/heatmap_color_mode.dart'; +import 'package:nepanikar/widgets/heatmap/util/datasets_util.dart'; +import 'package:nepanikar/widgets/heatmap/util/date_util.dart'; +import 'package:nepanikar/widgets/heatmap/widget/heatmap_calendar_row.dart'; + +class HeatMapCalendarPage extends StatelessWidget { + /// The DateTime value which contains the current calendar's date value. + final DateTime baseDate; + + /// The list value of the map value that contains + /// separated start and end of every weeks on month. + /// + /// Separate [datasets] using [DateUtil.separatedMonth]. + final List> separatedDate; + + /// The margin value for every block. + final EdgeInsets? margin; + + /// Make block size flexible if value is true. + final bool? flexible; + + /// The double value of every block's width and height. + final double? size; + + /// The double value of every block's fontSize. + final double? fontSize; + + /// The datasets which fill blocks based on its value. + final Map? datasets; + + /// The default background color value of every blocks + final Color? defaultColor; + + /// The text color value of every blocks + final Color? textColor; + + /// ColorMode changes the color mode of blocks. + /// + /// [ColorMode.opacity] requires just one colorsets value and changes color + /// dynamically based on hightest value of [datasets]. + /// [ColorMode.color] changes colors based on [colorsets] thresholds key value. + final ColorMode colorMode; + + /// The colorsets which give the color value for its thresholds key value. + /// + /// Be aware that first Color is the maximum value if [ColorMode] is [ColorMode.opacity]. + final Map? colorsets; + + /// The double value of every block's borderRadius + final double? borderRadius; + + /// The integer value of the maximum value for the [datasets]. + /// + /// Filtering [datasets] with [baseDate] using [DatasetsUtil.filterMonth]. + /// And get highest key value of filtered datasets using [DatasetsUtil.getMaxValue]. + final int? maxValue; + + /// Function that will be called when a block is clicked. + /// + /// Paratmeter gives clicked [DateTime] value. + final Function(DateTime)? onClick; + + HeatMapCalendarPage({ + Key? key, + required this.baseDate, + required this.colorMode, + this.flexible, + this.size, + this.fontSize, + this.defaultColor, + this.textColor, + this.margin, + this.datasets, + this.colorsets, + this.borderRadius, + this.onClick, + }) : separatedDate = DateUtil.separatedMonth(baseDate), + maxValue = DatasetsUtil.getMaxValue( + DatasetsUtil.filterMonth(datasets, baseDate)), + super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (var date in separatedDate) + HeatMapCalendarRow( + startDate: date.keys.first, + endDate: date.values.first, + colorMode: colorMode, + size: size, + fontSize: fontSize, + defaultColor: defaultColor, + colorsets: colorsets, + textColor: textColor, + borderRadius: borderRadius, + flexible: flexible, + margin: margin, + maxValue: maxValue, + onClick: onClick, + datasets: Map.from(datasets ?? {}) + ..removeWhere( + (key, value) => !(key.isAfter(date.keys.first) && + key.isBefore(date.values.first) || + key == date.keys.first || + key == date.values.first), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_calendar_row.dart b/lib/widgets/heatmap/widget/heatmap_calendar_row.dart new file mode 100644 index 00000000..559c3762 --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_calendar_row.dart @@ -0,0 +1,167 @@ +import 'package:flutter/material.dart'; +import 'package:nepanikar/widgets/heatmap/data/heatmap_color_mode.dart'; +import 'package:nepanikar/widgets/heatmap/util/datasets_util.dart'; +import 'package:nepanikar/widgets/heatmap/util/date_util.dart'; +import 'package:nepanikar/widgets/heatmap/util/widget_util.dart'; +import 'package:nepanikar/widgets/heatmap/widget/heatmap_container.dart'; + +class HeatMapCalendarRow extends StatelessWidget { + /// The integer value of beginning date of the week. + final DateTime startDate; + + /// The integer value of end date of the week + final DateTime endDate; + + /// The double value of every [HeatMapContainer]'s width and height. + final double? size; + + /// The double value of every [HeatMapContainer]'s fontSize. + final double? fontSize; + + /// The List of row items. + /// + /// It includes every days of the week and + /// if one week doesn't have 7 days, it will be filled with [SizedBox]. + final List dayContainers; + + /// The default background color value of [HeatMapContainer] + final Color? defaultColor; + + /// The text color value of [HeatMapContainer] + final Color? textColor; + + /// The colorsets which give the color value for its thresholds key value. + /// + /// Be aware that first Color is the maximum value if [ColorMode] is [ColorMode.opacity]. + final Map? colorsets; + + /// The double value of [HeatMapContainer]'s borderRadius + final double? borderRadius; + + /// Make block size flexible if value is true. + final bool? flexible; + + /// The margin value for every block. + final EdgeInsets? margin; + + /// The datasets which fill blocks based on its value. + /// + /// datasets keys have to greater or equal to [startDate] and + /// smaller or equal to [endDate]. + final Map? datasets; + + /// ColorMode changes the color mode of blocks. + /// + /// [ColorMode.opacity] requires just one colorsets value and changes color + /// dynamically based on hightest value of [datasets]. + /// [ColorMode.color] changes colors based on [colorsets] thresholdsc key value. + final ColorMode colorMode; + + /// The integer value of the maximum value for the highest value of the month. + final int? maxValue; + + /// Function that will be called when a block is clicked. + /// + /// Paratmeter gives clicked [DateTime] value. + final Function(DateTime)? onClick; + + HeatMapCalendarRow({ + Key? key, + required this.startDate, + required this.endDate, + required this.colorMode, + this.size, + this.fontSize, + this.defaultColor, + this.colorsets, + this.textColor, + this.borderRadius, + this.flexible, + this.margin, + this.datasets, + this.maxValue, + this.onClick, + }) : dayContainers = List.generate( + 7, + // If current week has first day of the month and + // the first day is not a sunday, it must have extra space on it. + // Then fill it with empty Container for extra space. + // + // Do same works if current week has last day of the month and + // the last day is not a saturday. + (i) => (startDate == DateUtil.startDayOfMonth(startDate) && + endDate.day - startDate.day != 7 && + i < (startDate.weekday % 7)) || + (endDate == DateUtil.endDayOfMonth(endDate) && + endDate.day - startDate.day != 7 && + i > (endDate.weekday % 7)) + ? Container( + width: size ?? 42, + height: size ?? 42, + margin: margin ?? const EdgeInsets.all(2), + ) + // If the day is not a empty one then create HeatMapContainer. + : HeatMapContainer( + // Given information about the week is that + // start day of week value and end day of week. + // + // So we have to give every day information to each HeatMapContainer. + date: DateTime(startDate.year, startDate.month, + startDate.day - startDate.weekday % 7 + i), + backgroundColor: defaultColor, + size: size, + fontSize: fontSize, + textColor: textColor, + borderRadius: borderRadius, + margin: margin, + onClick: onClick, + // If datasets has DateTime key which is equal to this HeatMapContainer's date, + // we have to color the matched HeatMapContainer. + // + // If datasets is null or doesn't contains the equal DateTime value, send null. + selectedColor: datasets?.keys.contains(DateTime( + startDate.year, + startDate.month, + startDate.day - startDate.weekday % 7 + i)) ?? + false + // If colorMode is ColorMode.opacity, + ? colorMode == ColorMode.opacity + // Color the container with first value of colorsets + // and set opacity value to current day's datasets key + // devided by maxValue which is the maximum value of the month. + ? colorsets?.values.first.withOpacity((datasets?[ + DateTime( + startDate.year, + startDate.month, + startDate.day + + i - + (startDate.weekday % 7))] ?? + 1) / + (maxValue ?? 1)) + // Else if colorMode is ColorMode.Color. + // + // Get color value from colorsets which is filtered with DateTime value + // Using DatasetsUtil.getColor() + : DatasetsUtil.getColor( + colorsets, + datasets?[DateTime( + startDate.year, + startDate.month, + startDate.day + i - (startDate.weekday % 7))]) + : null, + ), + ), + super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + for (Widget container in dayContainers) + WidgetUtil.flexibleContainer(flexible ?? false, true, container), + ], + ); + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_color_tip.dart b/lib/widgets/heatmap/widget/heatmap_color_tip.dart new file mode 100644 index 00000000..5e5951ab --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_color_tip.dart @@ -0,0 +1,130 @@ +import 'dart:collection'; + +import 'package:flutter/material.dart'; + +import 'package:nepanikar/widgets/heatmap/data/heatmap_color.dart'; +import 'package:nepanikar/widgets/heatmap/data/heatmap_color_mode.dart'; + +class MyKey implements Comparable { + + MyKey(this.value); + final int value; + + @override + int compareTo(MyKey other) { + return value.compareTo(other.value); + } +} + +class HeatMapColorTip extends StatelessWidget { + + const HeatMapColorTip({ + Key? key, + required this.colorMode, + this.colorsets, + this.leftWidget, + this.rightWidget, + this.containerCount, + this.size, + }) : super(key: key); + /// Default length of [containerCount]. + final int _defaultLength = 7; + + /// The colorsets which give the color value for its thresholds key value. + /// + /// Be aware that first Color is the maximum value if [ColorMode] is [ColorMode.opacity]. + final Map? colorsets; + + /// ColorMode changes the color mode of blocks. + /// + /// [ColorMode.opacity] requires just one colorsets value and changes color + /// [ColorMode.color] changes colors based on [colorsets] thresholds key value. + final ColorMode colorMode; + + /// The widget which shows left side of [HeatMapColorTip]. + /// + /// If the value is null then show default 'less' [Text]. + final Widget? leftWidget; + + /// The widget which shows right side of [HeatMapColorTip]. + /// + /// If the value is null then show default 'more' [Text]. + final Widget? rightWidget; + + /// The integer value of color tip containers count. + final int? containerCount; + + /// The double value of tip container's size. + final double? size; + + /// It returns the List of tip container. + /// + /// If [ColorMode.color], call [_heatmapListColor] + /// If [ColorMode.opacity], call [_heatmapListOpacity] + List _heatmapList() => colorMode == ColorMode.color + ? _heatmapListColor() + : _heatmapListOpacity(); + + /// Evenly show every colors from lowest to highest. + List _heatmapListColor() { + List children = []; + SplayTreeMap sortedColorset = + SplayTreeMap.from(colorsets ?? {}, (a, b) => a.compareTo(b)); + + for (int i = 0; i < (containerCount ?? _defaultLength); i++) { + // Correctly calculate index within bounds and use elements + int index = ((sortedColorset.length / (containerCount ?? _defaultLength)) * i).floor(); + Color color = sortedColorset.values.elementAt(index); + children.add(_tipContainer(color)); + } + + return children; + } + + /// Evenly show every colors from transparent to non-transparent. + List _heatmapListOpacity() { + List children = []; + + for (int i = 0; i < (containerCount ?? _defaultLength); i++) { + children.add(_tipContainer(colorsets?.values.first + .withOpacity(i / (containerCount ?? _defaultLength)) ?? + Colors.white)); + } + return children; + } + + /// Container which is colored by [color]. + Widget _tipContainer(Color color) { + return Container( + color: HeatMapColor.defaultColor, + child: Container( + width: size ?? 10, + height: size ?? 10, + color: color, + ), + ); + } + + /// Default text widget. + Widget _defaultText(String text) { + return Text( + text, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: size ?? 10), + ); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + leftWidget ?? _defaultText('less'), + ..._heatmapList(), + rightWidget ?? _defaultText('more'), + ], + ), + ); + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_column.dart b/lib/widgets/heatmap/widget/heatmap_column.dart new file mode 100644 index 00000000..827af15c --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_column.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; + +import 'package:nepanikar/widgets/heatmap/data/heatmap_color_mode.dart'; +import 'package:nepanikar/widgets/heatmap/util/datasets_util.dart'; +import 'package:nepanikar/widgets/heatmap/util/date_util.dart'; +import 'package:nepanikar/widgets/heatmap/widget/heatmap_container.dart'; + +class HeatMapColumn extends StatelessWidget { + HeatMapColumn({ + super.key, + required this.startDate, + required this.endDate, + required this.colorMode, + required this.numDays, + this.size, + this.fontSize, + this.defaultColor, + this.datasets, + this.textColor, + this.borderRadius, + this.margin, + this.colorsets, + this.onClick, + this.maxValue, + this.showText, + }) : + // Init list. + dayContainers = List.generate( + numDays, + (i) { + final currentDate = DateUtil.changeDay(startDate, i); + final isSelected = datasets?.keys.contains(currentDate) ?? false; + + // Determine selected color based on the color mode and dataset value + final selectedColor = isSelected + ? (colorMode == ColorMode.opacity + ? colorsets?.values.first.withOpacity( + (datasets?[currentDate]?.toDouble() ?? 0) / + (maxValue?.toDouble() ?? 1), + ) + : DatasetsUtil.getColor(colorsets, datasets?[currentDate])) + : null; + + return HeatMapContainer( + date: currentDate, + backgroundColor: defaultColor, + size: size, + fontSize: fontSize, + textColor: textColor, + borderRadius: borderRadius, + margin: margin, + onClick: onClick, + showText: showText, + selectedColor: selectedColor, + ); + }, + ); + + /// The List widgets of [HeatMapContainer]. + /// + /// It includes every days of the week and + /// if one week doesn't have 7 days, it will be filled with empty [Container]. + final List dayContainers; + + /// The List widgets of empty [Container]. + /// + /// It only processes when given week's length is not 7. + + /// The date value of first day of given week. + final DateTime startDate; + + /// The date value of last day of given week. + final DateTime endDate; + + /// The double value of every [HeatMapContainer]'s width and height. + final double? size; + + /// The double value of every [HeatMapContainer]'s fontSize. + final double? fontSize; + + /// The default background color value of [HeatMapContainer]. + final Color? defaultColor; + + /// The datasets which fill blocks based on its value. + /// + /// datasets keys have to greater or equal to [startDate] and + /// smaller or equal to [endDate]. + final Map? datasets; + + /// The text color value of [HeatMapContainer]. + final Color? textColor; + + /// The colorsets which give the color value for its thresholds key value. + /// + /// Be aware that first Color is the maximum value if [ColorMode] is [ColorMode.opacity]. + final Map? colorsets; + + /// ColorMode changes the color mode of blocks. + /// + /// [ColorMode.opacity] requires just one colorsets value and changes color + /// dynamically based on hightest value of [datasets]. + /// [ColorMode.color] changes colors based on [colorsets] thresholdsc key value. + final ColorMode colorMode; + + /// The double value of [HeatMapContainer]'s borderRadius. + final double? borderRadius; + + /// The margin value for every block. + final EdgeInsets? margin; + + /// Function that will be called when a block is clicked. + /// + /// Paratmeter gives clicked [DateTime] value. + final Function(DateTime)? onClick; + + /// The integer value of the maximum value for the highest value of the month. + final int? maxValue; + + /// Show day text in every blocks if the value is true. + final bool? showText; + + // The number of day blocks to draw. This should be seven for all but the + // current week. + final int numDays; + + @override + Widget build(BuildContext context) { + return Column( + children: [...dayContainers], + ); + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_container.dart b/lib/widgets/heatmap/widget/heatmap_container.dart new file mode 100644 index 00000000..2e42a490 --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_container.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import '../data/heatmap_color.dart'; + +class HeatMapContainer extends StatelessWidget { + final DateTime date; + final double? size; + final double? fontSize; + final double? borderRadius; + final Color? backgroundColor; + final Color? selectedColor; + final Color? textColor; + final EdgeInsets? margin; + final bool? showText; + final Function(DateTime dateTime)? onClick; + + const HeatMapContainer({ + super.key, + required this.date, + this.margin, + this.size, + this.fontSize, + this.borderRadius, + this.backgroundColor, + this.selectedColor, + this.textColor, + this.onClick, + this.showText, + }); + + @override + Widget build(BuildContext context) { + final isColored = selectedColor != null; + return Padding( + padding: margin ?? const EdgeInsets.all(2), + child: GestureDetector( + child: Container( + decoration: BoxDecoration( + color: backgroundColor ?? HeatMapColor.defaultColor, + borderRadius: BorderRadius.all(Radius.circular(borderRadius ?? 5)), + ), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOutQuad, + width: size, + height: size, + alignment: Alignment.center, + decoration: BoxDecoration( + color: selectedColor, + borderRadius: + BorderRadius.all(Radius.circular(borderRadius ?? 5)), + ), + child: (showText ?? true) + ? Text( + date.day.toString(), + style: TextStyle( + color: isColored ? Colors.black : textColor ?? const Color(0xFF8A8A8A), + fontSize: fontSize, + fontWeight: FontWeight.bold), + ) + : null, + ), + ), + onTap: () { + onClick != null ? onClick!(date) : null; + }, + ), + ); + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_month2.dart b/lib/widgets/heatmap/widget/heatmap_month2.dart new file mode 100644 index 00000000..10417c55 --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_month2.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import '../util/date_util.dart'; + +class HeatMapMonth2 extends StatelessWidget { + /// The month value. + /// + /// From 1: January to 12: December. + final int month; + + /// The double value of font size. + final double? fontSize; + + /// The color value of font color. + final Color? fontColor; + + const HeatMapMonth2({ + Key? key, + required this.month, // Now just a single int representing the month + this.fontSize, + this.fontColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + // Use the `month` to directly index into `DateUtil.SHORT_MONTH_LABEL` + // Remember to adjust the index if necessary (e.g., if `month` is 1-based but the array is 0-based) + String monthText = DateUtil.SHORT_MONTH_LABEL[month - 1]; // Assuming month is 1-based and your array is 0-based + + return Text( + monthText, + style: TextStyle( + color: fontColor, + fontSize: fontSize, + ), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/heatmap/widget/heatmap_month_text.dart b/lib/widgets/heatmap/widget/heatmap_month_text.dart new file mode 100644 index 00000000..f9569da1 --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_month_text.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import '../util/date_util.dart'; + +class HeatMapMonthText extends StatelessWidget { + /// List value of every sunday's month information. + /// + /// From 1: January to 12: December. + final List? firstDayInfos; + + /// The double value for space between labels. + final double? size; + + /// The double value of font size. + final double? fontSize; + + /// The color value of font color. + final Color? fontColor; + + /// The margin value for correctly space between labels. + final EdgeInsets? margin; + + const HeatMapMonthText({ + Key? key, + this.firstDayInfos, + this.fontSize, + this.fontColor, + this.size, + this.margin, + }) : super(key: key); + + /// The list of every month labels and fitted space. + List _labels() { + List items = []; + + // Set true if previous week was the first day of the month. + bool _write = false; + + // Loop until check every given weeks. + for (int label = 0; label < (firstDayInfos?.length ?? 0); label++) { + // If given week is first week of given datesets or + // first week of month, create labels + if (label == 0 || + (label > 0 && firstDayInfos![label] != firstDayInfos![label - 1])) { + _write = true; + + // Add Text without width margin if first week is end of the month. + // Otherwise, add Text with width margin. + items.add( + firstDayInfos!.length == 1 || (label == 0 && firstDayInfos![label] != firstDayInfos![label + 1]) + ? _renderText(DateUtil.SHORT_MONTH_LABEL[firstDayInfos![label]]) + : Container( + width: (((size ?? 20) + (margin?.right ?? 2)) * 2), + margin: EdgeInsets.only( + left: margin?.left ?? 2, right: margin?.right ?? 2), + child: _renderText( + DateUtil.SHORT_MONTH_LABEL[firstDayInfos![label]]), + ), + ); + } else if (_write) { + // If given week is the next week of labeled week. + // do nothing. + _write = false; + } else { + // Else create empty box. + items.add(Container( + margin: EdgeInsets.only( + left: margin?.left ?? 2, right: margin?.right ?? 2), + width: size ?? 20, + )); + } + } + + return items; + } + + Widget _renderText(String text) { + return Text( + text, + style: TextStyle( + color: fontColor, + fontSize: fontSize, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Row( + children: _labels(), + ); + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_page.dart b/lib/widgets/heatmap/widget/heatmap_page.dart new file mode 100644 index 00000000..2779c9ca --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_page.dart @@ -0,0 +1,212 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:nepanikar/widgets/heatmap/widget/heatmap_month2.dart'; +import 'heatmap_month_text.dart'; +import './heatmap_column.dart'; +import '../data/heatmap_color_mode.dart'; +import '../util/datasets_util.dart'; +import '../util/date_util.dart'; +import 'heatmap_week_text.dart'; + +class HeatMapPage extends StatelessWidget { + /// List value of every sunday's month information. + /// + /// From 1: January to 12: December. + final List _firstDayInfos = []; + + /// The number of days between [startDate] and [endDate]. + final int _dateDifferent; + + /// The Date value of start day of heatmap. + /// + /// HeatMap shows the start day of [startDate]'s week. + /// + /// Default value is 1 year before the [endDate]. + /// And if [endDate] is null, then set 1 year before the [DateTime.now] + final DateTime startDate; + + /// The Date value of end day of heatmap. + /// + /// Default value is [DateTime.now] + final DateTime endDate; + + /// The double value of every block's width and height. + final double? size; + + /// The double value of every block's fontSize. + final double? fontSize; + + /// The datasets which fill blocks based on its value. + final Map? datasets; + + /// The margin value for every block. + final EdgeInsets? margin; + + /// The default background color value of every blocks. + final Color? defaultColor; + + /// The text color value of every blocks. + final Color? textColor; + + /// ColorMode changes the color mode of blocks. + /// + /// [ColorMode.opacity] requires just one colorsets value and changes color + /// dynamically based on hightest value of [datasets]. + /// [ColorMode.color] changes colors based on [colorsets] thresholds key value. + final ColorMode colorMode; + + /// The colorsets which give the color value for its thresholds key value. + /// + /// Be aware that first Color is the maximum value if [ColorMode] is [ColorMode.opacity]. + final Map? colorsets; + + /// The double value of every block's borderRadius. + final double? borderRadius; + + /// The integer value of the maximum value for the [datasets]. + /// + /// Get highest key value of filtered datasets using [DatasetsUtil.getMaxValue]. + final int? maxValue; + + /// Function that will be called when a block is clicked. + /// + /// Paratmeter gives clicked [DateTime] value. + final Function(DateTime)? onClick; + + final bool? showText; + + HeatMapPage({ + Key? key, + required this.colorMode, + required this.startDate, + required this.endDate, + this.size, + this.fontSize, + this.datasets, + this.defaultColor, + this.textColor, + this.colorsets, + this.borderRadius, + this.onClick, + this.margin, + this.showText, + }) : _dateDifferent = endDate.difference(startDate).inDays, + maxValue = DatasetsUtil.getMaxValue(datasets), + super(key: key); + + /// Get [HeatMapColumn] from [startDate] to [endDate]. + List _heatmapColumnList(int specificMonth) { + List columns = []; + int year = startDate.year; + final dateTime = DateTime(year, specificMonth); + final lastDay = DateUtil.endDayOfMonth(dateTime); + int daysInMonth = lastDay.day; + + // Process each week in the month + for (int day = 1; day <= daysInMonth; day += 7) { + DateTime weekStart = DateTime(year, specificMonth, day); + DateTime weekEnd = + DateTime(year, specificMonth, min(day + 6, daysInMonth)); + + // Filter datasets for the days in this week only + Map weeklyData = {}; + if (datasets != null) { + for (var entry in datasets!.entries) { + if (entry.key.isAfter(weekStart.subtract(Duration(days: 1))) && + (entry.key.isBefore(weekEnd.add(Duration(days: 1))) || + entry.key.isAtSameMomentAs(weekEnd))) { + weeklyData[entry.key] = entry.value; + } + } + } + + // Create a HeatMapColumn for the current week + columns.add(HeatMapColumn( + startDate: weekStart, + endDate: weekEnd, + colorMode: colorMode, + numDays: weekEnd.difference(weekStart).inDays + 1, + size: size, + fontSize: fontSize, + defaultColor: defaultColor, + colorsets: colorsets, + textColor: textColor, + borderRadius: borderRadius, + margin: margin, + maxValue: maxValue, + onClick: onClick, + datasets: weeklyData, + showText: showText, + )); + } + + return columns; + } + + Widget _buildYearlyHeatmap() { + List monthlyColumns = []; + // Iterate over each month and create a HeatMapColumn for that month + for (int month = 1; month <= 12; month++) { + int year = startDate.year; + DateTime firstDayOfMonth = DateUtil.startDayOfMonth(DateTime(year, month)); + DateTime lastDayOfMonth = DateUtil.endDayOfMonth(DateTime(year, month)); + + Map monthlyData = Map.fromEntries( + datasets?.entries.where( + (entry) => entry.key.month == month && entry.key.year == year + ) ?? const Iterable.empty() + ); + + // Create a HeatMapColumn for the entire month + Widget heatmapColumn = HeatMapColumn( + startDate: firstDayOfMonth, + endDate: lastDayOfMonth, + colorMode: colorMode, + numDays: lastDayOfMonth.day, + size: size, + fontSize: fontSize, + defaultColor: defaultColor, + colorsets: colorsets, + textColor: textColor, + borderRadius: borderRadius, + margin: margin, + maxValue: maxValue, + onClick: onClick, + datasets: monthlyData, + showText: showText, + ); + + monthlyColumns.add( + Padding( + padding: EdgeInsets.only(right: 2), + child: SizedBox( + width: 21, + height: 675, + child: Column( + children: [ + Text( // You can also use the first letter of the month here as per your requirement + DateFormat('MMM').format(firstDayOfMonth), // 'MMM' formats to a three-letter month name + style: TextStyle(fontSize: fontSize, color: textColor, fontWeight: FontWeight.bold), + ), + heatmapColumn, + ], + ), + ), + ) + ); + } + + // Return a Row that contains all the monthly columns + return SingleChildScrollView( + child: Row(children: monthlyColumns), + ); + } + + @override + Widget build(BuildContext context) { + final yearlyHeatmap = _buildYearlyHeatmap(); + return yearlyHeatmap; + } +} diff --git a/lib/widgets/heatmap/widget/heatmap_week_text.dart b/lib/widgets/heatmap/widget/heatmap_week_text.dart new file mode 100644 index 00000000..96d9f364 --- /dev/null +++ b/lib/widgets/heatmap/widget/heatmap_week_text.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import '../util/date_util.dart'; + +class HeatMapWeekText extends StatelessWidget { + /// The margin value for correctly space between labels. + final EdgeInsets? margin; + + /// The double value of label's font size. + final double? fontSize; + + /// The double value of every block's size to fit the height. + final double? size; + + /// The color value of every font's color. + final Color? fontColor; + + const HeatMapWeekText({ + Key? key, + this.margin, + this.fontSize, + this.size, + this.fontColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (String label in DateUtil.WEEK_LABEL) + if(label.isNotEmpty) ... [ + Container( + height: size ?? 20, + margin: margin ?? const EdgeInsets.all(2.0), + child: Text( + label, + style: TextStyle( + fontSize: fontSize ?? 12, + color: fontColor, + ), + ), + ), + ], + ], + ); + } +} diff --git a/lib/widgets/home_tile.dart b/lib/widgets/home_tile.dart index 85915530..0b8f8448 100644 --- a/lib/widgets/home_tile.dart +++ b/lib/widgets/home_tile.dart @@ -22,10 +22,8 @@ class HomeTile extends StatelessWidget { @override Widget build(BuildContext context) { - bool _isDarkMode = false; - ThemeMode currentThemeMode = Theme.of(context).brightness == Brightness.dark ? - ThemeMode.dark : ThemeMode.light; - _isDarkMode = currentThemeMode == ThemeMode.dark ? true : false; + + final isDarkMode = Theme.of(context).brightness == Brightness.dark; return Semantics( button: true, @@ -46,7 +44,7 @@ class HomeTile extends StatelessWidget { ], ), child: Material( - color: _isDarkMode ? NepanikarColors.containerD : Colors.white, + color: isDarkMode ? NepanikarColors.containerD : Colors.white, borderRadius: BorderRadius.circular(16), child: InkWell( borderRadius: BorderRadius.circular(16), @@ -65,7 +63,7 @@ class HomeTile extends StatelessWidget { child: image, ), Assets.icons.navigation.arrowRight.svg(width: 16, height: 16, - color: _isDarkMode ? Colors.white : NepanikarColors.primaryD,) + color: isDarkMode ? Colors.white : NepanikarColors.primaryD,) ], ), ), @@ -76,7 +74,7 @@ class HomeTile extends StatelessWidget { text, style: NepanikarFonts.bodyHeavy.copyWith( fontSize: context.isSmallScreen ? 14 : 15, - color: _isDarkMode ? Colors.white : NepanikarColors.primaryD, + color: isDarkMode ? Colors.white : NepanikarColors.primaryD, ), maxLines: 2, overflow: TextOverflow.ellipsis, diff --git a/lib/widgets/input_form_contents/checklist_form_content.dart b/lib/widgets/input_form_contents/checklist_form_content.dart index 69f2c63e..ee833b34 100644 --- a/lib/widgets/input_form_contents/checklist_form_content.dart +++ b/lib/widgets/input_form_contents/checklist_form_content.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/l10n/ext.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/sizes.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/helpers/platform_helpers.dart'; import 'package:nepanikar/helpers/semantics_helpers.dart'; import 'package:nepanikar/services/db/common/checklist_item_model.dart'; @@ -57,6 +58,9 @@ class _ChecklistFormContentState @override Widget build(BuildContext context) { + final checkedTextColor = customColorsBasedOnDarkMode(context, NepanikarColors.white, NepanikarColors.dark); + + return GestureDetector( onTapDown: (_) => FocusScope.of(context).unfocus(), excludeFromSemantics: true, @@ -172,7 +176,7 @@ class _ChecklistFormContentState textInputAction: TextInputAction.newline, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: checkFormState == true - ? NepanikarColors.dark + ? checkedTextColor : NepanikarColors.primarySwatch.shade400, ), decoration: InputDecoration( diff --git a/lib/widgets/input_form_contents/plan_form_content.dart b/lib/widgets/input_form_contents/plan_form_content.dart index b8f5d0c0..cf9ac007 100644 --- a/lib/widgets/input_form_contents/plan_form_content.dart +++ b/lib/widgets/input_form_contents/plan_form_content.dart @@ -4,6 +4,7 @@ import 'package:collection/collection.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/services/db/common/nepanikar_plan_form_dao.dart'; import 'package:nepanikar/utils/registry.dart'; import 'package:nepanikar/widgets/nepanikar_screen_wrapper.dart'; @@ -71,6 +72,8 @@ class _PlanFormContentState extends State FocusScope.of(context).unfocus(), excludeFromSemantics: true, @@ -107,6 +110,7 @@ class _PlanFormContentState extends State { deleteIcon: const Icon(Icons.cancel), onDeleted: () => _removeEmotion(emotion), backgroundColor: Colors.purple, - // Customize as needed deleteIconColor: Colors.white, - // Customize as needed labelStyle: - const TextStyle(color: Colors.white), // Customize as needed + const TextStyle(color: Colors.white), ), ) .toList(), @@ -69,20 +67,18 @@ class _ChosenEmotionsWidgetState extends State { else { return Wrap( spacing: 8.0, + alignment: WrapAlignment.end, children: - selectedEmotions + selectedEmotions.take(9) .map( (emotion) => Chip( label: Text(emotion), backgroundColor: Colors.purple, - // Customize as needed deleteIconColor: Colors.white, - // Customize as needed labelStyle: - const TextStyle(color: Colors.white), // Customize as needed + const TextStyle(color: Colors.white), ), - ) - .toList(), + ).toList(), ); } } diff --git a/lib/widgets/mood/mood_heatmap.dart b/lib/widgets/mood/mood_heatmap.dart new file mode 100644 index 00000000..bf035a33 --- /dev/null +++ b/lib/widgets/mood/mood_heatmap.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:nepanikar/app/theme/colors.dart'; +import 'package:nepanikar/providers/mood_heatmap_filter_provider.dart'; +import 'package:nepanikar/widgets/heatmap/heatmap.dart'; + +import '../heatmap/data/heatmap_color_mode.dart'; +import '../heatmap/heatmap_calendar.dart'; + +class MoodHeatmap extends StatelessWidget{ + const MoodHeatmap({ + super.key, + required this.heatmapType, + required this.dateRange, + required this.moodScores, + }); + + final Map moodScores; + final DateTimeRange dateRange; + final HeatmapFilter heatmapType; + + + + @override + Widget build(BuildContext context) { + switch (heatmapType){ + case HeatmapFilter.month: + return monthHeatmap(); + case HeatmapFilter.year: + return yearHeatmap(); + } + } + + Widget monthHeatmap(){ + return HeatMapCalendar( + datasets: moodScores, + colorsets: _heatMapColors, + initDate: dateRange.start, + flexible: true, + colorMode: ColorMode.color, + colorTipCount: 5, + colorTipSize: 13, + monthFontSize: 18, + ); + } + + + + Widget yearHeatmap(){ + return HeatMap( + datasets: moodScores, + colorsets: _heatMapColors, + startDate: dateRange.start, + endDate: dateRange.end, + colorMode: ColorMode.color, + size: 17, + fontSize: 10, + scrollable: true, + showText: true, + colorTipCount: 5, + colorTipSize: 13, + ); + } + + static const _heatMapColors = { + 1: Color(0xffA72C2C), + 2: Color(0xffC78B31), + 3: Color(0xffDCC678), + 4: Color(0xffA6AC5A), + 5: Color(0xff49A3BF) + }; +} diff --git a/lib/widgets/mood/mood_picker.dart b/lib/widgets/mood/mood_picker.dart index d5803a58..786376c7 100644 --- a/lib/widgets/mood/mood_picker.dart +++ b/lib/widgets/mood/mood_picker.dart @@ -2,13 +2,15 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/app/l10n/ext.dart'; +import 'package:nepanikar/app/router/routes.dart'; import 'package:nepanikar/app/theme/colors.dart'; import 'package:nepanikar/app/theme/fonts.dart'; import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/providers/mood_state_provider.dart'; import 'package:nepanikar/screens/home/my_records/mood/mood_picker_screen.dart'; +import 'package:nepanikar/screens/home/my_records/mood/mood_records_screen.dart'; +import 'package:nepanikar/screens/home/my_records/my_records_sleep_track_screen.dart'; import 'package:nepanikar/services/db/my_records/mood_track_model.dart'; import 'package:nepanikar/utils/lottie_cache_manager.dart'; import 'package:nepanikar/utils/registry.dart'; @@ -85,12 +87,14 @@ class _MoodPickerState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { final textStyleColor = customColorsBasedOnDarkMode(context, NepanikarColors.white, NepanikarColors.primaryD); - final bool isMoodPickerScreen = GoRouter.of(context).location == const MoodPickerRoute().location; + final location = GoRouter.of(context).location; + final bool shouldDisplayTitle = + GoRouter.of(context).location == const MoodPickerRoute().location; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if(!isMoodPickerScreen) + if(!shouldDisplayTitle) if (!widget.autoSizeTitle) Text( widget.header ?? context.l10n.mood_welcome_title, @@ -114,15 +118,28 @@ class _MoodPickerState extends State with TickerProviderStateMixin { label: widget.showLabels ? null : mood.getSemanticsLabel(context), child: InkWell( onTap: () { - Provider.of(context, listen: false).setActiveMood(mood); - if(GoRouter.of(context).location != const MoodPickerRoute().location){ - context.push(const MoodPickerRoute().location); - } if (isPicked) { _playLottieAnim(mood); return; } - + if(location == const MyRecordsSleepTrackRoute().location){ + _playLottieAnim(mood); + analytics.logEvent( + name: 'mood_picked', + parameters: { + 'mood': mood.name, + }, + ); + widget.onPick.call(mood); + } + else{ + Provider.of(context, listen: false).setActiveMood(mood); + if(location != const MoodPickerRoute().location){ + Provider.of(context,listen: false).setEditing(false); + context.push(const MoodPickerRoute().location); + } + return; + } setState(() => activeMood = mood); _playLottieAnim(mood); @@ -132,11 +149,10 @@ class _MoodPickerState extends State with TickerProviderStateMixin { 'mood': mood.name, }, ); - }, borderRadius: BorderRadius.circular(12), child: Opacity( - opacity: activeMood != null && activeMood != mood && isMoodPickerScreen ? 0.4 : 1 , + opacity: activeMood != null && activeMood != mood && shouldDisplayTitle ? 0.4 : 1 , child: Padding( padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 4), child: Column( diff --git a/lib/widgets/nepanikar_button.dart b/lib/widgets/nepanikar_button.dart index 6507df19..71b5bcd8 100644 --- a/lib/widgets/nepanikar_button.dart +++ b/lib/widgets/nepanikar_button.dart @@ -5,19 +5,25 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/generated/assets.gen.dart'; import 'package:nepanikar/app/theme/colors.dart'; +import '../helpers/color_helpers.dart'; + enum ButtonType { primary, primaryAsync, secondary, secondaryAsync; - bool get isPrimary => this == ButtonType.primary || this == ButtonType.primaryAsync; + bool get isPrimary => + this == ButtonType.primary || this == ButtonType.primaryAsync; - bool get isSecondary => this == ButtonType.secondary || this == ButtonType.secondaryAsync; + bool get isSecondary => + this == ButtonType.secondary || this == ButtonType.secondaryAsync; - bool get isAsync => this == ButtonType.primaryAsync || this == ButtonType.secondaryAsync; + bool get isAsync => + this == ButtonType.primaryAsync || this == ButtonType.secondaryAsync; - bool get isOutlined => this == ButtonType.secondary || this == ButtonType.secondaryAsync; + bool get isOutlined => + this == ButtonType.secondary || this == ButtonType.secondaryAsync; } class NepanikarButton extends StatefulWidget { @@ -78,7 +84,8 @@ class NepanikarButton extends StatefulWidget { State createState() => _NepanikarButtonState(); } -class _NepanikarButtonState extends State with SingleTickerProviderStateMixin { +class _NepanikarButtonState extends State + with SingleTickerProviderStateMixin { bool _isLoading = false; late final AnimationController _animController; @@ -137,6 +144,7 @@ class _NepanikarButtonState extends State with SingleTickerProv : _isButtonInteractive ? NepanikarColors.primary : NepanikarColors.primarySwatch.shade500; + final textColor = textColorBasedOnDarkMode(context); return _isLoading ? SizedBox( @@ -145,26 +153,38 @@ class _NepanikarButtonState extends State with SingleTickerProv turns: Tween(begin: 0.0, end: 1.0).animate(_animController), child: widget.buttonType.isPrimary ? Assets.icons.spinner.svg(width: iconSize, color: iconColor) - : Assets.icons.warningWavy.svg(width: iconSize, color: iconColor), + : Assets.icons.warningWavy + .svg(width: iconSize, color: iconColor), ), ) : Row( mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: widget.expandToContentWidth ? MainAxisSize.max : MainAxisSize.min, + mainAxisSize: widget.expandToContentWidth + ? MainAxisSize.max + : MainAxisSize.min, children: [ if (widget.leadingIcon != null) Padding( padding: const EdgeInsets.all(12.0), child: ExcludeSemantics( - child: widget.leadingIcon!.svg(width: iconSize / 3, color: iconColor), + child: widget.leadingIcon! + .svg(width: iconSize / 3, color: NepanikarColors.white), + ), + ), + Flexible( + child: Text( + widget.text, + style: TextStyle( + color: textColor, ), ), - Flexible(child: Text(widget.text)), + ), if (widget.trailingIcon != null) Padding( padding: const EdgeInsets.all(12.0), child: ExcludeSemantics( - child: widget.trailingIcon!.svg(width: iconSize / 3, color: iconColor), + child: widget.trailingIcon! + .svg(width: iconSize / 3, color: iconColor), ), ), ], diff --git a/lib/widgets/nepanikar_dialog.dart b/lib/widgets/nepanikar_dialog.dart index 6642f8fe..2c942156 100644 --- a/lib/widgets/nepanikar_dialog.dart +++ b/lib/widgets/nepanikar_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:nepanikar/app/theme/fonts.dart'; +import 'package:nepanikar/helpers/color_helpers.dart'; import 'package:nepanikar/widgets/nepanikar_button.dart'; enum DialogDefaultAction { @@ -52,7 +53,11 @@ class NepanikarDialog extends StatelessWidget { @override Widget build(BuildContext context) { + final backgroundColor = longTileColorBasedOnDarkMode(context); + final textColor = textColorBasedOnDarkMode(context); + return Dialog( + backgroundColor: backgroundColor, child: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( @@ -60,7 +65,7 @@ class NepanikarDialog extends StatelessWidget { if (title != null) ...[ Text( title!, - style: NepanikarFonts.dialogTitle, + style: NepanikarFonts.dialogTitle.copyWith(color: textColor), textAlign: TextAlign.center, ), const SizedBox(height: 10), diff --git a/lib/widgets/nepanikar_dropdown.dart b/lib/widgets/nepanikar_dropdown.dart index 0304b192..72e7e7a7 100644 --- a/lib/widgets/nepanikar_dropdown.dart +++ b/lib/widgets/nepanikar_dropdown.dart @@ -83,7 +83,6 @@ class NepanikarDropdown extends StatelessWidget { @override Widget build(BuildContext context) { - final isDarkMode = Theme.of(context).brightness == Brightness.dark; const rightPadding = EdgeInsets.only(right: 16); final textColor = customColorsBasedOnDarkMode(context, NepanikarColors.white, _type.textColor); final dropDownColor = customColorsBasedOnDarkMode(context, NepanikarColors.dropdownMenuD, _type.bgColor); diff --git a/packages/nepanikar_data_migration/lib/src/models/my_records/my_records_mood_track_dto.dart b/packages/nepanikar_data_migration/lib/src/models/my_records/my_records_mood_track_dto.dart index 10ae7de3..72529daa 100644 --- a/packages/nepanikar_data_migration/lib/src/models/my_records/my_records_mood_track_dto.dart +++ b/packages/nepanikar_data_migration/lib/src/models/my_records/my_records_mood_track_dto.dart @@ -7,32 +7,42 @@ class MyRecordsMoodTrackDTO extends Equatable { required this.values, required this.summaries, this.descriptions, + this.emotions, }); factory MyRecordsMoodTrackDTO.getAndroidData(Config config, {String sectionName = 'moods'}) { final convertedValues = {}; final convertedSummaries = {}; final convertedDescriptions = {}; + final convertedEmotions = >{}; final confValues = config.items(sectionName); if (confValues != null) { for (final item in confValues) { - String? description; + String? description, summary; + int? value; + if (item.length == 2) { final key = item[0]; final keyValues = item[1]; if (key != null && keyValues != null && key.contains('value')) { final split = keyValues.split('|'); final dateTime = split[0].getIniDateTimeValue(); - final value = split[1].getIniIntValue(); - final summary = split[2].getIniStrValue(); + if(split.length > 1) { + value = split[1].getIniIntValue(); + } + final emotions = split.length > 2 ? split[2].split(',').cast() : [].cast(); if(split.length > 3){ - description = split[3].getIniStrValue(); + summary = split[3].getIniStrValue(); + } + if(split.length > 4) { + description = split[4].getIniStrValue(); } if (dateTime != null && value != null) { final date = DateTime.utc(dateTime.year, dateTime.month, dateTime.day); convertedValues[date] = value - 1; + convertedEmotions[date] = emotions; convertedSummaries[date] = summary!; convertedDescriptions[date] = description; } @@ -44,6 +54,7 @@ class MyRecordsMoodTrackDTO extends Equatable { values: convertedValues.isEmpty ? null : convertedValues, summaries: convertedSummaries.isEmpty ? null : convertedSummaries, descriptions: convertedDescriptions.isEmpty ? null : convertedDescriptions, + emotions: convertedEmotions.isEmpty ? null : convertedEmotions, ); } @@ -56,23 +67,33 @@ class MyRecordsMoodTrackDTO extends Equatable { final convertedDescriptions = {}; final convertedValues = {}; final convertedSummaries = {}; + final convertedEmotions = >{}; if (moodValuesSize != null) { for (var i = 1; i <= moodValuesSize; i++) { - String? description; + String? description, summary; + List emotions = []; + int? value; + final line = config['$sectionName.$i.value']?.toString(); if (line != null) { final split = line.split('|'); final dateTime = split[0].getIniDateTimeValue(); - final value = split[1].getIniIntValue(); - final summary = split[2].getIniStrValue(); - if(split.length > 3){ - description = split[3].getIniStrValue(); + if (split.length > 1) { + value = split[1].getIniIntValue(); + } + emotions = split.length > 2 ? split[2].split(',').cast() : []; + if (split.length > 3) { + summary = split[3]; + } + if (split.length > 4) { + description = split[4]; } if (dateTime != null && value != null) { final date = DateTime.utc(dateTime.year, dateTime.month, dateTime.day); - convertedValues[date] = value - 1; - convertedSummaries[date] = summary!; + convertedValues[date] = value; + convertedEmotions[date] = emotions; + convertedSummaries[date] = summary ?? ''; convertedDescriptions[date] = description; } } @@ -82,6 +103,7 @@ class MyRecordsMoodTrackDTO extends Equatable { values: convertedValues.isEmpty ? null : convertedValues, summaries: convertedSummaries.isEmpty ? null : convertedSummaries, descriptions: convertedDescriptions.isEmpty ? null : convertedDescriptions, + emotions: convertedEmotions.isEmpty ? null : convertedEmotions, ); } @@ -89,7 +111,8 @@ class MyRecordsMoodTrackDTO extends Equatable { Map values, Map summaries, Map descriptions, - ) => MyRecordsMoodTrackDTO._(values: values, summaries: summaries, descriptions: descriptions); + Map> emotions, + ) => MyRecordsMoodTrackDTO._(values: values, summaries: summaries, descriptions: descriptions, emotions: emotions); /// The map of mood track values. /// @@ -97,6 +120,7 @@ class MyRecordsMoodTrackDTO extends Equatable { final Map? values; final Map? summaries; final Map? descriptions; + final Map>? emotions; @override List get props => [values]; diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 810c97c9..00000000 --- a/pubspec.lock +++ /dev/null @@ -1,1334 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "0c80aeab9bc807ab10022cd3b2f4cf2ecdf231949dc1ddd9442406a003f19201" - url: "https://pub.dev" - source: hosted - version: "52.0.0" - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: cb3a948a1eebbf8efd987c43f95418269930e912a88bc7b6a5a7658423133635 - url: "https://pub.dev" - source: hosted - version: "1.0.17" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: cd8ee83568a77f3ae6b913a36093a1c9b1264e7cb7f834d9ddd2311dade9c1f4 - url: "https://pub.dev" - source: hosted - version: "5.4.0" - archive: - dependency: transitive - description: - name: archive - sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d - url: "https://pub.dev" - source: hosted - version: "3.3.6" - args: - dependency: transitive - description: - name: args - sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - assets_audio_player: - dependency: "direct main" - description: - name: assets_audio_player - sha256: dcea8cd9c11cd9c34586f2446bfcdf099362159c56f97517ba941ac151974ea9 - url: "https://pub.dev" - source: hosted - version: "3.0.6" - assets_audio_player_web: - dependency: transitive - description: - name: assets_audio_player_web - sha256: "4575ec40033d818ff022d48f7d46e24c01bc632bd1cd36a4f8b58b38e9aa4a81" - url: "https://pub.dev" - source: hosted - version: "3.0.6" - async: - dependency: transitive - description: - name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 - url: "https://pub.dev" - source: hosted - version: "2.10.0" - auto_size_text: - dependency: "direct main" - description: - name: auto_size_text - sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - awesome_notifications: - dependency: "direct main" - description: - name: awesome_notifications - sha256: "2b430c75cc879d6cfd52bb6eb2b5c1591ed425347816408cdcbd3f6916bba14c" - url: "https://pub.dev" - source: hosted - version: "0.7.4+1" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "7c35a3a7868626257d8aee47b51c26b9dba11eaddf3431117ed2744951416aab" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 - url: "https://pub.dev" - source: hosted - version: "2.3.3" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" - url: "https://pub.dev" - source: hosted - version: "7.2.7" - build_verify: - dependency: "direct dev" - description: - name: build_verify - sha256: abbb9b9eda076854ac1678d284c053a5ec608e64da741d0801f56d4bbea27e23 - url: "https://pub.dev" - source: hosted - version: "3.1.0" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" - url: "https://pub.dev" - source: hosted - version: "8.4.3" - characters: - dependency: transitive - description: - name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c - url: "https://pub.dev" - source: hosted - version: "1.2.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" - url: "https://pub.dev" - source: hosted - version: "4.4.0" - collection: - dependency: "direct main" - description: - name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 - url: "https://pub.dev" - source: hosted - version: "1.17.0" - color: - dependency: transitive - description: - name: color - sha256: ddcdf1b3badd7008233f5acffaf20ca9f5dc2cd0172b75f68f24526a5f5725cb - url: "https://pub.dev" - source: hosted - version: "3.0.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: "961c4aebd27917269b1896382c7cb1b1ba81629ba669ba09c27a7e5710ec9040" - url: "https://pub.dev" - source: hosted - version: "1.6.2" - crypto: - dependency: transitive - description: - name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 - url: "https://pub.dev" - source: hosted - version: "3.0.2" - csslib: - dependency: transitive - description: - name: csslib - sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745 - url: "https://pub.dev" - source: hosted - version: "0.17.2" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be - url: "https://pub.dev" - source: hosted - version: "1.0.5" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" - url: "https://pub.dev" - source: hosted - version: "2.2.4" - dartx: - dependency: transitive - description: - name: dartx - sha256: "45d7176701f16c5a5e00a4798791c1964bc231491b879369c818dd9a9c764871" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - email_validator: - dependency: "direct main" - description: - name: email_validator - sha256: e9a90f27ab2b915a27d7f9c2a7ddda5dd752d6942616ee83529b686fc086221b - url: "https://pub.dev" - source: hosted - version: "2.1.17" - equatable: - dependency: "direct main" - description: - name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 - url: "https://pub.dev" - source: hosted - version: "2.0.5" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 - url: "https://pub.dev" - source: hosted - version: "2.0.1" - file: - dependency: transitive - description: - name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" - source: hosted - version: "6.1.4" - firebase_analytics: - dependency: "direct main" - description: - name: firebase_analytics - sha256: "4d7829b265823ae6aff81656d5ab9d89f61edfa5a6647843ca978bd1d6ec1ae6" - url: "https://pub.dev" - source: hosted - version: "10.1.4" - firebase_analytics_platform_interface: - dependency: transitive - description: - name: firebase_analytics_platform_interface - sha256: b18475c1367622b3a86585d970a755a73d00f320efa8977a46b4768e7752c037 - url: "https://pub.dev" - source: hosted - version: "3.3.21" - firebase_analytics_web: - dependency: transitive - description: - name: firebase_analytics_web - sha256: "30e5631f14bce70592bf9f26672302aafe62fe50b194c4c633c4b977051581ec" - url: "https://pub.dev" - source: hosted - version: "0.5.1+12" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "1c121a478af23755b0b93fd4aa70d3bd32a587dd51ef0a3979091ac0d2317d32" - url: "https://pub.dev" - source: hosted - version: "2.7.1" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: "5615b30c36f55b2777d0533771deda7e5730e769e5d3cb7fda79e9bed86cfa55" - url: "https://pub.dev" - source: hosted - version: "4.5.3" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "0c1cf1f1022d2245ac117443bb95207952ca770281524d2908e323bc063fb8ff" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - firebase_crashlytics: - dependency: "direct main" - description: - name: firebase_crashlytics - sha256: d4190479611f42a34a04630d9188f30b592b65013d0f19c408d5249998429864 - url: "https://pub.dev" - source: hosted - version: "3.0.16" - firebase_crashlytics_platform_interface: - dependency: transitive - description: - name: firebase_crashlytics_platform_interface - sha256: "667955b348b7729ed3d987956021a97fcdd05957df7c27b2d855e6b3f70e9521" - url: "https://pub.dev" - source: hosted - version: "3.3.16" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - fl_chart: - dependency: "direct main" - description: - name: fl_chart - sha256: "749b3342ea3e95cbf61a0fec31a62606e837377b8b6d0caa7367a7ef80f38b7d" - url: "https://pub.dev" - source: hosted - version: "0.55.2" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_animate: - dependency: "direct main" - description: - name: flutter_animate - sha256: "3cb5eb80827abb48e362a9f29f7ba4e1b0edba6b2e2f35b1d350b6ef069bb547" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - flutter_file_dialog: - dependency: "direct main" - description: - name: flutter_file_dialog - sha256: "94c4bc7c2e52fa6b73f4ff2f12e790b47a2564270c4c5b4a3b1d33b8d7236590" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - flutter_gen_core: - dependency: transitive - description: - name: flutter_gen_core - sha256: "8c5edb4db82dadf0d343b1be8bc5e878b66500eb79a583723147b1efb5749ccb" - url: "https://pub.dev" - source: hosted - version: "5.1.0+1" - flutter_gen_runner: - dependency: "direct dev" - description: - name: flutter_gen_runner - sha256: a77b51f955c7d829bf0d077993c88b3be66d496b6a2c2c0d8fdae7b46e0435b5 - url: "https://pub.dev" - source: hosted - version: "5.1.0+1" - flutter_localizations: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_native_splash: - dependency: "direct main" - description: - name: flutter_native_splash - sha256: "048bd1f1dc0e5ea25899f702815934d9a9e916fe23451c320e7dd94d5e3ad933" - url: "https://pub.dev" - source: hosted - version: "2.2.17" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" - url: "https://pub.dev" - source: hosted - version: "1.1.6" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - freezed: - dependency: "direct dev" - description: - name: freezed - sha256: e819441678f1679b719008ff2ff0ef045d66eed9f9ec81166ca0d9b02a187454 - url: "https://pub.dev" - source: hosted - version: "2.3.2" - freezed_annotation: - dependency: "direct main" - description: - name: freezed_annotation - sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 - url: "https://pub.dev" - source: hosted - version: "2.2.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - get_it: - dependency: "direct main" - description: - name: get_it - sha256: "290fde3a86072e4b37dbb03c07bec6126f0ecc28dad403c12ffe2e5a2d751ab7" - url: "https://pub.dev" - source: hosted - version: "7.2.0" - glob: - dependency: transitive - description: - name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: b4bb06205ec607278b6fc23db238278417bca84a3905779cc68d1eb7afae37e2 - url: "https://pub.dev" - source: hosted - version: "6.2.0" - go_router_builder: - dependency: "direct dev" - description: - name: go_router_builder - sha256: a1d988b5583b831ac43f917b9291f2116950245bc9c98b4244d621edcb20b3a4 - url: "https://pub.dev" - source: hosted - version: "1.1.3" - graphs: - dependency: transitive - description: - name: graphs - sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 - url: "https://pub.dev" - source: hosted - version: "2.2.0" - html: - dependency: transitive - description: - name: html - sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269 - url: "https://pub.dev" - source: hosted - version: "0.15.1" - http: - dependency: transitive - description: - name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" - url: "https://pub.dev" - source: hosted - version: "0.13.5" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - image: - dependency: transitive - description: - name: image - sha256: "3686865febd85c57a632d87a0fb1f0a8a9ef602fdb68d909c3e9aa29ec70fd24" - url: "https://pub.dev" - source: hosted - version: "4.0.13" - ini: - dependency: transitive - description: - name: ini - sha256: "12a76c53591ffdf86d1265be3f986888a6dfeb34a85957774bc65912d989a173" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - intl: - dependency: "direct main" - description: - name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" - url: "https://pub.dev" - source: hosted - version: "0.17.0" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" - url: "https://pub.dev" - source: hosted - version: "0.6.5" - json_annotation: - dependency: "direct main" - description: - name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 - url: "https://pub.dev" - source: hosted - version: "4.8.0" - json_serializable: - dependency: "direct dev" - description: - name: json_serializable - sha256: dadc08bd61f72559f938dd08ec20dbfec6c709bba83515085ea943d2078d187a - url: "https://pub.dev" - source: hosted - version: "6.6.1" - linkify: - dependency: "direct main" - description: - name: linkify - sha256: bdfbdafec6cdc9cd0ebb333a868cafc046714ad508e48be8095208c54691d959 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - lint: - dependency: "direct dev" - description: - name: lint - sha256: "3e9343b1cededcfb1e8b40d0dbd3592b7a1c6c0121545663a991433390c2bc97" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - logging: - dependency: transitive - description: - name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - lottie: - dependency: "direct main" - description: - name: lottie - sha256: "49bbc544e44bf0c734ccda29b182e3516a12f5021ea98b206cf31a168b0f97da" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" - url: "https://pub.dev" - source: hosted - version: "0.12.13" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - meta: - dependency: transitive - description: - name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" - url: "https://pub.dev" - source: hosted - version: "1.8.0" - mime: - dependency: transitive - description: - name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" - source: hosted - version: "1.0.4" - multi_select_flutter: - dependency: "direct main" - description: - name: multi_select_flutter - sha256: "503857b415d390d29159df8a9d92d83c6aac17aaf1c307fb7bcfc77d097d20ed" - url: "https://pub.dev" - source: hosted - version: "4.1.3" - native_shared_preferences: - dependency: "direct main" - description: - path: "." - ref: "9c85498974324f927bde0c0dd69bd45fafba71e9" - resolved-ref: "9c85498974324f927bde0c0dd69bd45fafba71e9" - url: "https://github.com/Nepanikar/native_shared_preferences.git" - source: git - version: "2.0.6" - nepanikar_contacts_gen: - dependency: "direct main" - description: - path: "packages/nepanikar_contacts_gen" - relative: true - source: path - version: "1.0.0" - nepanikar_data_migration: - dependency: "direct main" - description: - path: "packages/nepanikar_data_migration" - relative: true - source: path - version: "1.0.0" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "8ebdbaa3b96d5285d068f80772390d27c21e1fa10fb2df6627b1b9415043608d" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - package_info_plus: - dependency: "direct main" - description: - name: package_info_plus - sha256: "8df5ab0a481d7dc20c0e63809e90a588e496d276ba53358afc4c4443d0a00697" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - path: - dependency: "direct main" - description: - name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b - url: "https://pub.dev" - source: hosted - version: "1.8.2" - path_drawing: - dependency: transitive - description: - name: path_drawing - sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 - url: "https://pub.dev" - source: hosted - version: "1.0.1" - path_parsing: - dependency: transitive - description: - name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://pub.dev" - source: hosted - version: "1.0.1" - path_provider: - dependency: "direct main" - description: - name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 - url: "https://pub.dev" - source: hosted - version: "2.0.12" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e - url: "https://pub.dev" - source: hosted - version: "2.0.22" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 - url: "https://pub.dev" - source: hosted - version: "2.1.7" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 - url: "https://pub.dev" - source: hosted - version: "2.0.5" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c - url: "https://pub.dev" - source: hosted - version: "2.1.3" - path_to_regexp: - dependency: transitive - description: - name: path_to_regexp - sha256: "169d78fbd55e61ea8873bcca545979f559d22238f66facdd7ef30870c7f53327" - url: "https://pub.dev" - source: hosted - version: "0.4.0" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" - url: "https://pub.dev" - source: hosted - version: "5.1.0" - platform: - dependency: transitive - description: - name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a - url: "https://pub.dev" - source: hosted - version: "2.1.3" - pointycastle: - dependency: transitive - description: - name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 - url: "https://pub.dev" - source: hosted - version: "3.6.2" - polygon: - dependency: "direct main" - description: - name: polygon - sha256: "9ebff01019574fb77662c5c9a9fa13df63176e9da6850dc594858a3dc0ac53ea" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" - provider: - dependency: "direct main" - description: - name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f - url: "https://pub.dev" - source: hosted - version: "6.0.5" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" - url: "https://pub.dev" - source: hosted - version: "2.1.3" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - rxdart: - dependency: "direct main" - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - sembast: - dependency: "direct main" - description: - name: sembast - sha256: "4997717aa84f0622691815d7e2739988b7f7d3a463302fc878f7d5acfa748e96" - url: "https://pub.dev" - source: hosted - version: "3.4.0+6" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" - url: "https://pub.dev" - source: hosted - version: "2.0.17" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7" - url: "https://pub.dev" - source: hosted - version: "2.0.15" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0" - url: "https://pub.dev" - source: hosted - version: "2.1.3" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874 - url: "https://pub.dev" - source: hosted - version: "2.1.3" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - sha256: "81b6a60b2d27020eb0fc41f4cebc91353047309967901a79ee8203e40c42ed46" - url: "https://pub.dev" - source: hosted - version: "2.0.5" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 - url: "https://pub.dev" - source: hosted - version: "2.1.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 - url: "https://pub.dev" - source: hosted - version: "2.0.4" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9" - url: "https://pub.dev" - source: hosted - version: "2.1.3" - shelf: - dependency: transitive - description: - name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c - url: "https://pub.dev" - source: hosted - version: "1.4.0" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: aef74dc9195746a384843102142ab65b6a4735bb3beea791e63527b88cc83306 - url: "https://pub.dev" - source: hosted - version: "3.0.1" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: e792b76b96a36d4a41b819da593aff4bdd413576b3ba6150df5d8d9996d2e74c - url: "https://pub.dev" - source: hosted - version: "1.1.1" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 - url: "https://pub.dev" - source: hosted - version: "1.0.3" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: c2bea18c95cfa0276a366270afaa2850b09b4a76db95d546f3d003dcc7011298 - url: "https://pub.dev" - source: hosted - version: "1.2.7" - source_helper: - dependency: transitive - description: - name: source_helper - sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "490098075234dcedb83c5d949b4c93dad5e6b7702748de000be2b57b8e6b2427" - url: "https://pub.dev" - source: hosted - version: "0.10.11" - source_span: - dependency: transitive - description: - name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 - url: "https://pub.dev" - source: hosted - version: "1.9.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test: - dependency: transitive - description: - name: test - sha256: a5fcd2d25eeadbb6589e80198a47d6a464ba3e2049da473943b8af9797900c2d - url: "https://pub.dev" - source: hosted - version: "1.22.0" - test_api: - dependency: transitive - description: - name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 - url: "https://pub.dev" - source: hosted - version: "0.4.16" - test_core: - dependency: transitive - description: - name: test_core - sha256: "0ef9755ec6d746951ba0aabe62f874b707690b5ede0fecc818b138fcc9b14888" - url: "https://pub.dev" - source: hosted - version: "0.4.20" - time: - dependency: transitive - description: - name: time - sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124" - url: "https://pub.dev" - source: hosted - version: "2.1.3" - time_machine: - dependency: "direct main" - description: - name: time_machine - sha256: b39511bf66cc8553d86c1242c43dbf67a17c594d5d7124727c1fdbf331e3989d - url: "https://pub.dev" - source: hosted - version: "0.9.17" - timer_builder: - dependency: "direct main" - description: - name: timer_builder - sha256: "67c5653a8d9f6ce62fe9121e520736e9da74418a919fe1c0e181b5d2627dbc4a" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - tuple: - dependency: "direct main" - description: - name: tuple - sha256: "0ea99cd2f9352b2586583ab2ce6489d1f95a5f6de6fb9492faaf97ae2060f0aa" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - universal_io: - dependency: transitive - description: - name: universal_io - sha256: "79f78ddad839ee3aae3ec7c01eb4575faf0d5c860f8e5223bc9f9c17f7f03cef" - url: "https://pub.dev" - source: hosted - version: "2.0.4" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" - url: "https://pub.dev" - source: hosted - version: "6.1.10" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1" - url: "https://pub.dev" - source: hosted - version: "6.0.23" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: bb328b24d3bccc20bdf1024a0990ac4f869d57663660de9c936fb8c043edefe3 - url: "https://pub.dev" - source: hosted - version: "6.0.18" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0" - url: "https://pub.dev" - source: hosted - version: "2.0.14" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615 - url: "https://pub.dev" - source: hosted - version: "3.0.3" - uuid: - dependency: transitive - description: - name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.dev" - source: hosted - version: "3.0.7" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 - url: "https://pub.dev" - source: hosted - version: "9.4.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" - url: "https://pub.dev" - source: hosted - version: "1.0.2" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b - url: "https://pub.dev" - source: hosted - version: "2.3.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - win32: - dependency: transitive - description: - name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 - url: "https://pub.dev" - source: hosted - version: "3.1.3" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 - url: "https://pub.dev" - source: hosted - version: "0.2.0+3" - xml: - dependency: transitive - description: - name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" - url: "https://pub.dev" - source: hosted - version: "6.2.2" - yaml: - dependency: transitive - description: - name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" - url: "https://pub.dev" - source: hosted - version: "3.1.1" -sdks: - dart: ">=2.19.0 <3.0.0" - flutter: ">=3.3.0"