From 8e959ac358aa7fefef33f2a438999ee506d44427 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 22 Oct 2023 12:40:38 +0300 Subject: [PATCH 001/475] Remove comments --- lib/main.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 1d2bdb2..dd24dbc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,15 +23,6 @@ class MyApp extends StatelessWidget { class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - final String title; @override From 736d61c02c511b7f0f3919dec483a803b0fe09dd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 22 Oct 2023 12:42:46 +0300 Subject: [PATCH 002/475] Rename references --- lib/main.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index dd24dbc..7f341fa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,35 +1,35 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(const PoetlumApp()); } -class MyApp extends StatelessWidget { - const MyApp({super.key}); +class PoetlumApp extends StatelessWidget { + const PoetlumApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'Poetlum', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + home: const PoetlumHomePage(title: 'Poetlum'), ); } } -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); +class PoetlumHomePage extends StatefulWidget { + const PoetlumHomePage({super.key, required this.title}); final String title; @override - State createState() => _MyHomePageState(); + State createState() => _PoetlumHomePageState(); } -class _MyHomePageState extends State { +class _PoetlumHomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( From 72e26c6fee855b3d0fb66b41880aef9518b87612 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 22 Oct 2023 12:55:57 +0300 Subject: [PATCH 003/475] Add linter rules --- analysis_options.yaml | 117 +++++++++++++++++++++++++++++++++--------- pubspec.lock | 8 +-- pubspec.yaml | 2 +- 3 files changed, 98 insertions(+), 29 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..ed66032 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,28 +1,97 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml +analyzer: + exclude: + - "build/**" + - "**/*.g.dart" + - "**/*.gr.dart" + - "**/*.config.dart" + - "**/*.freezed.dart" + - "**/generated/**" + errors: + unused_element: error + unused_import: error + unused_local_variable: error + dead_code: error + linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + - unnecessary_late + - null_check_on_nullable_type_parameter + - use_super_parameters + - avoid_dynamic_calls + - always_use_package_imports + - avoid_returning_null_for_future + - avoid_slow_async_io + - avoid_type_to_string + - cancel_subscriptions + - close_sinks + - literal_only_boolean_expressions + - no_adjacent_strings_in_list + - test_types_in_equals + - throw_in_finally + - unnecessary_statements + - always_declare_return_types + - avoid_bool_literals_in_conditional_expressions + - avoid_equals_and_hash_code_on_mutable_classes + - avoid_escaping_inner_quotes + - avoid_field_initializers_in_const_classes + - avoid_final_parameters + - avoid_multiple_declarations_per_line + - avoid_positional_boolean_parameters + - avoid_private_typedef_functions + - avoid_redundant_argument_values + - avoid_returning_null + - avoid_returning_this + - avoid_setters_without_getters + - avoid_types_on_closure_parameters + - avoid_unused_constructor_parameters + - avoid_void_async + - cascade_invocations + - deprecated_consistency + - directives_ordering + - do_not_use_environment + - eol_at_end_of_file + - join_return_with_assignment + - leading_newlines_in_multiline_strings + - library_private_types_in_public_api + - no_default_cases + - no_leading_underscores_for_library_prefixes + - no_leading_underscores_for_local_identifiers + - no_runtimeType_toString + - noop_primitive_operations + - omit_local_variable_types + - one_member_abstracts + - parameter_assignments + - prefer_asserts_in_initializer_lists + - prefer_constructors_over_static_methods + - prefer_expression_function_bodies + - prefer_final_in_for_each + - prefer_final_locals + - prefer_if_elements_to_conditional_expressions + - prefer_interpolation_to_compose_strings + - prefer_null_aware_method_calls + - prefer_single_quotes + - require_trailing_commas + - sized_box_shrink_expand + - sort_child_properties_last + - sort_constructors_first + - sort_unnamed_constructors_first + - tighten_type_of_initializing_formals + - type_annotate_public_apis + - unawaited_futures + - unnecessary_await_in_return + - unnecessary_constructor_name + - unnecessary_null_aware_assignments + - unnecessary_parenthesis + - unnecessary_raw_strings + - use_decorated_box + - use_if_null_to_convert_nulls_to_bools + - use_is_even_rather_than_modulo + - use_named_constants + - use_setters_to_change_properties + - use_string_buffers + - use_test_throws_matchers + - use_to_and_as_if_applicable + - depend_on_referenced_packages + - secure_pubspec_urls \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index aad0600..7950bc7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -66,10 +66,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -79,10 +79,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0fd05d7..742a6f3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,7 +45,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^2.0.0 + flutter_lints: ^3.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From c9b7aada4fc62e28f8d3dab917ece3037c4f33e7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:11:21 +0300 Subject: [PATCH 004/475] Add firebase dependencies --- pubspec.lock | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 5 ++ 2 files changed, 163 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index 7950bc7..698aec5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "76c15c4167f820b74abcd8c6fc5e393e1ed5e1207a34e9b22953603e03b3ba6a" + url: "https://pub.dev" + source: hosted + version: "1.3.9" async: dependency: transitive description: @@ -57,6 +65,118 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + firebase_analytics: + dependency: "direct main" + description: + name: firebase_analytics + sha256: be3edadbd6e3a7840d9eae38ec3dfc6d83efab5c6e0d5cee5f758341d0643c30 + url: "https://pub.dev" + source: hosted + version: "10.6.1" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + sha256: fc7c032f769e2c37bc7aa84eb44b02d17d53de8c256882072dbedbf1def158b8 + url: "https://pub.dev" + source: hosted + version: "3.7.3" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + sha256: "219e5995e79aff8deb3d633041dbdb8f704c1f5a044e970eed141ad851caf9d1" + url: "https://pub.dev" + source: hosted + version: "0.5.5+3" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + sha256: "46129e2733336ac77377174c3ca33a68cca2cd5848504aad63028aeb92afb7b2" + url: "https://pub.dev" + source: hosted + version: "4.11.1" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + sha256: b89936896b2cc02496b97e486793fd4bcf8c51beb99d6a7223c0eea2352d404e + url: "https://pub.dev" + source: hosted + version: "7.0.1" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + sha256: "88b7655c9394d723e121fd53f115d876c32260b8c8b499bdc3108341044a8306" + url: "https://pub.dev" + source: hosted + version: "5.8.4" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "57bba167105d2315d243a4524939406df688f38a5b6d7a4159382bbbe43cdd00" + url: "https://pub.dev" + source: hosted + version: "2.19.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 + url: "https://pub.dev" + source: hosted + version: "5.0.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "0631a2ec971dbc540275e2fa00c3a8a2676f0a7adbc3c197d6fba569db689d97" + url: "https://pub.dev" + source: hosted + version: "2.8.1" + firebase_crashlytics: + dependency: "direct main" + description: + name: firebase_crashlytics + sha256: c58902959e7a73aadcf82c87e8d64f6eb10b5287fd7aaadfab5c2ef34ec11ff5 + url: "https://pub.dev" + source: hosted + version: "3.4.1" + firebase_crashlytics_platform_interface: + dependency: transitive + description: + name: firebase_crashlytics_platform_interface + sha256: "9d2f76bda1542615f75fb501a8c7afc6288d69c3728fdc762bf3d51f0ee0f4cb" + url: "https://pub.dev" + source: hosted + version: "3.6.9" + firebase_database: + dependency: "direct main" + description: + name: firebase_database + sha256: b7e4ab1bc08353adb645aa3b86ceed3e018eea33451732376e751479d13c8e2f + url: "https://pub.dev" + source: hosted + version: "10.3.1" + firebase_database_platform_interface: + dependency: transitive + description: + name: firebase_database_platform_interface + sha256: "69e696ae07ba1f3a9f6e6cda2ee3b78c7426cddf31afd2029e0d594926e95563" + url: "https://pub.dev" + source: hosted + version: "0.2.5+9" + firebase_database_web: + dependency: transitive + description: + name: firebase_database_web + sha256: "77899e5a4dc10b5e5a1fe2ff7840143835644351adff9b64bbf9d48c496ea3a7" + url: "https://pub.dev" + source: hosted + version: "0.2.3+9" flutter: dependency: "direct main" description: flutter @@ -75,6 +195,27 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" lints: dependency: transitive description: @@ -115,6 +256,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + url: "https://pub.dev" + source: hosted + version: "2.1.6" sky_engine: dependency: transitive description: flutter @@ -168,6 +317,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" vector_math: dependency: transitive description: @@ -186,3 +343,4 @@ packages: version: "0.1.4-beta" sdks: dart: ">=3.1.2 <4.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 742a6f3..5e34a6c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,11 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + firebase_core: ^2.19.0 + firebase_analytics: ^10.6.1 + firebase_crashlytics: ^3.4.1 + firebase_auth: ^4.11.1 + firebase_database: ^10.3.1 dev_dependencies: flutter_test: From f5f921dbe3444aa81c85f1d65934443ed4eb0255 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:17:12 +0300 Subject: [PATCH 005/475] Update gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 24476c5..5e3ba35 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,7 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# Firebase-related +android/app/google-services.json +lib/firebase_options.dart \ No newline at end of file From dd12c751165b8d020c0a3cc460936d145a757dd7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:20:20 +0300 Subject: [PATCH 006/475] Enable multidex support --- android/app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/build.gradle b/android/app/build.gradle index e81b21c..9b84540 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -49,6 +49,7 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + multiDexEnabled true } buildTypes { From ca7cd4d09c51562cd4ce50510f39c1762647b687 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:40:53 +0300 Subject: [PATCH 007/475] Add gradle dependencies --- android/app/build.gradle | 6 +++++- android/build.gradle | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9b84540..187ee33 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -2,6 +2,7 @@ plugins { id "com.android.application" id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" + id 'com.google.gms.google-services' } def localProperties = new Properties() @@ -65,4 +66,7 @@ flutter { source '../..' } -dependencies {} +dependencies { + implementation platform('com.google.firebase:firebase-bom:32.4.0') + implementation 'com.google.firebase:firebase-analytics-ktx' +} diff --git a/android/build.gradle b/android/build.gradle index f7eb7f6..f8d824a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,11 +1,12 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.10' repositories { google() mavenCentral() } dependencies { + classpath 'com.google.gms:google-services:4.4.0' classpath 'com.android.tools.build:gradle:7.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } From 2d17e140ab2714058ff1b6081c08ed25a748cb67 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:45:06 +0300 Subject: [PATCH 008/475] Allow internet connection --- android/app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 56343ac..9658178 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + Date: Wed, 25 Oct 2023 16:50:15 +0300 Subject: [PATCH 009/475] Rewrite gradle files --- android/app/build.gradle | 18 +++++++++++------- android/build.gradle | 6 +++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 187ee33..528e571 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,10 +1,3 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" - id 'com.google.gms.google-services' -} - def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -13,6 +6,11 @@ if (localPropertiesFile.exists()) { } } +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -23,6 +21,11 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } +apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + android { namespace "com.example.poetlum" compileSdkVersion flutter.compileSdkVersion @@ -69,4 +72,5 @@ flutter { dependencies { implementation platform('com.google.firebase:firebase-bom:32.4.0') implementation 'com.google.firebase:firebase-analytics-ktx' + implementation 'com.android.installreferrer:installreferrer:2.2' } diff --git a/android/build.gradle b/android/build.gradle index f8d824a..8db8a48 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,13 +1,13 @@ buildscript { - ext.kotlin_version = '1.9.10' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.google.gms:google-services:4.4.0' - classpath 'com.android.tools.build:gradle:7.3.0' + classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.android.tools.build:gradle:7.1.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } From 3e8171d2087fa090fbc0ea0e9d0e9d18f7261ced Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:19:37 +0300 Subject: [PATCH 010/475] Create custom loader widget --- .../firebase/presentation/widgets/loaders/loader.dart | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/features/firebase/presentation/widgets/loaders/loader.dart diff --git a/lib/features/firebase/presentation/widgets/loaders/loader.dart b/lib/features/firebase/presentation/widgets/loaders/loader.dart new file mode 100644 index 0000000..19a0a58 --- /dev/null +++ b/lib/features/firebase/presentation/widgets/loaders/loader.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class Loader extends StatelessWidget { + const Loader({super.key}); + + @override + Widget build(BuildContext context) => const Center(child: CircularProgressIndicator()); +} From 05dcae99a83d8c1608892cf294fb398b19452670 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:19:49 +0300 Subject: [PATCH 011/475] Create InitCrashlyticsWidget --- .../widgets/init_crashlytics_widget.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/features/firebase/presentation/widgets/init_crashlytics_widget.dart diff --git a/lib/features/firebase/presentation/widgets/init_crashlytics_widget.dart b/lib/features/firebase/presentation/widgets/init_crashlytics_widget.dart new file mode 100644 index 0000000..aa1662c --- /dev/null +++ b/lib/features/firebase/presentation/widgets/init_crashlytics_widget.dart @@ -0,0 +1,43 @@ +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +class InitCrashlyticsWidget extends StatefulWidget { + const InitCrashlyticsWidget({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _InitCrashlyticsWidgetState(); +} + +class _InitCrashlyticsWidgetState extends State { + @override + void initState() { + _initializeCrashlytics(); + super.initState(); + } + + void _initializeCrashlytics(){ + _initFatalErrors(); + _initAsyncErrors(); + } + + void _initFatalErrors(){ + FlutterError.onError = (errorDetails) { + FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails); + FirebaseCrashlytics.instance.recordFlutterError(errorDetails); + }; + } + + void _initAsyncErrors(){ + PlatformDispatcher.instance.onError = (error, stack) { + FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); + FirebaseCrashlytics.instance.recordError(error, stack); + return true; + }; + } + + @override + Widget build(BuildContext context) => widget.child; +} From 41cc57730d546dc388e7267da122582db4339ac2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:20:25 +0300 Subject: [PATCH 012/475] Create InitFirebaseWidget --- .../widgets/init_firebase_widget.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/features/firebase/presentation/widgets/init_firebase_widget.dart diff --git a/lib/features/firebase/presentation/widgets/init_firebase_widget.dart b/lib/features/firebase/presentation/widgets/init_firebase_widget.dart new file mode 100644 index 0000000..c3f4156 --- /dev/null +++ b/lib/features/firebase/presentation/widgets/init_firebase_widget.dart @@ -0,0 +1,43 @@ +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; +import 'package:poetlum/features/firebase/presentation/widgets/loaders/loader.dart'; + +class InitFirebaseWidget extends StatefulWidget { + const InitFirebaseWidget({ + super.key, + required this.options, + required this.child, + this.loader, + }); + + final FirebaseOptions options; + final Widget child; + final Widget? loader; + + @override + State createState() => _InitFirebaseWidgetState(); +} + +class _InitFirebaseWidgetState extends State { + Future _initFirebase() async { + await Firebase.initializeApp( + options: widget.options, + ); + } + + @override + Widget build(BuildContext context) => FutureBuilder( + future: _initFirebase(), + builder:(context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return widget.child; + } else{ + return MaterialApp( + home: Scaffold( + body: widget.loader ?? const Loader(), + ), + ); + } + }, + ); +} From 8192462223e17d3e72ce48db7e39f1584f2dd5e3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:21:00 +0300 Subject: [PATCH 013/475] Connect Firebase to the app --- lib/main.dart | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 7f341fa..8e9579c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,26 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; +import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; +import 'package:poetlum/firebase_options.dart'; -void main() { - runApp(const PoetlumApp()); +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + runApp( + InitFirebaseWidget( + options: DefaultFirebaseOptions.currentPlatform, + child: const InitCrashlyticsWidget( + child: PoetlumApp(), + ), + ), + ); } class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @override - Widget build(BuildContext context) { - return MaterialApp( + Widget build(BuildContext context) => MaterialApp( title: 'Poetlum', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), @@ -17,7 +28,6 @@ class PoetlumApp extends StatelessWidget { ), home: const PoetlumHomePage(title: 'Poetlum'), ); - } } class PoetlumHomePage extends StatefulWidget { @@ -31,13 +41,11 @@ class PoetlumHomePage extends StatefulWidget { class _PoetlumHomePageState extends State { @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), - ), - body: const Placeholder(), - ); - } + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text(widget.title), + ), + body: const Placeholder(), + ); } From 6c9412a627f27685bc8c10ea885d4e68d29c4bde Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:39:15 +0300 Subject: [PATCH 014/475] Move app into separate file --- lib/features/application/poetlum_app.dart | 35 +++++++++++++++++++++++ lib/main.dart | 35 +---------------------- 2 files changed, 36 insertions(+), 34 deletions(-) create mode 100644 lib/features/application/poetlum_app.dart diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart new file mode 100644 index 0000000..9e3d5e3 --- /dev/null +++ b/lib/features/application/poetlum_app.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class PoetlumApp extends StatelessWidget { + const PoetlumApp({super.key}); + + @override + Widget build(BuildContext context) => MaterialApp( + title: 'Poetlum', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const PoetlumHomePage(title: 'Poetlum'), + ); +} + +class PoetlumHomePage extends StatefulWidget { + const PoetlumHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _PoetlumHomePageState(); +} + +class _PoetlumHomePageState extends State { + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text(widget.title), + ), + body: const Placeholder(), + ); +} diff --git a/lib/main.dart b/lib/main.dart index 8e9579c..f266b0c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/poetlum_app.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; import 'package:poetlum/firebase_options.dart'; @@ -15,37 +16,3 @@ void main() async { ), ); } - -class PoetlumApp extends StatelessWidget { - const PoetlumApp({super.key}); - - @override - Widget build(BuildContext context) => MaterialApp( - title: 'Poetlum', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const PoetlumHomePage(title: 'Poetlum'), - ); -} - -class PoetlumHomePage extends StatefulWidget { - const PoetlumHomePage({super.key, required this.title}); - - final String title; - - @override - State createState() => _PoetlumHomePageState(); -} - -class _PoetlumHomePageState extends State { - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), - ), - body: const Placeholder(), - ); -} From fb83127e705773f752c89cc001628ddd84ba96ba Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:16:23 +0300 Subject: [PATCH 015/475] Add flutter_launcher_icons package --- pubspec.lock | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 7 ++++ 2 files changed, 111 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index 698aec5..64df0fe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.9" + archive: + dependency: transitive + description: + name: archive + sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + url: "https://pub.dev" + source: hosted + version: "3.4.6" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" async: dependency: transitive description: @@ -33,6 +49,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + url: "https://pub.dev" + source: hosted + version: "0.4.0" clock: dependency: transitive description: @@ -49,6 +81,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.2" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -182,6 +230,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" + source: hosted + version: "0.13.1" flutter_lints: dependency: "direct dev" description: @@ -208,6 +264,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image: + dependency: transitive + description: + name: image + sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + url: "https://pub.dev" + source: hosted + version: "4.1.3" js: dependency: transitive description: @@ -216,6 +280,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" lints: dependency: transitive description: @@ -256,6 +328,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" plugin_platform_interface: dependency: transitive description: @@ -264,6 +344,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.6" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" sky_engine: dependency: transitive description: flutter @@ -341,6 +429,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.4-beta" + xml: + dependency: transitive + description: + name: xml + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" sdks: dart: ">=3.1.2 <4.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5e34a6c..886dc1c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_launcher_icons: ^0.13.1 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is @@ -55,6 +56,12 @@ dev_dependencies: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec +flutter_launcher_icons: + android: "launcher_icon" + image_path: "assets/icon.png" + adaptive_icon_foreground: "assets/icon_foreground.png" + adaptive_icon_background: "#ecfafc" + # The following section is specific to Flutter packages. flutter: From 35213dca0b22dd32cb87b6c4ca4116519dba2e72 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:16:45 +0300 Subject: [PATCH 016/475] Change app's name --- android/app/src/main/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9658178..a6ac7d1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,9 +1,9 @@ + android:icon="@mipmap/launcher_icon"> Date: Wed, 25 Oct 2023 19:16:51 +0300 Subject: [PATCH 017/475] Change icon --- .../drawable-hdpi/ic_launcher_foreground.png | Bin 0 -> 17613 bytes .../drawable-mdpi/ic_launcher_foreground.png | Bin 0 -> 9179 bytes .../drawable-xhdpi/ic_launcher_foreground.png | Bin 0 -> 27603 bytes .../ic_launcher_foreground.png | Bin 0 -> 51648 bytes .../ic_launcher_foreground.png | Bin 0 -> 81797 bytes .../res/mipmap-anydpi-v26/launcher_icon.xml | 5 +++++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 8449 bytes .../main/res/mipmap-hdpi/launcher_icon.png | Bin 0 -> 8449 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 4478 bytes .../main/res/mipmap-mdpi/launcher_icon.png | Bin 0 -> 4478 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 13409 bytes .../main/res/mipmap-xhdpi/launcher_icon.png | Bin 0 -> 13409 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 24960 bytes .../main/res/mipmap-xxhdpi/launcher_icon.png | Bin 0 -> 24960 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 37961 bytes .../main/res/mipmap-xxxhdpi/launcher_icon.png | Bin 0 -> 37961 bytes android/app/src/main/res/values/colors.xml | 4 ++++ assets/icon.png | Bin 0 -> 567017 bytes assets/icon_foreground.png | Bin 0 -> 118949 bytes 19 files changed, 9 insertions(+) create mode 100644 android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-mdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/values/colors.xml create mode 100644 assets/icon.png create mode 100644 assets/icon_foreground.png diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..09bacbbdf71af325ce2d102342b3932725d7ec7a GIT binary patch literal 17613 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 T9a;lCri8)M)z4*}Q$iB}m~SY{ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..cca23996cb41588ca49b2a61fdbfc375d619a3d0 GIT binary patch literal 9179 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..051ab5bfe5a5e323e54be4f6c5b12cbb90baadad GIT binary patch literal 27603 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 Qtm{8HDP_qTag(6`1B1xf(*OVf literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..1b4a8fc948b8777f11221a86a8fbfcaca85756fb GIT binary patch literal 51648 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⩔pozcP# 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⪼bQbQr|F0M@U@NBuF6H#@-3!v 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

-{V}eqpk)=fBpt-Cf*KbXoU7| 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 DKsqtv literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9ed43bd4707f5067daf04c7829c67f55a2a8eb GIT binary patch literal 81797 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%0A8aJcJ1w1--af-YDMmEB_@X)#2hBSK$vg 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml b/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml new file mode 100644 index 0000000..5f349f7 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..22773f5dec053a164b73b1c9e7e6d63ea47e811b 100644 GIT binary patch literal 8449 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 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..22773f5dec053a164b73b1c9e7e6d63ea47e811b GIT binary patch literal 8449 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..7935cfa94277871b7ae7993b55bc5861c7e1aaed 100644 GIT binary patch literal 4478 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 DJvm44 literal 721 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy!iOI#yLg7ec#$`gxH85~pclTsBt za}(23gHjVyDhp4h+5i=O3-AeX1=1l$e`s#|#^}+&7(N@w0CIr{$Oe+Uk^K-ZP~83C zcc@hG6rikF&NPT(23>y!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5d06cd6093245f0ce76a83d20f3a86cce843823a GIT binary patch literal 13409 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 DJvm44 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..bf39a2ea99f34567a8e8a00edf1dc2e367c93f22 100644 GIT binary patch literal 24960 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 diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bf39a2ea99f34567a8e8a00edf1dc2e367c93f22 GIT binary patch literal 24960 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 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..075f098f66a7c5f59cf2ef3f955881bc9a97c102 GIT binary patch literal 37961 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 + + #ecfafc + \ No newline at end of file diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f406297a488931645b41aacfe63b0d5ad4793268 GIT binary patch literal 567017 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

k(|XwsEvG7XCIJj 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⩔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 z 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 V`Nf9aZXafDeubx{YGT_9C9-t%A*nc~j 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

;#>oc=&^YBZQRmS0dt;a@W)ag%n`#I5ImQt8d zNC-e-CI4)FTetEUBUdc=uYKD6A$4Ll-pTa{l89 z<`alGTfN-`N_WTH`50;3ogN)DagYbT=1Ids%74ad7LnYXcnleV%(ET_x#OPs*9&6R zy=nOF6N42(GfgWY?@Oaa!%Kyjpd$%97hmH(>qtds z7Q_==866=c4ZlJ&b3#MH_jq6!N;k=L1pwYeda)?e;-p=aZK13{A#OZdJ%*%-Kb#q> z(8qf^|2fAn@HoteB%bBZ#3=|kh zD{e05*t~#6?FDkS9|Goo;e4)b+16Hpdd2> z(1W@~-t|R$yEv5-=aMWZU6mte)kB&i~>?T6zar}1XTuiE`61W`qY`jXlFoud5@OJ_d-5VW|?`h z2yuL)g-5QkL2B`fT&e0t!3Z1O%*nl(=X?Fh^|j8&A~PCsNE(d{Nxw$C>WGV)6hR~* zOT6|chEr)1z( zM7gxU)7?SU*G8-Ce0U1|OZ=zYtpP@Gs^n)L7w}pbBE2o8Bj)+kN^8?2uo7Pa zlCinX2hYGvY4c%`+_pd^d79m^3{eRk@RyFxR{iDWu;Om&uE)wpr+W#ViH@<`<~KB{ zG@foGMchm|i3nQhR<1DfoS+OvJ1WS}UNoA}z=*F7n1@JLCg$}bt~z*l)2&m{Rw!Wi z2YY0h@!eyBtC<8upOCIH2yifqIID=h#Rela>V~yojLZTIZ|raCCmxmwT#_ zMoLyB1b1l9tF3^8HOrTtH*BV8Pyk^DkwNE-lK|>Vc`x6FvE6#&Tpg~tf2AI!khm5;LwYtkKYy#8 zptL*VVzg*=@pD=Pe?V_8?XAcj-dWg(J?=KIDnbkqHLAF*)pBifh0`p&LzjL5x_2a- zvIRs_g!624i(by(_}m&G55f6f7by_}k;;FAMEtP0+S*zZ6p=|uWiie4ImE$Qya+Oo zfFd-nGxPlMac=Ew9@OROJ6x`adF0*Eq-Lfbi8KY;g1luT5-s zrqpu#xAm1V&Xw_={3jB2sYWS2XAHr?DH#AYT0a&X8gp-cc+K#_j+Ta~K?mV#l~?@9 zTEaiNRr6kWMQd2GyGt4pz%a2#qCP`2M&9;)MH>%C??poW4Kwmk>mwh%Yq;V}u3V1S z7vstkWt0@#=HC=^#vSI15p}|8;FkCWOcQ<2s*~!GR|Jwc|LlV+l)*BibY)WFmQ?$$ zCzl%0GCYi$aBxlNvz$Uqr_ut^Igcp*L&s;ZFy z(U500P=9(TgcR@i00mIxm_c|swZ`ot_JW*JT>PE?Z#vKo2U`~InM z`|wi6GKGLAI)FWdzt;7HH#Q2OFxoQUFD(@(A>goEY(|%)_5ZX0<*}C`Huo+F!tGLG zYFN~qcwiL8 zWDnIpGbO--@gvE1hT+Byn!I4G4cfkAnKdd)=^o}E;9c*X?p9~4xIK4e%R^~=K`;7D zS}sV9-`k7ylA3D?&`v%i{j7m8-X?;{WJ3}LI&az%4kgShB{)4t2=K}xxLJ0`sLzUG z2g<^76^ZgHv#kUdXup$sp!UgtS))uXchSgkV5kC*nO-nRP3MNz3o5MmD$h48CH4f- z?J|BM-g(eke!>L1u&y3V?GW>wl>+a+$Lw2b`#~M>Ks)#Zc~ePbX|v|pB{1zFynL!N z;b>z!?@krv=OGI}*0|F2X zf_G})*nL4_ZfU~$FE_N^@li14=!3aAg0(jw-KYY7y;C^yNAsB3dXe|@|d-nO|BgqqIf z7fTB~$7{Pt@*Mr;oU;**v3%?A*{8kOF*Tg;KfeHSm@E=xqK&}|M1;0AP@b@=r1Nbm zV>bdM=zkB`x`TE_@lC`|gd8%SSn^WCoj&yqnuz89gX7b_bM8O@^_Za>bbY4KhRwfL z61uh6H&Z28!K1hKr?Lefpe}!NWq@?Bv6dB{_!)CWN)b*=ToNUGJ&Sk7$nm?9ADIOd z?LuCEIYI==C5J8Ff2rnrvxq75Bi*)<6kxL;pBAhOMwJX7S{j1p0OEq2G78#4X&&%{ z3nnK6g15=#Jua3%f^Gg%Z1?u*?sm<_J+4{c^m;tYrk03qMxN|Nq*=e z{lN0A;0iYU;R}geGB_L86BufR#G!!!9Z2@&yv3LG_4S~`WYG1v3J+)}t^lw=yGeL? zTp^whqOo|us}oHh*(^Q|4!UVro!!tY($)OgM^&eN$ieTfv6{FJ-n$bHxOoL7H4e!*7)+8i6?3w~~+HdHvY6i1wwb$0qZdTwsa!=nRO zf3!pCDPFs>*W{=F#mBXNk(pX|wLLP}Z*(1tAqO5H6%h7ZF#tKZ)*CEL{ik zp$hv)KULmKbz7>y*ell=)`{r{DlqI((IQQ-%+coM)&2IJT?ezC(BjS|cPM%W_*A;sMaCn|4b=o za5!VJveNCp2!eU3&{LPauxX>s^(3^veu}Fhx4<*!)z`mTL83{;O{}E)a3zKl@-aE; zmkaw;T~!tAXxW}5pFzJK8bfx$Y`s5hk@RV(Jl@^cld7N_ANoigt(H?omz6^l8SvcE zEJZQY!un2J3B9sbDfHUfSB@$)%LtFm|8Xin-0zm{b43jBf!=?LdtEv1royLF2t^`Q z3sD0hsGxSP9WQN~Z%eNbp8Z~C=EoiO=hss^@QSg@53uPZQ4S3|t&aNV`B^3;w5S@N zhI{g-CRD0>;tBi{Z~mkWt($$EkcsupM1$?vMVr{y!X^x&JY}5}T-5NWj;JC5W{n;% zta7-ktU`zq7Ja6O#9unX6@MgzQVz`wWiiZ@B2QyUNWGqo7g@fGsVyYgZ7k;#iZ5mo zS+=PhwP11Ka>IDUFIbHkX;CT%T|#qG7PK4i4Q_!3PtPIZS=u@uvC6HWmJMH62=w+{ z>C3-T zK$sKvPHv8$QvFVM!#Sa)We#uNTuO>n>3DM!llP=%xh-$si~ykWX4>LV$%)Jg$^QJ3>L|?@GT-t8ts3F^JG*v7d zG+-zD$|X`vu~3A`8pvkx9dCdddA}h^wb|{&N#f>lEm)=~C)-uT3?Mu=qh}?V>m(|+ z88J&%o9a_qs8>(RCs}t2JKLGIbc1vkW~E#Mfq^#rUHZGMCR8IXu_gLhAE=?5BT@*i(U(F{gKfI zA4m=dD%8riEDF`~dl)bUm-LC_QUgbHxh~=9}f{dT~Hbkhs2X2vydy}JMc}L{TJn(@<-=@;uAw3 zJMy1KatTXpf!_c$n_VLD$7FwNr97Woqyv_#qyY1GuaMU3sR1?@VG^`XeBdO26Q7a> zG7D%`9H=#XS_--yJFj9t@HHl0Oya zDg7NL9r*UT1g~!7iIG(_GcB!P|8_TEg*37Hx;TYeX*@LkG!MJ}DYY4_j>r zwyISon!GHS`lsgjq0&|%`J}*Z*;jAiaR--=v-xvv`ggp4^y=k4IwdOF_cCsR1jppL zPk8-p?)dI4%lao)NjDAU*{q7eWUBe84Z%mv?{z{1iXJqi?g`Fh!Ls+i4q+Qo&aDM6rDK9)NkzD{s{e zrKZKm0X68gC)4DfsJQ<%J~UTx%|dl>{2k77T5v3e5cVPDmZBH4y?U3IXqv(!Gk@Zd z^l|+J5=_kL<1~-f*E}>4$dgY02u&Nj;a=RK)hyNeAu}_6e$u!UZt$?gUoZ~(=NEfk zgZrAgK`dq{ARzt|nK-*B8VWrFAoFik1DVHvkc#wwkV-_fuGTA%*ejq5_=aL^pi-Ej zVI~LNloIzhqbS7NKG zbys_6R0Iv#(P+58$p_R(@3*dmiQi&9KPk^dbG8Z!kJ!VI&x~WB?_%l?;yn3-!);Fm zihy5)9IwjZd-x40Yw5GiZ?Ddo*HEL({qD07g6}4wxc!7KXmmFIkeu6M4&`KTGexr16tEo%R(UX(d?3 zSrQ~n&(NHNghI!xC*vHH6=vfG}HjYUehp% z*_(+w-jM9r?knLMp{H?oK#m}h8U~u}p2tiu23N}M?v{Vc?}0hEYO8nL%OFr9BYBzO zANMnJ&vM+3$g^5#IT3PsKWLZx&nDe}nTD?lhuEQoCkZu|BjNqL*FO<|xxzb~5#Ebe z7N|0i4YAGGgl^w9tk`I!BNNSMcCeH3p;2fs#3ZjB2Isjdm&v7>tKqb}LQ88LF8m2I z6WhguF7mrOKUu95SCXU&n&usz#40*X!GEoyvaSUzo|s77Zbe+d25XwwynlFk6aKn4 zrySn3bl4(Zs}Or$)$lXYvwmPf#S-5Y#aqBGt9&|||E&=`hFZ&`>B@>^yw;WY_u-{@ ztAoqsUL+2Sq%Ys64iHb4jn&KkT?_z~TE?e)T_g!mfU#H!ftx&X#p+al6$3313I(Mr zT-atu;fGNyVo3nI7ARPsA|Z?mrPAEymCj}OU|+^5_)pqq>W0InNW3D!72gXJ5G63( z8Z#THQVha(7A_ZliL^Vgb0F!JPOwX)hn|SlqMO*E0AOp3?&WUq-Y(bDn*lW`RkB`E zOqpdU{J1Za6EU5(Q!?wNvwM{6{h;h{r&~6}?_}FfQ=&7KFn;FAEs-h6^z2U1m1CC4 zG%Kq6C|S8uW(d8bAP4D&yXcg0NK1UB4&BeaE~dO7C7!##KMAO(a__Xv?8Sd>DrWLy z{Cgw)R^rC#B3%>5?M=6BZ#JfmrqtgK6iD&161=7BMDtJwa|G41g*Wut(-S3hKWh|U zU^NXw+p6#CkF|)Y!(*yR&cO2GTg*VjxMog?xX}X$h`)~v)q5?At_c6r=K~wVG^4mS zKn;2kwzvFgy^pMu;>QF$QkpRXF3daLEZPotM*1V{ptraijG(}c$KPvTNp@Y3s(+%c z+I^n$Ek;75;NTetZoK z93tb_BON{u@$)%KCb{3`AJD?^m5n_*)T$Dmvc8DRl4)O3 z;?1>ZI@6M4Vamj;(4|ThMLJk@P7z#Rtcu*DOmr3;O%Y5LnTdofVS^$#t#Z9VF|%C7 zWu#4&ycGHw$WyaSdWzPb45}L!QcQiOyF{v1Y306`o;-gJtM>34V|1xi^SN{+`}?G= z0*6NK1A!QU8U9rH*ldjAhYV-YOww6bc1>T8v@3JQsLv}wOj>f^cbjmg6dGlo`&#BK zzk><_tpz&9m=Eb5cXZcX+T94QW9>`4hxY&C@6V`%Xpp2FY?O$cA@yen7S36IfINz( z;jP-BsJ!#$TeY~<$M;pU*RxHAb%$GC#=hOM(jGfW!d(h+GaB`ifW6pAR@1YIx!*RD z0-hOY>u78Bf0AzSYzvqdR!eR#OozgZqIQFAf4Voxp0pl> z-4F@oTFdz8pDzxqFQoVxl9S21&(W74Gr{Y><@Rb2D}@oaT>&e43!b|)tWYyJhtRAW zWZIpsRG7%StS^1S9K0U>jqd%NTz$QJ3FmkhqQo1f>q=*XX4n`+3%s{6S|OfeTyMU3 zSahkbD+QXJDU=e}^5uWH=CCGelMuCXb?W_+&zYOy_FAz7;${MC&OicfEi8%Yg)o%2 za74RrWjn#-JStz=hZC^ng#N_K#}BO5hwE7?mK4Sj#3L_e*9s|@;MXvHcLjn56ViQk zlxkOAWqI|RvOE2b~|tLZoa}=q4LRj5!vic-v3jr*8?>e|=a_ zft0GPd+^}sh~N{*_MKkLn~e%bTfqr4zAL=16KNfPKNTk%dwmRdct4cG;*_b7ij4#* zq`R;rCZxPfPQGP%Pt&oXAHynxIei%UQTOs|6B_MrvY7Bh%jv1qvWWAFbmmLck(L5l z+J2OC&s|_$vdIFncF$3iTn_%o+Ak;l{<7VF-eLQ#>mFq!SI?(Ossm--d$O6o-kUl! z@Yp8Ztiq={FuKRGw>JUlQBw5i-Z1=IRoEc#bvtoy6AM2GfC?Dy8dv;3ry#9ni{lm_yr}UNM)PN z$k?_g4&RBK*UOpr?a@ zLxqYf9m0$U212>T6Uczq^WMlVK2*@jU>Hy}k!>G%siJG9vb2S*Lez?E z?_cEhHTr4-0jsqyy)X|yt6sOLAE4g3cjOZ&(#68dHj-VE^y~Bf3cA#*rNwwz&F{=* zs~@uz3HW5NLy=p$IRnpj1pINfY4vIW%!8gF_hd{&EOPW@j#>PfZ)}-+e(B)#dOdR- zQ}OLID~c4V>(rUXV?wX(1m1N`QLAO@RLs#hBfmbd;{A5gjCkFM$4tpS8lq{w8#ukQ z-?4!x^ceIyL58G^%7KuS6|PyuV33U~^dWdH;)t?5A*9Z*!0nR@&S=~8=IVu~^vs+6 zaFwCO=o?|i^;XN$G#PhRFMA56Uc1TnK7;YyzKC=&MKCtES8T?$$zMO|(5?CwDmY=2 zdn6p}5G*OQ$aW%0IKlIYf5TT*8;^q;+-W!=(w^a6@^9L@2GfZNyG;4&-}uBhLnoBb ze#W%Sku;ORhIqlMT}CQ;coDK1}ipa_yZ)9>1ZGToSrc!#Q# z$>8~+{4OSBMFtbJL~G+F>R@p3f|R}Buje20IOp;TBllD%RK=S~Gy}@nSYN-3w9>}9 zYYLn9bQfdw&jUz`?AcQ~F-b49YAN!H(tCt*s~8QK&2IJFG@%)H$d!ZQ+@EDD$KRVt zDYX*5T%Y+Tf+umC6l6}^P9;Q8`$u)@kv1Wn|(`F=-}%$cpqKJb}c6d z8@H?VBH_E_ONYroKd$;mm7p4uT-;i&3}@8AkhNN@w+uCYR)L!<9Cdy~yePi@dqq_9 zIE3#9Uv8v)hptvb@2$I;o(=gDUxW6BIDad!Am?IUz5DI-Gr~&qG{wi1!+U#TRa>*u zYOhr$9K{=F{ki;;?mcIVaXFj@gYy#rQrbJ3G% zUPuOJlF4O0pP9u@`i2eZ&E=r>Z#BFwn_Ir<{Nj7KfK}eP>2A-qmz_anpWb~H8@a{) zY=XeFEh$Q;{%N!(_f>#{DdWkMi;^lMOG3}&f3~bT%?6?>roW@VY1}0M!$G(U?h32Z zI7umd*7UG<>8WAUw~s8?keGFXCf6$aqtvTisAIol3#C%$JkPxHoU&JvO(bH??D*w>PJ4ZI^?v#l^|g3<&fN8ORqSqBn^zmFx_$lN zjyWG@)0K7F_D-m-z2qn#ZDLzrqV0pbf;vm+19ri-sAh);Uhn24ubw%w^1S+^a5pyo8?jfI)%7-Zd5N0k zWk3+`Duu)u=pmX#fD+;W*YfVv;N+y?-*u4eJ7@C=kTzT!H7Yh%9dTcXLY<|_-u~b& zf{bc11MkL6E2<40Y7lMFezus^AIxR;V2w36fHPcTVK$DdQBKE{P?Q#O-}ZUv>OLH} z!=t6wM)PUo!_A|VDHcZfCSRNfU3W@ffr=HV24U!OG6uN3#2~g1j36vy&ci>k-FQyZK4DkMp_8T0#WM$dI znX}9s(x>()VSKtK@0}r^b2mp+BWhV~2O+Gcy#$o--s`b4~AH#_3)dbYB*H>Bk7Tx}#BWi*ho z^bAgw%7kC9VmthO@AE6H`7NitE0?wN z;3B?VZ`mBeKg`nlc}RH$F%a$cor^&fVr`-~?04)lICN)XDFNm2-nAh`mEr>}P#*%c zV7bgA#-DX=em#@ky01(i<&>VZGLdEyL?@fn%yI>Nj$3TaE_{?V=uG!HD@(EBf@XbT zp~L)`*HF-CM1~M_)V##dO!nb2%_-%vN@`T6?to^-mKXeIo+Cv^s^OAWYNZY;^Qm!p ztDbnzkNj6Y(P|42DHFcE%Z{FkRmPJ78G{Ril!wkxj`>QqbpiZEN$z@2e0?LC)AF=UG(wnWYJxX8O9=0KSW_xD zJV*9*zqZ}tJ<*u4DVV{Vec8&chL6OxYU}+`oqT-f)@qk4G}Nl)~L}wR+=Jcvx(Urnr;i5J)n;{Atmn;*!PF}nJ##i9?Iy1#d$hqFV?Se}lhhX9< ze?@5b`@0zK!0Ucz9gAj-`3FAtyywWpIhWd^)M%7a>#FC#D*x);C-zyVE#VXCrJ}b;K}r7^jpO}N46Y;fB#M#y7NP<2l&q`&DeFDWRtx(TZq(W6GvB_ zH(&a8)?61toXbo$ENu)po}JbEA>&H%Q;+mUbVX$A(^|&Z7ycYl;V)tWX?hEnH<=4>rFH9N5!(mx|Ec?PwGbXsuLzPkWMfX_S|keMUL1M z4E#`IawCqSjXvvHrfjjis*TDP`n?97Ew4(y$|?Q&;iq~gImNR9UCJdT+Qe0_Pvw#P zRxLqM;?M-2x#{;k=PA@(PZL}cUM(--_74YE?#F>8s4@c|y*X(!snyMxA7AIf6phuU zCA0s2SPNbc$mM!4Vq4uf6j%ucOBxL&#$+ic7mceNFy!xpg(5WI@0Km8s}fHooe`}2 z9t)ooA#b9^Yt9r4bcr-+xhdfKe>GW1z=v~Beo~Cs`uSk&gBYWB=v!dMv-Tcbr(>=r z`mlc#GymEKTN5Ob!6RV!`Qe;q-Ycba^ca^&ZE*q`AyTdx+D%f>t=rPQJth)$h;_v! zoh#{d>(`}~E15f+X5{3rgVh$_H#Czr z4&i^TbN~XiGqXtu?e=t8pRC$S)#jl6@qLwzOWzO8Lf7lDjYjexNo|fGq zEjNpKf8v|cWu;Hy)6#0^q!^=bP9D)y@{I-9H2Y`bpn#3mGqoo`$MUj~w8>zQ z#ufj7B`vl#hF269D(zLjgg?SCK0|tKNkC?H@EFC4b z!|+j8pvHD=ekc)9i*k;SxS*jSpzZp;Muok5)d>$fiOz-nPS5wq1 z(OMKJIuGaQ%iLE?JNZfQx&He)*UE+brZA=!>Tjz9P6JrzbV~0g;ioZts=`2(s{}>ug|EIE zWhWt8y!0HBvd%KX6v(Y=VZeC(6zZV*HYb46sOTe$Y#7=bJU+_jxpn+|2-9e-_2w-I zTFZq4ZpE(6{?I5?f3W@(kahInp)|}iAG_-*Cs&)eRx~)b0XSNaw5_xFMZ5EF_TBMH zxGdN!eOc*b))xZPEdK|_k66nQb+qSb)!{A;Qc|PHz#2O@{utWM<~o(bmc6geJ9#I8 zMp~lNRX-@RZ`J<@y>r!9e&CaH{|Tm;Zk8Z9QdLS-n^`Zj1QY$>4I*%%Q&lN8cg@pNT#;bC-lqY9vF7ibd>dxQy5|BLZ;C?vX+ajfH&#n{C@uVL8JH8 zqg|)$-@RuPZVir9(=O8ys4ljROZjGqLl$EJ6D(ZAlK&wSlitTm0KZS1zeWJRn=B)% zLieC2{-GOAYNb-eJigD0zqfzf2J5l4#ZROGq*UBpuA&4%{Jx-T%j#LiDYlE@T1^s_ zch7yEXgJXHm!f2yb!!7y0$51i6RtW>gQt2?g|mz!K}2MpacgU*I4sTk!fJDc?|tTp z2a6?)jk>NXzSVZ8GEx>*<}+4KJOc60T~mKeRp!gPev0ofxa>x6e=vwjZ6C zR=#rnwc{m~K8a*ZfMkR7iW&egI?#x=TE7pQ(|3CgB79lPk8$Lz&{9p1!XvYHbdQtp zl3XDcs%oWN{R#PxxSnX;%C7y%rDQ)quz>VDIe2ywLbSJdi)OVEYPRDAX!c5 znd8x!-ET46&$v4C7@2zZ1MSVZwH5jlq13WkDAMEdBTYC^6w`9)+8)=pXEI>yQpuV> zJA!%9<}*KL$sxEN$Zf$X@LHY!doY{ekVwYUOgAX923x@>{hbmbk%iKgAajqxhi3_3C+QnU+gQ>i9D5E zQi}Q>bEocpBOBFc7C}LR=4OHLV&9X+oE5Prqs7y|4pR5;1wc#|V%(sJcTM5=js}S; zu*Ql9rt5w`hM6x-)jS&GOSE)I0jfYqewTu&+KdBt=@BBh<;AIY7<-%O*{3FF)h=5w z-rW*SbX20Y=H}$2nwe){J5+7f{Z`{PPzYYA=#MIm2G3oOK^Bd7fmG7T_ohcYy-Oo% z;67!>LZB?Jzxg)cOV4jyNmp~VMZMQd*x02!?Q}a))kNLxNP{j zyy}(_MPGXj_J1JUgh9FI$yAV{vpXWzGzJ{MQ9WUB<2^#y9J8yugJ35uHK3A ziP6Aefol#Gwe9PlmnZFmMANdIFNar7^AU&}_4^pd0iDW6d?qJImsggIL2}%n;XWNA z#|At<C#d)^R1xu<6&FzH96TbQ znSQx;XT)2Bui=duhxN*}^6M0z9IEFZRpLjLs!Cf0V93f-3=I{$-AtUaQNq_Zz3Y)? zC!Y^ql}ApNKBwN5S?q@8n4{k}%V==-MQ=S0;22Z()`{vA@3I7J$5R=f0*)S z`y}1CO@^1+#g9q-4sPBYJg350B+583sB`@j8W9qTtA4J;;J&ruDWhI}gc=~0Z^v>{NB zCM1xQuBm>A{gDbtwna+?9B2_5J9FRMZVivTeuQ(gY-NkE?oOkfV};z&j{OUQUdF!S zaN%FCa?yI`PW3cuKleqv>vXvkw5S5$BvwB>GHy}6IbVi3P5LEtkMy;CAEO-1bDZ=5 z4>!5M=wOsS&MWzN#SC$1U1k7Ck1QAJbh3$vSF7pg5^rx%StL^CUqBY2+#X8LS9y8y zrL`8L!h42gpD0_qfN8>Ji7P*JC8Ted7#SfBp-K1qle9#?sNkGm$}eq~mTYK54o5uy zh-3b19N<9z9p&iv}^RFS)wW9R2EbX&+ir+@_s<7AcR_B4DWtZ|*eU&?c zIew1Kv#M&{`YpM(-$2OJhM5aJdGmTA#3zJⅅ5SnfaKV+rNHxJpUpKtu$A2#8fs} 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

FY8`!%0UdBZw9 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 e>x^{vr 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%0DO|TBM1<*tr$FKM;AxaD|ZiX^$eS0JvtvvO*KvG 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 Date: Fri, 27 Oct 2023 22:00:17 +0300 Subject: [PATCH 018/475] Create custom AppBar --- lib/features/application/poetlum_app.dart | 7 +++--- .../presentation/widgets/AppBar/app_bar.dart | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 lib/features/application/presentation/widgets/AppBar/app_bar.dart diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 9e3d5e3..fa825e5 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @@ -26,9 +27,9 @@ class PoetlumHomePage extends StatefulWidget { class _PoetlumHomePageState extends State { @override Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), + appBar: CustomAppBar( + title: 'Poetlum', + backgroundColor: Colors.grey[350]!, ), body: const Placeholder(), ); diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart new file mode 100644 index 0000000..06cadf3 --- /dev/null +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + const CustomAppBar({super.key, required this.title, required this.backgroundColor}); + + final String title; + final Color backgroundColor; + + @override + Widget build(BuildContext context) => AppBar( + centerTitle: true, + backgroundColor: backgroundColor, + title: Text( + title, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ); + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} From cd00516c5b07caa22de2eefced1d3e6a6aef4d8c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 27 Oct 2023 22:07:43 +0300 Subject: [PATCH 019/475] Remove centerTitle property --- .../application/presentation/widgets/AppBar/app_bar.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index 06cadf3..a8f07cf 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -8,7 +8,6 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) => AppBar( - centerTitle: true, backgroundColor: backgroundColor, title: Text( title, From f239ba447ccac43aaecfd94bab6db9b337330ce1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 1 Nov 2023 13:47:27 +0200 Subject: [PATCH 020/475] Create mixin for rotating buttons --- .../AppBar/buttons/rotating_button_mixin.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart b/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart new file mode 100644 index 0000000..68b6bf9 --- /dev/null +++ b/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +mixin RotatingButtonMixin on TickerProvider { + late AnimationController rotationController; + late Animation rotationAnimation; + + void playAnimation() => rotationController..reset()..forward(); + + @mustCallSuper + void initState() { + rotationController = AnimationController( + duration: const Duration(milliseconds: 500), + vsync: this, + ); + rotationAnimation = CurvedAnimation( + parent: rotationController, + curve: Curves.easeOutCubic, + ); + } + + @mustCallSuper + void dispose() { + rotationController.dispose(); + } +} From 5a9ca0c26db2a3d2b3609f5d6c97d6e885a23bcf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 1 Nov 2023 13:49:06 +0200 Subject: [PATCH 021/475] Create menu button --- .../widgets/AppBar/buttons/menu_buton.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart b/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart new file mode 100644 index 0000000..1c733e1 --- /dev/null +++ b/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart'; + +class MenuButton extends StatefulWidget { + const MenuButton({super.key}); + + @override + State createState() => _MenuButtonState(); +} + +class _MenuButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { + @override + Widget build(BuildContext context) => RotationTransition( + turns: rotationAnimation, + child: IconButton( + onPressed: (){ + playAnimation(); + // ... + }, + icon: const Icon(Icons.menu), + ), + ); +} From 3ca21aa4ddf52f769638c89c12b6f4384f8be595 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 1 Nov 2023 13:49:13 +0200 Subject: [PATCH 022/475] Create settings button --- .../AppBar/buttons/settings_button.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart new file mode 100644 index 0000000..4b5aed8 --- /dev/null +++ b/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart'; + +class SettingsButton extends StatefulWidget { + const SettingsButton({super.key}); + + @override + State createState() => _SettingsButtonState(); +} + +class _SettingsButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { + @override + Widget build(BuildContext context) => RotationTransition( + turns: rotationAnimation, + child: IconButton( + onPressed: (){ + playAnimation(); + // ... + }, + icon: const Icon(Icons.settings), + ), + ); +} From 3114acf12416b8a12470e7d81bdc9bbc7a3d2dee Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 1 Nov 2023 13:49:23 +0200 Subject: [PATCH 023/475] Add buttons in the appbar --- .../application/presentation/widgets/AppBar/app_bar.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index a8f07cf..5f55d8e 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { const CustomAppBar({super.key, required this.title, required this.backgroundColor}); @@ -8,7 +10,11 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) => AppBar( + leading: const MenuButton(), backgroundColor: backgroundColor, + actions: const [ + SettingsButton(), + ], title: Text( title, style: const TextStyle( From 680aaad7cf7639a69673c15a5d190cec4b05953e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:29:45 +0200 Subject: [PATCH 024/475] Fix dispose issue --- .../presentation/widgets/AppBar/buttons/menu_buton.dart | 6 ++++++ .../widgets/AppBar/buttons/rotating_button_mixin.dart | 5 ----- .../widgets/AppBar/buttons/settings_button.dart | 6 ++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart b/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart index 1c733e1..1d86ec2 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart +++ b/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart @@ -20,4 +20,10 @@ class _MenuButtonState extends State with TickerProviderStateMixin, icon: const Icon(Icons.menu), ), ); + + @override + void dispose() { + rotationController.dispose(); + super.dispose(); + } } diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart b/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart index 68b6bf9..e40ef93 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart +++ b/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart @@ -17,9 +17,4 @@ mixin RotatingButtonMixin on TickerProvider { curve: Curves.easeOutCubic, ); } - - @mustCallSuper - void dispose() { - rotationController.dispose(); - } } diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart index 4b5aed8..ada8d67 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart @@ -20,4 +20,10 @@ class _SettingsButtonState extends State with TickerProviderStat icon: const Icon(Icons.settings), ), ); + + @override + void dispose() { + rotationController.dispose(); + super.dispose(); + } } From 0b3734d0d4c830e79707ae8bc9f3fd213a88bdae Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:11:52 +0200 Subject: [PATCH 025/475] Install packages --- pubspec.lock | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++- pubspec.yaml | 3 +++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index 64df0fe..970b48e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.2" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f + url: "https://pub.dev" + source: hosted + version: "5.0.1" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" convert: dependency: transitive description: @@ -105,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" fake_async: dependency: transitive description: @@ -113,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" + source: hosted + version: "2.1.0" firebase_analytics: dependency: "direct main" description: @@ -256,6 +288,22 @@ packages: description: flutter source: sdk version: "0.0.0" + get: + dependency: "direct main" + description: + name: get + sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e + url: "https://pub.dev" + source: hosted + version: "4.6.6" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: f79870884de16d689cf9a7d15eedf31ed61d750e813c538a6efb92660fea83c3 + url: "https://pub.dev" + source: hosted + version: "7.6.4" http_parser: dependency: transitive description: @@ -320,6 +368,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" path: dependency: transitive description: @@ -447,4 +503,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.1.2 <4.0.0" - flutter: ">=3.3.0" + flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index 886dc1c..2cd2ae5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,9 @@ dependencies: firebase_crashlytics: ^3.4.1 firebase_auth: ^4.11.1 firebase_database: ^10.3.1 + connectivity_plus: ^5.0.1 + get_it: ^7.6.4 + get: ^4.6.6 dev_dependencies: flutter_test: From cc2d1b90ec98aacbf48fafd7394867afc625872c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:30:01 +0200 Subject: [PATCH 026/475] Create network controller --- .../network_controller.dart | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 lib/core/network/internet_connection_monitoring/network_controller.dart diff --git a/lib/core/network/internet_connection_monitoring/network_controller.dart b/lib/core/network/internet_connection_monitoring/network_controller.dart new file mode 100644 index 0000000..1099ffd --- /dev/null +++ b/lib/core/network/internet_connection_monitoring/network_controller.dart @@ -0,0 +1,36 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class NetworkController extends GetxController { + final Connectivity _connectivity = Connectivity(); + + @override + void onInit() { + super.onInit(); + _connectivity.onConnectivityChanged.listen(_updateConnectionStatus); + } + + void _updateConnectionStatus(ConnectivityResult connectivityResult) { + if (connectivityResult == ConnectivityResult.none) { + Get.rawSnackbar( + messageText: const Text( + 'Internet connection is lost', + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + isDismissible: false, + duration: const Duration(days: 1), + backgroundColor: Colors.red[400]!, + icon : const Icon(Icons.wifi_off, color: Colors.white, size: 35,), + snackStyle: SnackStyle.GROUNDED, + ); + } else { + if (Get.isSnackbarOpen) { + Get.closeCurrentSnackbar(); + } + } + } +} From 93114310856eb6b9a540ba67a0c6e9223a1db2e6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:30:14 +0200 Subject: [PATCH 027/475] Create DI for the controller --- .../network_controller_injection.dart | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/core/network/internet_connection_monitoring/network_controller_injection.dart diff --git a/lib/core/network/internet_connection_monitoring/network_controller_injection.dart b/lib/core/network/internet_connection_monitoring/network_controller_injection.dart new file mode 100644 index 0000000..398830a --- /dev/null +++ b/lib/core/network/internet_connection_monitoring/network_controller_injection.dart @@ -0,0 +1,8 @@ +import 'package:get/get.dart'; +import 'package:poetlum/core/network/internet_connection_monitoring/network_controller.dart'; + +class NetworkControllerInjection { + static void init() { + Get.put(NetworkController(), permanent:true); + } +} From fe4570d983575028a73c25446fc49256bac6dff0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:30:27 +0200 Subject: [PATCH 028/475] Implement network monitoring --- lib/features/application/poetlum_app.dart | 3 ++- lib/main.dart | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index fa825e5..0429638 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @override - Widget build(BuildContext context) => MaterialApp( + Widget build(BuildContext context) => GetMaterialApp( title: 'Poetlum', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), diff --git a/lib/main.dart b/lib/main.dart index f266b0c..7a540be 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/core/network/internet_connection_monitoring/network_controller_injection.dart'; import 'package:poetlum/features/application/poetlum_app.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; @@ -7,6 +8,8 @@ import 'package:poetlum/firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + NetworkControllerInjection.init(); + runApp( InitFirebaseWidget( options: DefaultFirebaseOptions.currentPlatform, @@ -15,4 +18,6 @@ void main() async { ), ), ); + + } From 9cc734c9304e94402a739722e5cd1a9098fb063f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:34:21 +0200 Subject: [PATCH 029/475] Move network controller init into separate widget --- .../widgets/init_network_controller.dart | 23 +++++++++++++++++++ lib/main.dart | 16 ++++++------- 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 lib/features/internet_connection_monitoring/presentation/widgets/init_network_controller.dart diff --git a/lib/features/internet_connection_monitoring/presentation/widgets/init_network_controller.dart b/lib/features/internet_connection_monitoring/presentation/widgets/init_network_controller.dart new file mode 100644 index 0000000..72e44da --- /dev/null +++ b/lib/features/internet_connection_monitoring/presentation/widgets/init_network_controller.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/core/network/internet_connection_monitoring/network_controller_injection.dart'; + +class InitNetworkController extends StatefulWidget { + const InitNetworkController({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _InitNetworkControllerState(); +} + +class _InitNetworkControllerState extends State { + @override + void initState() { + super.initState(); + + NetworkControllerInjection.init(); + } + + @override + Widget build(BuildContext context) => widget.child; +} diff --git a/lib/main.dart b/lib/main.dart index 7a540be..ddc64ea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,23 +1,21 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/core/network/internet_connection_monitoring/network_controller_injection.dart'; import 'package:poetlum/features/application/poetlum_app.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; +import 'package:poetlum/features/internet_connection_monitoring/presentation/widgets/init_network_controller.dart'; import 'package:poetlum/firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - - NetworkControllerInjection.init(); runApp( - InitFirebaseWidget( - options: DefaultFirebaseOptions.currentPlatform, - child: const InitCrashlyticsWidget( - child: PoetlumApp(), + InitNetworkController( + child: InitFirebaseWidget( + options: DefaultFirebaseOptions.currentPlatform, + child: const InitCrashlyticsWidget( + child: PoetlumApp(), + ), ), ), ); - - } From 20e1b310e91d0f630c5204de5e58e442d73136c4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:35:03 +0200 Subject: [PATCH 030/475] Make main non-async --- lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index ddc64ea..1ef7e1e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,7 @@ import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_wid import 'package:poetlum/features/internet_connection_monitoring/presentation/widgets/init_network_controller.dart'; import 'package:poetlum/firebase_options.dart'; -void main() async { +void main() { WidgetsFlutterBinding.ensureInitialized(); runApp( From bf4c773694316081d8288b7d78f757f5ffa6749f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 7 Nov 2023 11:15:12 +0200 Subject: [PATCH 031/475] Fix dispose issue --- .../presentation/widgets/AppBar/buttons/menu_buton.dart | 6 ++++++ .../widgets/AppBar/buttons/rotating_button_mixin.dart | 5 ----- .../widgets/AppBar/buttons/settings_button.dart | 6 ++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart b/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart index 1c733e1..1d86ec2 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart +++ b/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart @@ -20,4 +20,10 @@ class _MenuButtonState extends State with TickerProviderStateMixin, icon: const Icon(Icons.menu), ), ); + + @override + void dispose() { + rotationController.dispose(); + super.dispose(); + } } diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart b/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart index 68b6bf9..e40ef93 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart +++ b/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart @@ -17,9 +17,4 @@ mixin RotatingButtonMixin on TickerProvider { curve: Curves.easeOutCubic, ); } - - @mustCallSuper - void dispose() { - rotationController.dispose(); - } } diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart index 4b5aed8..ada8d67 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart @@ -20,4 +20,10 @@ class _SettingsButtonState extends State with TickerProviderStat icon: const Icon(Icons.settings), ), ); + + @override + void dispose() { + rotationController.dispose(); + super.dispose(); + } } From 01881ea242497c4075482310152e9d26be467e5c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:30:19 +0200 Subject: [PATCH 032/475] Add DI package --- pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index 64df0fe..2b580cf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -256,6 +256,14 @@ packages: description: flutter source: sdk version: "0.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: f79870884de16d689cf9a7d15eedf31ed61d750e813c538a6efb92660fea83c3 + url: "https://pub.dev" + source: hosted + version: "7.6.4" http_parser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 886dc1c..302675c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: firebase_crashlytics: ^3.4.1 firebase_auth: ^4.11.1 firebase_database: ^10.3.1 + get_it: ^7.6.4 dev_dependencies: flutter_test: From 925837987442eb5b25da199f56ac6ec90785328c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:30:53 +0200 Subject: [PATCH 033/475] Create database manager --- .../data/data_sources/database.dart | 3 +++ .../domain/entities/database_manager.dart | 20 +++++++++++++++++++ .../database_manager_interface.dart | 4 ++++ 3 files changed, 27 insertions(+) create mode 100644 lib/features/realtime_database/data/data_sources/database.dart create mode 100644 lib/features/realtime_database/domain/entities/database_manager.dart create mode 100644 lib/features/realtime_database/domain/repository/database_manager_interface.dart diff --git a/lib/features/realtime_database/data/data_sources/database.dart b/lib/features/realtime_database/data/data_sources/database.dart new file mode 100644 index 0000000..19f0356 --- /dev/null +++ b/lib/features/realtime_database/data/data_sources/database.dart @@ -0,0 +1,3 @@ +import 'package:firebase_database/firebase_database.dart'; + +FirebaseDatabase database = FirebaseDatabase.instance; diff --git a/lib/features/realtime_database/domain/entities/database_manager.dart b/lib/features/realtime_database/domain/entities/database_manager.dart new file mode 100644 index 0000000..ca0d74d --- /dev/null +++ b/lib/features/realtime_database/domain/entities/database_manager.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/features/realtime_database/data/data_sources/database.dart'; +import 'package:poetlum/features/realtime_database/domain/repository/database_manager_interface.dart'; + +class DatabaseManager implements IDatabaseManager{ + @override + Future read(String path) async { + final databaseReference = database.ref(path); + + final snapshot = await databaseReference.get(); + + return snapshot.exists + ? snapshot.value as Map + : null; + } + + @override + void write(Map data, String path) { + database.ref(path).set(data); + } +} diff --git a/lib/features/realtime_database/domain/repository/database_manager_interface.dart b/lib/features/realtime_database/domain/repository/database_manager_interface.dart new file mode 100644 index 0000000..e84443e --- /dev/null +++ b/lib/features/realtime_database/domain/repository/database_manager_interface.dart @@ -0,0 +1,4 @@ +abstract class IDatabaseManager{ + void write(Map data, String path); + Future read(String path); +} From a25c1c26697f2a915c778568925027b410090bd1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:31:04 +0200 Subject: [PATCH 034/475] Add manager to the DI --- lib/core/dependency_injection.dart | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 lib/core/dependency_injection.dart diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart new file mode 100644 index 0000000..fddda57 --- /dev/null +++ b/lib/core/dependency_injection.dart @@ -0,0 +1,9 @@ +import 'package:get_it/get_it.dart'; +import 'package:poetlum/features/realtime_database/domain/entities/database_manager.dart'; + +GetIt getIt = GetIt.instance; + +Future initializeDependencies() async { + // Database + getIt.registerSingleton(DatabaseManager()); +} From a1315e8368003d8dc8150679168eddda05e51b18 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:31:14 +0200 Subject: [PATCH 035/475] Create init DI widget --- .../widgets/init_dependencies.dart | 23 +++++++++++++++++++ lib/main.dart | 13 +++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 lib/features/dependency_injection/presentation/widgets/init_dependencies.dart diff --git a/lib/features/dependency_injection/presentation/widgets/init_dependencies.dart b/lib/features/dependency_injection/presentation/widgets/init_dependencies.dart new file mode 100644 index 0000000..4e9e8f7 --- /dev/null +++ b/lib/features/dependency_injection/presentation/widgets/init_dependencies.dart @@ -0,0 +1,23 @@ +import 'package:flutter/widgets.dart'; +import 'package:poetlum/core/dependency_injection.dart'; + +class InitDependencies extends StatefulWidget { + const InitDependencies({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _InitDependenciesState(); +} + +class _InitDependenciesState extends State { + @override + void initState() { + initializeDependencies(); + + super.initState(); + } + + @override + Widget build(BuildContext context) => widget.child; +} diff --git a/lib/main.dart b/lib/main.dart index f266b0c..94f2d64 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,17 +1,20 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/poetlum_app.dart'; +import 'package:poetlum/features/dependency_injection/presentation/widgets/init_dependencies.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; import 'package:poetlum/firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - + runApp( - InitFirebaseWidget( - options: DefaultFirebaseOptions.currentPlatform, - child: const InitCrashlyticsWidget( - child: PoetlumApp(), + InitDependencies( + child: InitFirebaseWidget( + options: DefaultFirebaseOptions.currentPlatform, + child: const InitCrashlyticsWidget( + child: PoetlumApp(), + ), ), ), ); From 0c20cd7dd8e81166dd58cbf7c2c75e96d8a000b3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 7 Nov 2023 21:44:51 +0200 Subject: [PATCH 036/475] Add equatable package --- pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index 2b580cf..da57a52 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -105,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 302675c..ead7282 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: firebase_auth: ^4.11.1 firebase_database: ^10.3.1 get_it: ^7.6.4 + equatable: ^2.0.5 dev_dependencies: flutter_test: From 89f90d87ff2b713871e6d366abfdfb9fe5d87a13 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:13:24 +0200 Subject: [PATCH 037/475] Install packages --- pubspec.lock | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- pubspec.yaml | 2 ++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index da57a52..462a275 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.2" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f + url: "https://pub.dev" + source: hosted + version: "5.0.1" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" convert: dependency: transitive description: @@ -105,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" equatable: dependency: "direct main" description: @@ -121,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" + source: hosted + version: "2.1.0" firebase_analytics: dependency: "direct main" description: @@ -264,6 +296,14 @@ packages: description: flutter source: sdk version: "0.0.0" + get: + dependency: "direct main" + description: + name: get + sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e + url: "https://pub.dev" + source: hosted + version: "4.6.6" get_it: dependency: "direct main" description: @@ -336,6 +376,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" path: dependency: transitive description: @@ -463,4 +511,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.1.2 <4.0.0" - flutter: ">=3.3.0" + flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index ead7282..57d95f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: firebase_database: ^10.3.1 get_it: ^7.6.4 equatable: ^2.0.5 + connectivity_plus: ^5.0.1 + get: ^4.6.6 dev_dependencies: flutter_test: From d9056b7177cb36f4b191d8d8251a5e5667f53358 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:40:01 +0200 Subject: [PATCH 038/475] Update gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5e3ba35..e92cdff 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,7 @@ app.*.map.json # Firebase-related android/app/google-services.json -lib/firebase_options.dart \ No newline at end of file +lib/firebase_options.dart + +# Generated files +*.g.dart \ No newline at end of file From 79028ad102d0baefe4042794ace192fe18a283ea Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:40:22 +0200 Subject: [PATCH 039/475] Install packages --- pubspec.lock | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++- pubspec.yaml | 7 ++ 2 files changed, 344 insertions(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index 462a275..ff49048 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + url: "https://pub.dev" + source: hosted + version: "64.0.0" _flutterfire_internals: dependency: transitive description: @@ -9,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.9" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + url: "https://pub.dev" + source: hosted + version: "6.2.0" archive: dependency: transitive description: @@ -33,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + url: "https://pub.dev" + source: hosted + version: "8.1.2" boolean_selector: dependency: transitive description: @@ -41,6 +65,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + url: "https://pub.dev" + source: hosted + version: "2.4.6" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 + url: "https://pub.dev" + source: hosted + version: "7.2.11" + 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: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e" + url: "https://pub.dev" + source: hosted + version: "8.7.0" characters: dependency: transitive description: @@ -73,6 +161,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + url: "https://pub.dev" + source: hosted + version: "4.7.0" collection: dependency: transitive description: @@ -121,6 +217,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + url: "https://pub.dev" + source: hosted + version: "2.3.3" dbus: dependency: transitive description: @@ -129,6 +233,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" + dio: + dependency: "direct main" + description: + name: dio + sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7" + url: "https://pub.dev" + source: hosted + version: "5.3.3" equatable: dependency: "direct main" description: @@ -153,6 +265,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" firebase_analytics: dependency: "direct main" description: @@ -265,11 +385,27 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.3+9" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae + url: "https://pub.dev" + source: hosted + version: "8.1.3" flutter_launcher_icons: dependency: "direct dev" description: @@ -296,6 +432,14 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" get: dependency: "direct main" description: @@ -312,6 +456,30 @@ packages: url: "https://pub.dev" source: hosted version: "7.6.4" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + 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: @@ -328,6 +496,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.3" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -337,13 +513,21 @@ packages: source: hosted version: "0.6.7" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted version: "4.8.1" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 + url: "https://pub.dev" + source: hosted + version: "6.7.1" lints: dependency: transitive description: @@ -352,6 +536,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -376,6 +568,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" nm: dependency: transitive description: @@ -384,6 +592,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -416,11 +632,91 @@ packages: url: "https://pub.dev" source: hosted version: "3.7.3" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + retrofit: + dependency: "direct main" + description: + name: retrofit + sha256: "04ed77c82cadb655bb9357e8d0cb9da72ff704749a2d0cfe6540dd1f1f7ca4b9" + url: "https://pub.dev" + source: hosted + version: "4.0.3" + retrofit_generator: + dependency: "direct dev" + description: + name: retrofit_generator + sha256: "9499eb46b3657a62192ddbc208ff7e6c6b768b19e83c1ee6f6b119c864b99690" + url: "https://pub.dev" + source: hosted + version: "7.0.8" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + url: "https://pub.dev" + source: hosted + version: "1.3.4" source_span: dependency: transitive description: @@ -445,6 +741,14 @@ packages: 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: @@ -469,6 +773,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: @@ -485,6 +805,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web: dependency: transitive description: @@ -493,6 +821,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.4-beta" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 57d95f0..2c92612 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,11 +44,18 @@ dependencies: equatable: ^2.0.5 connectivity_plus: ^5.0.1 get: ^4.6.6 + dio: ^5.3.3 + retrofit: ^4.0.3 + json_annotation: ^4.8.1 + flutter_bloc: ^8.1.3 dev_dependencies: flutter_test: sdk: flutter flutter_launcher_icons: ^0.13.1 + retrofit_generator: '>=7.0.0 <8.0.0' + build_runner: '>=2.3.0 <4.0.0' + json_serializable: ^6.6.2 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is From 70666e5d93377bc5d932cedf7befcf954c9ef0c3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:40:46 +0200 Subject: [PATCH 040/475] Create poem model --- lib/features/poems_feed/data/models/poem.dart | 17 +++++++++++++++++ .../poems_feed/domain/entities/poem.dart | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 lib/features/poems_feed/data/models/poem.dart create mode 100644 lib/features/poems_feed/domain/entities/poem.dart diff --git a/lib/features/poems_feed/data/models/poem.dart b/lib/features/poems_feed/data/models/poem.dart new file mode 100644 index 0000000..6e57377 --- /dev/null +++ b/lib/features/poems_feed/data/models/poem.dart @@ -0,0 +1,17 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class PoemModel extends PoemEntity{ + const PoemModel({ + String? title, + String? author, + List? lines, + int? linecount, + }); + + factory PoemModel.fromJson(Map json) => PoemModel( + title: json['title'] ?? '', + author: json['author'] ?? '', + lines: json['lines'] ?? [], + linecount: json['linecount'] != null ? int.parse(json['linecount']) : 0, + ); +} diff --git a/lib/features/poems_feed/domain/entities/poem.dart b/lib/features/poems_feed/domain/entities/poem.dart new file mode 100644 index 0000000..aae5aa3 --- /dev/null +++ b/lib/features/poems_feed/domain/entities/poem.dart @@ -0,0 +1,18 @@ +import 'package:equatable/equatable.dart'; + +class PoemEntity extends Equatable{ + const PoemEntity({this.author, this.title, this.lines, this.linecount}); + + final String? author; + final String? title; + final List? lines; + final int? linecount; + + @override + List get props => [ + author, + title, + lines, + linecount, + ]; +} From 7195753c4158d30aacbb5e86a4b3883b73702d11 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:40:58 +0200 Subject: [PATCH 041/475] Create constants file --- lib/core/constants/constants.dart | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 lib/core/constants/constants.dart diff --git a/lib/core/constants/constants.dart b/lib/core/constants/constants.dart new file mode 100644 index 0000000..25ff2ae --- /dev/null +++ b/lib/core/constants/constants.dart @@ -0,0 +1,2 @@ +const String poemApiBaseUrl = 'https://poetrydb.org/'; +const int defaultPoemsCount = 30; From ed0366c6b027ecef361e4ff3c35cfa1e64ce7eec Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:41:10 +0200 Subject: [PATCH 042/475] Create data state handling --- lib/core/resources/data_state.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 lib/core/resources/data_state.dart diff --git a/lib/core/resources/data_state.dart b/lib/core/resources/data_state.dart new file mode 100644 index 0000000..e84b45f --- /dev/null +++ b/lib/core/resources/data_state.dart @@ -0,0 +1,16 @@ +import 'package:dio/dio.dart'; + +abstract class DataState{ + const DataState({this.data, this.error}); + + final T? data; + final DioException? error; +} + +class DataSuccess extends DataState{ + const DataSuccess(T data) : super(data: data); +} + +class DataFailed extends DataState{ + const DataFailed(DioException error) : super(error: error); +} From 1b1d0f254e2200571ea4021d0687675ae69cb5a0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:41:20 +0200 Subject: [PATCH 043/475] Create repository --- .../data/repository/poem_repository_impl.dart | 35 +++++++++++++++++++ .../domain/repository/poem_repository.dart | 6 ++++ 2 files changed, 41 insertions(+) create mode 100644 lib/features/poems_feed/data/repository/poem_repository_impl.dart create mode 100644 lib/features/poems_feed/domain/repository/poem_repository.dart diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart new file mode 100644 index 0000000..69ef792 --- /dev/null +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -0,0 +1,35 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:poetlum/core/resources/data_state.dart'; +import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; +import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; + +class PoemRepositoryImpl implements PoemRepository{ + PoemRepositoryImpl(this._poemApiService); + + final PoemApiService _poemApiService; + + @override + Future>> getPoems() async { + try{ + final httpResponse = await _poemApiService.getPoems(); + + if(httpResponse.response.statusCode == HttpStatus.ok){ + return DataSuccess(httpResponse.data); + } else{ + return DataFailed( + DioException( + requestOptions: httpResponse.response.requestOptions, + response: httpResponse.response, + type: DioExceptionType.badResponse, + error: httpResponse.response.statusMessage, + ), + ); + } + } on DioException catch(error){ + return DataFailed(error); + } + } +} diff --git a/lib/features/poems_feed/domain/repository/poem_repository.dart b/lib/features/poems_feed/domain/repository/poem_repository.dart new file mode 100644 index 0000000..c47482f --- /dev/null +++ b/lib/features/poems_feed/domain/repository/poem_repository.dart @@ -0,0 +1,6 @@ +import 'package:poetlum/core/resources/data_state.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +abstract class PoemRepository{ + Future>> getPoems(); +} From cdb6d3274aca3d9078bf2da458a9d9ce77c3e0f9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:41:30 +0200 Subject: [PATCH 044/475] Create use case --- lib/core/usecases/usecase.dart | 3 +++ .../domain/usecases/get_poems_usecase.dart | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 lib/core/usecases/usecase.dart create mode 100644 lib/features/poems_feed/domain/usecases/get_poems_usecase.dart diff --git a/lib/core/usecases/usecase.dart b/lib/core/usecases/usecase.dart new file mode 100644 index 0000000..531d58c --- /dev/null +++ b/lib/core/usecases/usecase.dart @@ -0,0 +1,3 @@ +abstract class UseCase{ + Future call({Params params}); +} diff --git a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart new file mode 100644 index 0000000..c684e40 --- /dev/null +++ b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart @@ -0,0 +1,13 @@ +import 'package:poetlum/core/resources/data_state.dart'; +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; + +class GetPoemsUseCase implements UseCase>, void>{ + GetPoemsUseCase(this._poemRepository); + + final PoemRepository _poemRepository; + + @override + Future>> call({void params}) => _poemRepository.getPoems(); +} From 5e13890cc70c8e3e7b81573223d87a5e6db581bc Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:41:41 +0200 Subject: [PATCH 045/475] Create bloc --- .../bloc/poem/remote/remote_poem_bloc.dart | 31 +++++++++++++++++++ .../bloc/poem/remote/remote_poem_event.dart | 7 +++++ .../bloc/poem/remote/remote_poem_state.dart | 28 +++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart create mode 100644 lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart create mode 100644 lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart new file mode 100644 index 0000000..c79b121 --- /dev/null +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -0,0 +1,31 @@ +// ignore_for_file: invalid_use_of_visible_for_testing_member + +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/resources/data_state.dart'; +import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; + +class RemotePoemBloc extends Bloc{ + RemotePoemBloc(this._getPoemsUseCase): super(const RemotePoemLoading()){ + on(onGetPoems); + } + + final GetPoemsUseCase _getPoemsUseCase; + + void onGetPoems(GetPoemsEvent event, Emitter emitter) async{ + final dataState = await _getPoemsUseCase(); + + if(dataState is DataSuccess && dataState.data!.isNotEmpty){ + emit( + RemotePoemDone(dataState.data!), + ); + } + + if(dataState is DataFailed){ + emit( + RemotePoemError(dataState.error!), + ); + } + } +} diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart new file mode 100644 index 0000000..2ba0efa --- /dev/null +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart @@ -0,0 +1,7 @@ +abstract class RemotePoemEvent{ + const RemotePoemEvent(); +} + +class GetPoemsEvent extends RemotePoemEvent{ + const GetPoemsEvent(); +} diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart new file mode 100644 index 0000000..40bd944 --- /dev/null +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart @@ -0,0 +1,28 @@ +import 'package:dio/dio.dart'; +import 'package:equatable/equatable.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +abstract class RemotePoemState extends Equatable{ + const RemotePoemState({this.poems, this.error}); + + final List? poems; + final DioException? error; + + @override + List get props => [ + poems!, + error!, + ]; +} + +class RemotePoemLoading extends RemotePoemState{ + const RemotePoemLoading(); +} + +class RemotePoemDone extends RemotePoemState{ + const RemotePoemDone(List poems) : super(poems: poems); +} + +class RemotePoemError extends RemotePoemState{ + const RemotePoemError(DioException error) : super(error: error); +} From e9fa64eab31891cab49c64d59eefaa91e1353cf1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:41:49 +0200 Subject: [PATCH 046/475] Create api service --- .../data/data_sources/remote/poem_api_service.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart diff --git a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart new file mode 100644 index 0000000..bd5a3ca --- /dev/null +++ b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart @@ -0,0 +1,14 @@ +import 'package:dio/dio.dart'; +import 'package:poetlum/core/constants/constants.dart'; +import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:retrofit/retrofit.dart'; + +part 'poem_api_service.g.dart'; + +@RestApi(baseUrl: poemApiBaseUrl) +abstract class PoemApiService{ + factory PoemApiService(Dio dio) = _PoemApiService; + + @GET('random/$defaultPoemsCount') + Future>> getPoems(); +} From b25b5362616484661d1198e7985ada45ba26adeb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 00:42:00 +0200 Subject: [PATCH 047/475] Register all the stuff in the DI --- lib/core/dependency_injection.dart | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index fddda57..c733281 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -1,9 +1,31 @@ +import 'package:dio/dio.dart'; import 'package:get_it/get_it.dart'; +import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; +import 'package:poetlum/features/poems_feed/data/repository/poem_repository_impl.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; +import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/realtime_database/domain/entities/database_manager.dart'; GetIt getIt = GetIt.instance; -Future initializeDependencies() async { - // Database - getIt.registerSingleton(DatabaseManager()); +void initializeDependencies() { + getIt + // Database + ..registerSingleton(DatabaseManager()) + + // Dio + ..registerSingleton(Dio()) + + // API Service + ..registerSingleton(PoemApiService(getIt())) + + // Repository + ..registerSingleton(PoemRepositoryImpl(getIt())) + + // Usecase + ..registerSingleton(GetPoemsUseCase(getIt())) + + // Bloc + ..registerFactory(() => RemotePoemBloc(getIt())); } From 5d14ab5fa25b0231e2b56a9e73e2e15f0aa3686a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:36:25 +0200 Subject: [PATCH 048/475] Create theme for app --- lib/config/theme/app_theme.dart | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lib/config/theme/app_theme.dart diff --git a/lib/config/theme/app_theme.dart b/lib/config/theme/app_theme.dart new file mode 100644 index 0000000..b055c1d --- /dev/null +++ b/lib/config/theme/app_theme.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +ThemeData theme()=> ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + scaffoldBackgroundColor: Colors.white, +); From 2fd980ebbd1d11239a2eb6d8a2e9e7354f7e873a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:37:11 +0200 Subject: [PATCH 049/475] Add bloc providers --- lib/features/application/poetlum_app.dart | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 0429638..d3aa22e 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,19 +1,27 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; +import 'package:poetlum/config/theme/app_theme.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @override - Widget build(BuildContext context) => GetMaterialApp( - title: 'Poetlum', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, + Widget build(BuildContext context) => MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), + ], + child: GetMaterialApp( + title: 'Poetlum', + theme: theme(), + home: const PoemsFeed(), ), - home: const PoetlumHomePage(title: 'Poetlum'), - ); + ); } class PoetlumHomePage extends StatefulWidget { From 5893026c6d06368ad5a94586d9ca8e0cd7f25cb1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:37:24 +0200 Subject: [PATCH 050/475] Change poem model --- lib/features/poems_feed/data/models/poem.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/features/poems_feed/data/models/poem.dart b/lib/features/poems_feed/data/models/poem.dart index 6e57377..cfc41b6 100644 --- a/lib/features/poems_feed/data/models/poem.dart +++ b/lib/features/poems_feed/data/models/poem.dart @@ -1,17 +1,17 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -class PoemModel extends PoemEntity{ +class PoemModel extends PoemEntity { const PoemModel({ - String? title, - String? author, - List? lines, - int? linecount, + super.title = '', + super.author = '', + super.lines = const [], + super.linecount = 0, }); factory PoemModel.fromJson(Map json) => PoemModel( title: json['title'] ?? '', author: json['author'] ?? '', - lines: json['lines'] ?? [], - linecount: json['linecount'] != null ? int.parse(json['linecount']) : 0, + lines: List.from(json['lines'] ?? []), + linecount: json['linecount'] != null ? int.parse(json['linecount'].toString()) : 0, ); } From fd36213f9c5bd1bbb68f40d75af50f84f3a1edc1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:37:50 +0200 Subject: [PATCH 051/475] Create poems feed page --- .../presentation/pages/home/poems_feed.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/features/poems_feed/presentation/pages/home/poems_feed.dart diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart new file mode 100644 index 0000000..8b5a14b --- /dev/null +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; + +class PoemsFeed extends StatelessWidget { + const PoemsFeed({super.key}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: CustomAppBar( + title: 'Poetlum', + backgroundColor: Colors.grey[350]!, + ), + body: _buildBody(), + ); + + BlocBuilder _buildBody() => BlocBuilder( + builder: (_, state){ + if(state is RemotePoemLoading){ + return const Center(child: CircularProgressIndicator(),); + } + + if(state is RemotePoemError){ + return const Center(child: Icon(Icons.refresh)); + } + + if(state is RemotePoemDone){ + return ListView.builder( + itemCount: state.poems!.length, + itemBuilder: (__, index) { + return ListTile( + title: Text("${state.poems![index].author}"), + ); + }, + ); + } + + return const SizedBox(); + }, + ); +} From 8343954554eeaf31f6565c1d4c7346ca02b7e67c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:53:46 +0200 Subject: [PATCH 052/475] Change return type --- .../presentation/bloc/poem/remote/remote_poem_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart index c79b121..8b8e13f 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -13,7 +13,7 @@ class RemotePoemBloc extends Bloc{ final GetPoemsUseCase _getPoemsUseCase; - void onGetPoems(GetPoemsEvent event, Emitter emitter) async{ + Future onGetPoems(GetPoemsEvent event, Emitter emitter) async{ final dataState = await _getPoemsUseCase(); if(dataState is DataSuccess && dataState.data!.isNotEmpty){ From 26d0609bfdc52c18c20172976d5051a5a050d37c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:54:19 +0200 Subject: [PATCH 053/475] Rewrite model and entity to contain poem text --- lib/features/poems_feed/data/models/poem.dart | 4 ++-- lib/features/poems_feed/domain/entities/poem.dart | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/features/poems_feed/data/models/poem.dart b/lib/features/poems_feed/data/models/poem.dart index cfc41b6..6e275b7 100644 --- a/lib/features/poems_feed/data/models/poem.dart +++ b/lib/features/poems_feed/data/models/poem.dart @@ -4,14 +4,14 @@ class PoemModel extends PoemEntity { const PoemModel({ super.title = '', super.author = '', - super.lines = const [], + super.text = '', super.linecount = 0, }); factory PoemModel.fromJson(Map json) => PoemModel( title: json['title'] ?? '', author: json['author'] ?? '', - lines: List.from(json['lines'] ?? []), + text: List.from(json['lines'] ?? []).join('\n'), linecount: json['linecount'] != null ? int.parse(json['linecount'].toString()) : 0, ); } diff --git a/lib/features/poems_feed/domain/entities/poem.dart b/lib/features/poems_feed/domain/entities/poem.dart index aae5aa3..c0643c0 100644 --- a/lib/features/poems_feed/domain/entities/poem.dart +++ b/lib/features/poems_feed/domain/entities/poem.dart @@ -1,18 +1,18 @@ import 'package:equatable/equatable.dart'; class PoemEntity extends Equatable{ - const PoemEntity({this.author, this.title, this.lines, this.linecount}); + const PoemEntity({this.author, this.title, this.text, this.linecount}); final String? author; final String? title; - final List? lines; + final String? text; final int? linecount; @override List get props => [ author, title, - lines, + text, linecount, ]; } From e493e26d2fd6ff6e25e936685140ed38c5fcfa53 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 8 Nov 2023 18:03:08 +0200 Subject: [PATCH 054/475] Create PoemCard widget --- lib/features/application/poetlum_app.dart | 8 +- .../presentation/pages/home/poems_feed.dart | 9 +-- .../presentation/widgets/poem_card.dart | 74 +++++++++++++++++++ 3 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 lib/features/poems_feed/presentation/widgets/poem_card.dart diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index d3aa22e..d2d2c4b 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -17,10 +17,10 @@ class PoetlumApp extends StatelessWidget { BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), ], child: GetMaterialApp( - title: 'Poetlum', - theme: theme(), - home: const PoemsFeed(), - ), + title: 'Poetlum', + theme: theme(), + home: const PoemsFeed(), + ), ); } diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 8b5a14b..6a68692 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_card.dart'; class PoemsFeed extends StatelessWidget { const PoemsFeed({super.key}); @@ -29,11 +30,9 @@ class PoemsFeed extends StatelessWidget { if(state is RemotePoemDone){ return ListView.builder( itemCount: state.poems!.length, - itemBuilder: (__, index) { - return ListTile( - title: Text("${state.poems![index].author}"), - ); - }, + itemBuilder: (__, index) => PoemCard( + poemEntity: state.poems![index], + ), ); } diff --git a/lib/features/poems_feed/presentation/widgets/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poem_card.dart new file mode 100644 index 0000000..9096913 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/poem_card.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class PoemCard extends StatelessWidget { + const PoemCard({super.key, required this.poemEntity}); + + final PoemEntity poemEntity; + + @override + Widget build(BuildContext context) => Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TitleText(title: poemEntity.title), + const SizedBox(height: 8), + _AuthorText(author: poemEntity.author), + const SizedBox(height: 16), + _PoemText(text: poemEntity.text, maxLength: 250), + ], + ), + ), + ); +} + +class _TitleText extends StatelessWidget { + const _TitleText({required this.title}); + + final String? title; + + @override + Widget build(BuildContext context) => Text( + title ?? 'Untitled', + style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + ); +} + +class _AuthorText extends StatelessWidget { + const _AuthorText({required this.author}); + + final String? author; + + @override + Widget build(BuildContext context) => Text( + author ?? 'Unknown Author', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ); +} + +class _PoemText extends StatelessWidget { + const _PoemText({required this.text, required this.maxLength}); + + final String? text; + final int maxLength; + + @override + Widget build(BuildContext context) { + if (text == null || text!.isEmpty) { + return const Text('No content available.', + style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic), + ); + } + return Text( + text!.length > maxLength ? '${text!.substring(0, maxLength)}...' : text!, + style: const TextStyle(fontSize: 14), + ); + } +} From f42e309c929c942864a221df4b102169585dc088 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 9 Nov 2023 19:12:15 +0200 Subject: [PATCH 055/475] Add card elevation --- lib/features/poems_feed/presentation/widgets/poem_card.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/features/poems_feed/presentation/widgets/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poem_card.dart index 9096913..7012654 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_card.dart @@ -11,6 +11,7 @@ class PoemCard extends StatelessWidget { shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), + elevation: 3, margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Padding( padding: const EdgeInsets.all(16), From 5777ede59650b820a4cee8715515a66c07ccec22 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 12:00:08 +0200 Subject: [PATCH 056/475] Remove app bar color field --- lib/features/application/poetlum_app.dart | 1 - .../application/presentation/widgets/AppBar/app_bar.dart | 4 +--- .../poems_feed/presentation/pages/home/poems_feed.dart | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index d2d2c4b..c4283bc 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -38,7 +38,6 @@ class _PoetlumHomePageState extends State { Widget build(BuildContext context) => Scaffold( appBar: CustomAppBar( title: 'Poetlum', - backgroundColor: Colors.grey[350]!, ), body: const Placeholder(), ); diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index 5f55d8e..1006db6 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -3,15 +3,13 @@ import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { - const CustomAppBar({super.key, required this.title, required this.backgroundColor}); + const CustomAppBar({super.key, required this.title}); final String title; - final Color backgroundColor; @override Widget build(BuildContext context) => AppBar( leading: const MenuButton(), - backgroundColor: backgroundColor, actions: const [ SettingsButton(), ], diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 6a68692..b276503 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -12,7 +12,6 @@ class PoemsFeed extends StatelessWidget { Widget build(BuildContext context) => Scaffold( appBar: CustomAppBar( title: 'Poetlum', - backgroundColor: Colors.grey[350]!, ), body: _buildBody(), ); From ef7e50429a48b05da0bab3397f7aa56469a7097d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:08:33 +0200 Subject: [PATCH 057/475] Install validator package --- pubspec.lock | 16 ++++++++++++---- pubspec.yaml | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index ff49048..e0f15a5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: @@ -241,6 +241,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.3.3" + 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: @@ -644,10 +652,10 @@ packages: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2c92612..afce049 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: retrofit: ^4.0.3 json_annotation: ^4.8.1 flutter_bloc: ^8.1.3 + email_validator: ^2.1.17 dev_dependencies: flutter_test: From fa5af931c7f7b2fc51b2b5def887f34322795f7e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:09:02 +0200 Subject: [PATCH 058/475] Code enhancement --- lib/features/application/poetlum_app.dart | 6 +++--- .../poems_feed/presentation/pages/home/poems_feed.dart | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index c4283bc..6bf762a 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -6,7 +6,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; -import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; +import 'package:poetlum/features/registration/presentation/pages/registration/registration_page.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @@ -19,7 +19,7 @@ class PoetlumApp extends StatelessWidget { child: GetMaterialApp( title: 'Poetlum', theme: theme(), - home: const PoemsFeed(), + home: RegistrationPage(), ), ); } @@ -36,7 +36,7 @@ class PoetlumHomePage extends StatefulWidget { class _PoetlumHomePageState extends State { @override Widget build(BuildContext context) => Scaffold( - appBar: CustomAppBar( + appBar: const CustomAppBar( title: 'Poetlum', ), body: const Placeholder(), diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index b276503..4199791 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -10,7 +10,7 @@ class PoemsFeed extends StatelessWidget { @override Widget build(BuildContext context) => Scaffold( - appBar: CustomAppBar( + appBar: const CustomAppBar( title: 'Poetlum', ), body: _buildBody(), From a841b94271f8b09312899c70597d04c8c82d5713 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:09:12 +0200 Subject: [PATCH 059/475] Create textfield --- .../presentation/widgets/text_field.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 lib/features/registration/presentation/widgets/text_field.dart diff --git a/lib/features/registration/presentation/widgets/text_field.dart b/lib/features/registration/presentation/widgets/text_field.dart new file mode 100644 index 0000000..d94ff9d --- /dev/null +++ b/lib/features/registration/presentation/widgets/text_field.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class RegistrationTextField extends StatelessWidget { + const RegistrationTextField({super.key, required this.controller, required this.hintText, required this.isPassword}); + + final TextEditingController controller; + final String hintText; + final bool isPassword; + + @override + Widget build(BuildContext context) => SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: TextField( + controller: controller, + obscureText: isPassword, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: hintText, + hintStyle: const TextStyle( + color: Colors.grey, + ), + ), + ), + ); +} From f4d5736b7554812f85e9836dba099e4d2200edc1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:09:21 +0200 Subject: [PATCH 060/475] Create registration page --- .../pages/registration/registration_page.dart | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 lib/features/registration/presentation/pages/registration/registration_page.dart diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart new file mode 100644 index 0000000..9e757cc --- /dev/null +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/registration/presentation/widgets/text_field.dart'; + +class RegistrationPage extends StatelessWidget { + RegistrationPage({super.key}); + + final TextEditingController _usernameControlled = TextEditingController(); + final TextEditingController _emailControlled = TextEditingController(); + final TextEditingController _passwordControlled = TextEditingController(); + + @override + Widget build(BuildContext context) => Scaffold( + body: SafeArea( + child: Row( + children: [ + Expanded( + child: Column( + children: [ + const Spacer(), + const Text( + 'Registration', + style: TextStyle( + fontSize: 22, + ), + ), + const Spacer(flex: 2,), + + RegistrationTextField(controller: _usernameControlled, hintText: 'Username', isPassword: false,), + const Spacer(), + + RegistrationTextField(controller: _emailControlled, hintText: 'Email', isPassword: false,), + const Spacer(), + + RegistrationTextField(controller: _passwordControlled, hintText: 'Password', isPassword: true,), + const Spacer(flex: 2,), + + FilledButton.tonal( + onPressed: (){}, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text( + 'Register', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + const Spacer(), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Already have an account?'), + TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), + ], + ), + const Spacer(flex: 12,), + ], + ), + ), + ], + ), + ) + ); +} From e5a54a0c3ddf20c49f5f618038fb6eb3fb05ec5a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:20:40 +0200 Subject: [PATCH 061/475] Create email text field --- .../presentation/widgets/email_field.dart | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 lib/features/registration/presentation/widgets/email_field.dart diff --git a/lib/features/registration/presentation/widgets/email_field.dart b/lib/features/registration/presentation/widgets/email_field.dart new file mode 100644 index 0000000..19feffe --- /dev/null +++ b/lib/features/registration/presentation/widgets/email_field.dart @@ -0,0 +1,27 @@ +import 'package:email_validator/email_validator.dart'; +import 'package:flutter/material.dart'; + +class EmailField extends StatelessWidget { + const EmailField({super.key, required this.controller}); + + final TextEditingController controller; + + @override + Widget build(BuildContext context) => SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: Form( + autovalidateMode: AutovalidateMode.onUserInteraction, + child: TextFormField( + controller: controller, + validator: (value) => EmailValidator.validate(value!) ? null : 'Please enter a valid email', + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: 'Email', + hintStyle: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + ); +} From c3af9241ba8067b2fa5819c9c2eab52271f92952 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:20:50 +0200 Subject: [PATCH 062/475] Enhance text field --- lib/features/registration/presentation/widgets/text_field.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/registration/presentation/widgets/text_field.dart b/lib/features/registration/presentation/widgets/text_field.dart index d94ff9d..26d2446 100644 --- a/lib/features/registration/presentation/widgets/text_field.dart +++ b/lib/features/registration/presentation/widgets/text_field.dart @@ -13,6 +13,8 @@ class RegistrationTextField extends StatelessWidget { child: TextField( controller: controller, obscureText: isPassword, + enableSuggestions: false, + autocorrect: false, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: hintText, From 25c07b61aacb9cf1b3a0a99f54a682119069fa56 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:20:56 +0200 Subject: [PATCH 063/475] Add email textfield --- .../presentation/pages/registration/registration_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index 9e757cc..020c2b8 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/text_field.dart'; class RegistrationPage extends StatelessWidget { @@ -28,7 +29,7 @@ class RegistrationPage extends StatelessWidget { RegistrationTextField(controller: _usernameControlled, hintText: 'Username', isPassword: false,), const Spacer(), - RegistrationTextField(controller: _emailControlled, hintText: 'Email', isPassword: false,), + EmailField(controller: _emailControlled), const Spacer(), RegistrationTextField(controller: _passwordControlled, hintText: 'Password', isPassword: true,), From 88633c6e928e46a9b643556661ae349b77b51082 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:47:34 +0200 Subject: [PATCH 064/475] Rename widget --- .../registration/presentation/widgets/email_field.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/registration/presentation/widgets/email_field.dart b/lib/features/registration/presentation/widgets/email_field.dart index 19feffe..b6b6a86 100644 --- a/lib/features/registration/presentation/widgets/email_field.dart +++ b/lib/features/registration/presentation/widgets/email_field.dart @@ -1,8 +1,8 @@ import 'package:email_validator/email_validator.dart'; import 'package:flutter/material.dart'; -class EmailField extends StatelessWidget { - const EmailField({super.key, required this.controller}); +class EmailTextField extends StatelessWidget { + const EmailTextField({super.key, required this.controller}); final TextEditingController controller; From 2f092c34475cdb6cf34d7b28b6784665dc084aab Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:47:46 +0200 Subject: [PATCH 065/475] Create password textfield --- .../presentation/widgets/password_field.dart | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/features/registration/presentation/widgets/password_field.dart diff --git a/lib/features/registration/presentation/widgets/password_field.dart b/lib/features/registration/presentation/widgets/password_field.dart new file mode 100644 index 0000000..8e3981b --- /dev/null +++ b/lib/features/registration/presentation/widgets/password_field.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +class PasswordTextField extends StatefulWidget { + const PasswordTextField({super.key, required this.controller}); + + final TextEditingController controller; + + @override + State createState() => _PasswordTextFieldState(); +} + +class _PasswordTextFieldState extends State { + bool _isPasswordVisible = false; + + @override + Widget build(BuildContext context) => SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: TextField( + controller: widget.controller, + obscureText: !_isPasswordVisible, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: 'Password', + hintStyle: const TextStyle( + color: Colors.grey, + ), + suffixIcon: IconButton( + icon: Icon( + _isPasswordVisible + ? Icons.visibility + : Icons.visibility_off, + ), + onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), + ), + ), + ), + ); +} From 5267a3e07f723c530d839006a7499dd5bd13ce98 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:48:25 +0200 Subject: [PATCH 066/475] Create username field --- .../presentation/widgets/text_field.dart | 27 ------------------- .../presentation/widgets/username_field.dart | 24 +++++++++++++++++ 2 files changed, 24 insertions(+), 27 deletions(-) delete mode 100644 lib/features/registration/presentation/widgets/text_field.dart create mode 100644 lib/features/registration/presentation/widgets/username_field.dart diff --git a/lib/features/registration/presentation/widgets/text_field.dart b/lib/features/registration/presentation/widgets/text_field.dart deleted file mode 100644 index 26d2446..0000000 --- a/lib/features/registration/presentation/widgets/text_field.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -class RegistrationTextField extends StatelessWidget { - const RegistrationTextField({super.key, required this.controller, required this.hintText, required this.isPassword}); - - final TextEditingController controller; - final String hintText; - final bool isPassword; - - @override - Widget build(BuildContext context) => SizedBox( - width: MediaQuery.of(context).size.width/1.5, - child: TextField( - controller: controller, - obscureText: isPassword, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: hintText, - hintStyle: const TextStyle( - color: Colors.grey, - ), - ), - ), - ); -} diff --git a/lib/features/registration/presentation/widgets/username_field.dart b/lib/features/registration/presentation/widgets/username_field.dart new file mode 100644 index 0000000..1eb2f28 --- /dev/null +++ b/lib/features/registration/presentation/widgets/username_field.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class UsernameTextField extends StatelessWidget { + const UsernameTextField({super.key, required this.controller}); + + final TextEditingController controller; + + @override + Widget build(BuildContext context) => SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: TextField( + controller: controller, + enableSuggestions: false, + autocorrect: false, + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: 'Username', + hintStyle: TextStyle( + color: Colors.grey, + ), + ), + ), + ); +} From ca8569df79d56b4b58782296c29adc4c17a7805d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:48:30 +0200 Subject: [PATCH 067/475] Add widget --- .../pages/registration/registration_page.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index 020c2b8..f5efd8f 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; -import 'package:poetlum/features/registration/presentation/widgets/text_field.dart'; +import 'package:poetlum/features/registration/presentation/widgets/password_field.dart'; +import 'package:poetlum/features/registration/presentation/widgets/username_field.dart'; class RegistrationPage extends StatelessWidget { RegistrationPage({super.key}); @@ -26,13 +27,13 @@ class RegistrationPage extends StatelessWidget { ), const Spacer(flex: 2,), - RegistrationTextField(controller: _usernameControlled, hintText: 'Username', isPassword: false,), + UsernameTextField(controller: _usernameControlled), const Spacer(), - EmailField(controller: _emailControlled), + EmailTextField(controller: _emailControlled), const Spacer(), - RegistrationTextField(controller: _passwordControlled, hintText: 'Password', isPassword: true,), + PasswordTextField(controller: _passwordControlled), const Spacer(flex: 2,), FilledButton.tonal( @@ -62,6 +63,6 @@ class RegistrationPage extends StatelessWidget { ), ], ), - ) + ), ); } From d7eb585f0ecea8f1bb3073c921cd056031da1846 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:28:10 +0200 Subject: [PATCH 068/475] Add toast package --- pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index e0f15a5..12de637 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -440,6 +440,14 @@ packages: description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: "69a5c4fcfe9a163bc927ff386c0dedd62575913bba942d0ce80c1fd092885255" + url: "https://pub.dev" + source: hosted + version: "8.2.3" frontend_server_client: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index afce049..fcb27dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: json_annotation: ^4.8.1 flutter_bloc: ^8.1.3 email_validator: ^2.1.17 + fluttertoast: ^8.0.9 dev_dependencies: flutter_test: From a6b700634e888422067d801c40ba189ba09869b1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:28:35 +0200 Subject: [PATCH 069/475] Updade gradle build --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 528e571..3130fd9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -49,7 +49,7 @@ android { applicationId "com.example.poetlum" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 22 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName From ff18350a079765377a1d92f3a5dbdf3c89395d39 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:28:57 +0200 Subject: [PATCH 070/475] Create firebase service --- .../data_sources/remote/firebase_service.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 lib/features/registration/data/data_sources/remote/firebase_service.dart diff --git a/lib/features/registration/data/data_sources/remote/firebase_service.dart b/lib/features/registration/data/data_sources/remote/firebase_service.dart new file mode 100644 index 0000000..7040ad1 --- /dev/null +++ b/lib/features/registration/data/data_sources/remote/firebase_service.dart @@ -0,0 +1,16 @@ +import 'package:firebase_auth/firebase_auth.dart'; + +abstract class FirebaseService{ + Future registerUser({required String username, required String email, required String password}); +} + +class FirebaseServiceImpl implements FirebaseService{ + @override + Future registerUser({required String username, required String email, required String password}) async { + await FirebaseAuth.instance.createUserWithEmailAndPassword( + email: email, + password: password, + ); + await FirebaseAuth.instance.currentUser!.updateDisplayName(username); + } +} From ff2e4bd124df1fc474d2ed476175110791a695d2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:29:07 +0200 Subject: [PATCH 071/475] Create firebase repo --- .../data/repository/firebase_repository_impl.dart | 13 +++++++++++++ .../domain/repository/firebase_repository.dart | 3 +++ 2 files changed, 16 insertions(+) create mode 100644 lib/features/registration/data/repository/firebase_repository_impl.dart create mode 100644 lib/features/registration/domain/repository/firebase_repository.dart diff --git a/lib/features/registration/data/repository/firebase_repository_impl.dart b/lib/features/registration/data/repository/firebase_repository_impl.dart new file mode 100644 index 0000000..493fdcf --- /dev/null +++ b/lib/features/registration/data/repository/firebase_repository_impl.dart @@ -0,0 +1,13 @@ +import 'package:poetlum/features/registration/data/data_sources/remote/firebase_service.dart'; +import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; + +class FirebaseRepositoryImpl implements FirebaseRepository{ + FirebaseRepositoryImpl(this._firebaseService); + + final FirebaseService _firebaseService; + + @override + Future registerUser({required String username, required String email, required String password}) async{ + await _firebaseService.registerUser(username: username, email: email, password: password); + } +} diff --git a/lib/features/registration/domain/repository/firebase_repository.dart b/lib/features/registration/domain/repository/firebase_repository.dart new file mode 100644 index 0000000..302a960 --- /dev/null +++ b/lib/features/registration/domain/repository/firebase_repository.dart @@ -0,0 +1,3 @@ +abstract class FirebaseRepository{ + Future registerUser({required String username, required String email, required String password}); +} From de680500354fb40c4ddde4325f9aa6e84ee92bc9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:32:42 +0200 Subject: [PATCH 072/475] Create use case --- .../domain/usecases/register_user_params.dart | 11 +++++++++++ .../domain/usecases/register_user_usecase.dart | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 lib/features/registration/domain/usecases/register_user_params.dart create mode 100644 lib/features/registration/domain/usecases/register_user_usecase.dart diff --git a/lib/features/registration/domain/usecases/register_user_params.dart b/lib/features/registration/domain/usecases/register_user_params.dart new file mode 100644 index 0000000..c0292f9 --- /dev/null +++ b/lib/features/registration/domain/usecases/register_user_params.dart @@ -0,0 +1,11 @@ +class RegisterUserParams { + const RegisterUserParams({ + required this.username, + required this.email, + required this.password, + }); + + final String username; + final String email; + final String password; +} diff --git a/lib/features/registration/domain/usecases/register_user_usecase.dart b/lib/features/registration/domain/usecases/register_user_usecase.dart new file mode 100644 index 0000000..a25d3ca --- /dev/null +++ b/lib/features/registration/domain/usecases/register_user_usecase.dart @@ -0,0 +1,16 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; +import 'package:poetlum/features/registration/domain/usecases/register_user_params.dart'; + +class RegisterUserUseCase implements UseCase{ + RegisterUserUseCase(this._firebaseRepository); + + final FirebaseRepository _firebaseRepository; + + @override + Future call({RegisterUserParams? params}) async{ + if(params != null){ + await _firebaseRepository.registerUser(username: params.username, email: params.email, password: params.password); + } + } +} From fdc208f2936f7411061104faea4521d1002ab42c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:32:54 +0200 Subject: [PATCH 073/475] Create register cubit --- .../presentation/bloc/register_cubit.dart | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 lib/features/registration/presentation/bloc/register_cubit.dart diff --git a/lib/features/registration/presentation/bloc/register_cubit.dart b/lib/features/registration/presentation/bloc/register_cubit.dart new file mode 100644 index 0000000..5de9106 --- /dev/null +++ b/lib/features/registration/presentation/bloc/register_cubit.dart @@ -0,0 +1,35 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/registration/domain/usecases/register_user_params.dart'; +import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; + +class RegisterCubit extends Cubit{ + RegisterCubit(this._registerUserUseCase): super(null); + + final RegisterUserUseCase _registerUserUseCase; + + Future register( + String username, + String email, + String password, + void Function() showPositiveToast, + void Function(String error) showNegativeToast, + ) async{ + if(username.isEmpty || email.isEmpty || password.isEmpty){ + showNegativeToast('Please fill in all required fields'); + } else{ + try{ + await _registerUserUseCase( + params: RegisterUserParams( + username: username, + email: email, + password: password, + ), + ); + + showPositiveToast(); + } catch (e){ + showNegativeToast(e.toString()); + } + } + } +} From c66094d31cd2323b2ce60c3c55bba9f4a947b36a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:33:13 +0200 Subject: [PATCH 074/475] Init all the stuff in the DI --- lib/core/dependency_injection.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index c733281..224f895 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -6,6 +6,11 @@ import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.da import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/realtime_database/domain/entities/database_manager.dart'; +import 'package:poetlum/features/registration/data/data_sources/remote/firebase_service.dart'; +import 'package:poetlum/features/registration/data/repository/firebase_repository_impl.dart'; +import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; +import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; +import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; GetIt getIt = GetIt.instance; @@ -19,13 +24,17 @@ void initializeDependencies() { // API Service ..registerSingleton(PoemApiService(getIt())) + ..registerSingleton(FirebaseServiceImpl()) // Repository ..registerSingleton(PoemRepositoryImpl(getIt())) + ..registerSingleton(FirebaseRepositoryImpl(getIt())) // Usecase ..registerSingleton(GetPoemsUseCase(getIt())) + ..registerSingleton(RegisterUserUseCase(getIt())) // Bloc - ..registerFactory(() => RemotePoemBloc(getIt())); + ..registerFactory(() => RemotePoemBloc(getIt())) + ..registerFactory(() => RegisterCubit(getIt())); } From f9038effdd76ce052b1c3ad3d2335868b6ec9a21 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:33:19 +0200 Subject: [PATCH 075/475] Edit pages --- lib/features/application/poetlum_app.dart | 2 + .../pages/registration/registration_page.dart | 73 ++++++++++++++----- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 6bf762a..580c1da 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -6,6 +6,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; import 'package:poetlum/features/registration/presentation/pages/registration/registration_page.dart'; class PoetlumApp extends StatelessWidget { @@ -15,6 +16,7 @@ class PoetlumApp extends StatelessWidget { Widget build(BuildContext context) => MultiBlocProvider( providers: [ BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), + BlocProvider(create:(context) => getIt(),), ], child: GetMaterialApp( title: 'Poetlum', diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index f5efd8f..8307469 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/password_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/username_field.dart'; @@ -6,9 +9,9 @@ import 'package:poetlum/features/registration/presentation/widgets/username_fiel class RegistrationPage extends StatelessWidget { RegistrationPage({super.key}); - final TextEditingController _usernameControlled = TextEditingController(); - final TextEditingController _emailControlled = TextEditingController(); - final TextEditingController _passwordControlled = TextEditingController(); + final TextEditingController _usernameController = TextEditingController(); + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); @override Widget build(BuildContext context) => Scaffold( @@ -26,30 +29,40 @@ class RegistrationPage extends StatelessWidget { ), ), const Spacer(flex: 2,), - - UsernameTextField(controller: _usernameControlled), + + UsernameTextField(controller: _usernameController), const Spacer(), - - EmailTextField(controller: _emailControlled), + + EmailTextField(controller: _emailController), const Spacer(), - - PasswordTextField(controller: _passwordControlled), + + PasswordTextField(controller: _passwordController), const Spacer(flex: 2,), - - FilledButton.tonal( - onPressed: (){}, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Text( - 'Register', - style: TextStyle( - fontSize: 16, + + BlocBuilder( + builder:(context, state) => FilledButton.tonal( + onPressed: () async{ + await context.read().register( + _usernameController.text, + _emailController.text, + _passwordController.text, + _showPositiveToast, + _showNegativeToast, + ); + }, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text( + 'Register', + style: TextStyle( + fontSize: 16, + ), ), ), ), ), const Spacer(), - + Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -65,4 +78,26 @@ class RegistrationPage extends StatelessWidget { ), ), ); + + Future _showPositiveToast() async{ + await Fluttertoast.showToast( + msg: 'Your registration was successful', + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + ); + } + + Future _showNegativeToast(String error) async{ + await Fluttertoast.showToast( + msg: error, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); + } } From 48c9e4527fb0b9dd88a2bd6eaea153e37461667d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 12 Nov 2023 12:54:50 +0200 Subject: [PATCH 076/475] Rework submitting logic --- .../presentation/bloc/register_cubit.dart | 43 ++++++++---------- .../presentation/bloc/register_state.dart | 24 ++++++++++ .../pages/registration/registration_page.dart | 44 +++++++++++-------- 3 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 lib/features/registration/presentation/bloc/register_state.dart diff --git a/lib/features/registration/presentation/bloc/register_cubit.dart b/lib/features/registration/presentation/bloc/register_cubit.dart index 5de9106..7603a02 100644 --- a/lib/features/registration/presentation/bloc/register_cubit.dart +++ b/lib/features/registration/presentation/bloc/register_cubit.dart @@ -1,35 +1,28 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/registration/domain/usecases/register_user_params.dart'; import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; +import 'package:poetlum/features/registration/presentation/bloc/register_state.dart'; + +class RegisterCubit extends Cubit { + RegisterCubit(this._registerUserUseCase) : super(const RegisterState()); -class RegisterCubit extends Cubit{ - RegisterCubit(this._registerUserUseCase): super(null); - final RegisterUserUseCase _registerUserUseCase; - Future register( - String username, - String email, - String password, - void Function() showPositiveToast, - void Function(String error) showNegativeToast, - ) async{ - if(username.isEmpty || email.isEmpty || password.isEmpty){ - showNegativeToast('Please fill in all required fields'); - } else{ - try{ - await _registerUserUseCase( - params: RegisterUserParams( - username: username, - email: email, - password: password, - ), - ); + Future register(String username, String email, String password) async { + if (username.isEmpty || email.isEmpty || password.isEmpty) { + emit(state.copyWith(status: RegisterStatus.error, errorMessage: 'Please fill in all required fields')); + return; + } + + emit(state.copyWith(status: RegisterStatus.submitting)); - showPositiveToast(); - } catch (e){ - showNegativeToast(e.toString()); - } + try { + await _registerUserUseCase( + params: RegisterUserParams(username: username, email: email, password: password), + ); + emit(state.copyWith(status: RegisterStatus.success)); + } catch (e) { + emit(state.copyWith(status: RegisterStatus.error, errorMessage: e.toString())); } } } diff --git a/lib/features/registration/presentation/bloc/register_state.dart b/lib/features/registration/presentation/bloc/register_state.dart new file mode 100644 index 0000000..f6f6295 --- /dev/null +++ b/lib/features/registration/presentation/bloc/register_state.dart @@ -0,0 +1,24 @@ +import 'package:equatable/equatable.dart'; + +enum RegisterStatus { initial, submitting, success, error } + +class RegisterState extends Equatable { + const RegisterState({ + this.status = RegisterStatus.initial, + this.errorMessage, + }); + + final RegisterStatus status; + final String? errorMessage; + + RegisterState copyWith({ + RegisterStatus? status, + String? errorMessage, + }) => RegisterState( + status: status ?? this.status, + errorMessage: errorMessage, + ); + + @override + List get props => [status, errorMessage]; +} diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index 8307469..88c3a62 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/register_state.dart'; import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/password_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/username_field.dart'; @@ -39,27 +40,34 @@ class RegistrationPage extends StatelessWidget { PasswordTextField(controller: _passwordController), const Spacer(flex: 2,), - BlocBuilder( - builder:(context, state) => FilledButton.tonal( - onPressed: () async{ - await context.read().register( - _usernameController.text, - _emailController.text, - _passwordController.text, - _showPositiveToast, - _showNegativeToast, - ); - }, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Text( - 'Register', - style: TextStyle( - fontSize: 16, + BlocConsumer( + listener: (context, state) { + if (state.status == RegisterStatus.success) { + _showPositiveToast(); + } else if (state.status == RegisterStatus.error) { + _showNegativeToast(state.errorMessage ?? 'Unknown error'); + } + }, + builder: (context, state) => state.status == RegisterStatus.submitting + ? const CircularProgressIndicator() + : FilledButton.tonal( + onPressed: (){ + context.read().register( + _usernameController.text, + _emailController.text, + _passwordController.text, + ); + }, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text( + 'Register', + style: TextStyle( + fontSize: 16, + ), ), ), ), - ), ), const Spacer(), From ebd15c8c719be77133a4762676b35315f04d9f25 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:12:51 +0200 Subject: [PATCH 077/475] Create validation cubit --- .../presentation/bloc/validation_cubit.dart | 45 ++++++++++++++++ .../presentation/bloc/validation_state.dart | 51 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 lib/features/registration/presentation/bloc/validation_cubit.dart create mode 100644 lib/features/registration/presentation/bloc/validation_state.dart diff --git a/lib/features/registration/presentation/bloc/validation_cubit.dart b/lib/features/registration/presentation/bloc/validation_cubit.dart new file mode 100644 index 0000000..897236e --- /dev/null +++ b/lib/features/registration/presentation/bloc/validation_cubit.dart @@ -0,0 +1,45 @@ + + +import 'package:email_validator/email_validator.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation_state.dart'; + +class FormValidationCubit extends Cubit { + FormValidationCubit() : super(const FormValidationState()); + + void usernameChanged(String value) { + final usernameValid = _isUsernameValid(value); + + emit(state.copyWith( + username: value, + isUsernameValid: usernameValid, + isFormValid: usernameValid && state.isEmailValid && state.isPasswordValid, + ),); + } + + void emailChanged(String value) { + final emailValid = _isEmailValid(value); + + emit(state.copyWith( + email: value, + isEmailValid: emailValid, + isFormValid: state.isUsernameValid && emailValid && state.isPasswordValid, + ),); + } + + void passwordChanged(String value) { + final passwordValid = _isPasswordValid(value); + + emit(state.copyWith( + password: value, + isPasswordValid: passwordValid, + isFormValid: state.isUsernameValid && state.isEmailValid && passwordValid, + ),); + } + + bool _isUsernameValid(String username) => RegExp(r'^[a-zA-Z0-9]+$').hasMatch(username) && username.length <= 30; + + bool _isEmailValid(String email) => EmailValidator.validate(email) && email.length <= 40; + + bool _isPasswordValid(String password) => password.length >= 8 && password.length <= 20; +} diff --git a/lib/features/registration/presentation/bloc/validation_state.dart b/lib/features/registration/presentation/bloc/validation_state.dart new file mode 100644 index 0000000..3f9e1c0 --- /dev/null +++ b/lib/features/registration/presentation/bloc/validation_state.dart @@ -0,0 +1,51 @@ +import 'package:equatable/equatable.dart'; + + +class FormValidationState extends Equatable { + const FormValidationState({ + this.username = '', + this.email = '', + this.password = '', + this.isUsernameValid = false, + this.isEmailValid = false, + this.isPasswordValid = false, + this.isFormValid = false, + }); + + final String username; + final String email; + final String password; + final bool isUsernameValid; + final bool isEmailValid; + final bool isPasswordValid; + final bool isFormValid; + + FormValidationState copyWith({ + String? username, + String? email, + String? password, + bool? isUsernameValid, + bool? isEmailValid, + bool? isPasswordValid, + bool? isFormValid, + }) => FormValidationState( + username: username ?? this.username, + email: email ?? this.email, + password: password ?? this.password, + isUsernameValid: isUsernameValid ?? this.isUsernameValid, + isEmailValid: isEmailValid ?? this.isEmailValid, + isPasswordValid: isPasswordValid ?? this.isPasswordValid, + isFormValid: isFormValid ?? this.isFormValid, + ); + + @override + List get props => [ + username, + email, + password, + isUsernameValid, + isEmailValid, + isPasswordValid, + isFormValid, + ]; +} From 5c024f8cf4e80dbe9c94155fd91d9f70e6310f03 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:14:13 +0200 Subject: [PATCH 078/475] Add form validation --- lib/core/dependency_injection.dart | 4 +- lib/features/application/poetlum_app.dart | 2 + .../pages/registration/registration_page.dart | 150 ++++++++++-------- 3 files changed, 91 insertions(+), 65 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 224f895..18168e0 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -11,6 +11,7 @@ import 'package:poetlum/features/registration/data/repository/firebase_repositor import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; GetIt getIt = GetIt.instance; @@ -36,5 +37,6 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt())) - ..registerFactory(() => RegisterCubit(getIt())); + ..registerFactory(() => RegisterCubit(getIt())) + ..registerFactory(() => FormValidationCubit()); } diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 580c1da..d695d8e 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -7,6 +7,7 @@ import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; import 'package:poetlum/features/registration/presentation/pages/registration/registration_page.dart'; class PoetlumApp extends StatelessWidget { @@ -17,6 +18,7 @@ class PoetlumApp extends StatelessWidget { providers: [ BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), BlocProvider(create:(context) => getIt(),), + BlocProvider(create:(context) => getIt()), ], child: GetMaterialApp( title: 'Poetlum', diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index 88c3a62..1cb90fd 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -3,6 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; import 'package:poetlum/features/registration/presentation/bloc/register_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation_state.dart'; import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/password_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/username_field.dart'; @@ -15,77 +17,97 @@ class RegistrationPage extends StatelessWidget { final TextEditingController _passwordController = TextEditingController(); @override - Widget build(BuildContext context) => Scaffold( - body: SafeArea( - child: Row( - children: [ - Expanded( - child: Column( + Widget build(BuildContext context) { + final formCubit = context.read(); + + _usernameController.addListener(() { + formCubit.usernameChanged(_usernameController.text); + }); + + _emailController.addListener(() { + formCubit.emailChanged(_emailController.text); + }); + + _passwordController.addListener(() { + formCubit.passwordChanged(_passwordController.text); + }); + + return Scaffold( + body: SafeArea( + child: BlocBuilder( + builder: (context, validationState) => Row( children: [ - const Spacer(), - const Text( - 'Registration', - style: TextStyle( - fontSize: 22, - ), - ), - const Spacer(flex: 2,), - - UsernameTextField(controller: _usernameController), - const Spacer(), - - EmailTextField(controller: _emailController), - const Spacer(), - - PasswordTextField(controller: _passwordController), - const Spacer(flex: 2,), - - BlocConsumer( - listener: (context, state) { - if (state.status == RegisterStatus.success) { - _showPositiveToast(); - } else if (state.status == RegisterStatus.error) { - _showNegativeToast(state.errorMessage ?? 'Unknown error'); - } - }, - builder: (context, state) => state.status == RegisterStatus.submitting - ? const CircularProgressIndicator() - : FilledButton.tonal( - onPressed: (){ - context.read().register( - _usernameController.text, - _emailController.text, - _passwordController.text, - ); - }, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Text( - 'Register', - style: TextStyle( - fontSize: 16, - ), + Expanded( + child: Column( + children: [ + const Spacer(), + const Text( + 'Registration', + style: TextStyle( + fontSize: 22, ), ), - ), - ), - const Spacer(), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Already have an account?'), - TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), - ], + const Spacer(flex: 2,), + + UsernameTextField(controller: _usernameController), + const Spacer(), + + EmailTextField(controller: _emailController), + const Spacer(), + + PasswordTextField(controller: _passwordController), + const Spacer(flex: 2,), + + BlocConsumer( + listener: (context, registerState) { + if (registerState.status == RegisterStatus.success) { + _showPositiveToast(); + } else if (registerState.status == RegisterStatus.error) { + _showNegativeToast(registerState.errorMessage ?? 'Unknown error'); + } + }, + builder: (context, state) => state.status == RegisterStatus.submitting + ? const CircularProgressIndicator() + : FilledButton.tonal( + onPressed: validationState.isFormValid + ? () { + context.read().register( + _usernameController.text, + _emailController.text, + _passwordController.text, + ); + } + : null, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text( + 'Register', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + ), + const Spacer(), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Already have an account?'), + TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), + ], + ), + const Spacer(flex: 12,), + ], + ), ), - const Spacer(flex: 12,), ], ), - ), - ], + ), ), - ), - ); + ); + } Future _showPositiveToast() async{ await Fluttertoast.showToast( From 8e2d5ddd860ac60dba497bbe218ff575221067a4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:55:10 +0200 Subject: [PATCH 079/475] Rewrite validation logic --- .../presentation/bloc/validation_cubit.dart | 59 ++++++-- .../presentation/bloc/validation_state.dart | 140 ++++++++++++++---- 2 files changed, 155 insertions(+), 44 deletions(-) diff --git a/lib/features/registration/presentation/bloc/validation_cubit.dart b/lib/features/registration/presentation/bloc/validation_cubit.dart index 897236e..0f2263a 100644 --- a/lib/features/registration/presentation/bloc/validation_cubit.dart +++ b/lib/features/registration/presentation/bloc/validation_cubit.dart @@ -1,5 +1,3 @@ - - import 'package:email_validator/email_validator.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/registration/presentation/bloc/validation_state.dart'; @@ -9,35 +7,70 @@ class FormValidationCubit extends Cubit { void usernameChanged(String value) { final usernameValid = _isUsernameValid(value); - + late UsernameValidationStates validationResult; + + if(value.length < 5){ + validationResult = UsernameValidationStates.tooShort; + } else if(value.length > 30){ + validationResult = UsernameValidationStates.tooLong; + } else if(!RegExp(r'^[a-zA-Z0-9]+$').hasMatch(value)){ + validationResult = UsernameValidationStates.inappropriateChars; + } else{ + validationResult = UsernameValidationStates.correct; + } + emit(state.copyWith( - username: value, - isUsernameValid: usernameValid, - isFormValid: usernameValid && state.isEmailValid && state.isPasswordValid, + usernameValidationState: UsernameValidationState( + username: value, + isUsernameValid: usernameValid, + state: validationResult, + ), ),); } void emailChanged(String value) { final emailValid = _isEmailValid(value); + late EmailValidationStates validationResult; + + if(!EmailValidator.validate(value)){ + validationResult = EmailValidationStates.wrongFormat; + } else if(value.length > 40){ + validationResult = EmailValidationStates.tooLong; + } else{ + validationResult = EmailValidationStates.correct; + } emit(state.copyWith( - email: value, - isEmailValid: emailValid, - isFormValid: state.isUsernameValid && emailValid && state.isPasswordValid, + emailValidationState: EmailValidationState( + email: value, + isEmailValid: emailValid, + state: validationResult, + ), ),); } void passwordChanged(String value) { final passwordValid = _isPasswordValid(value); + late PasswordValidationStates validationResult; + + if(value.length < 8){ + validationResult = PasswordValidationStates.tooShort; + } else if(value.length > 20){ + validationResult = PasswordValidationStates.tooLong; + } else{ + validationResult = PasswordValidationStates.correct; + } emit(state.copyWith( - password: value, - isPasswordValid: passwordValid, - isFormValid: state.isUsernameValid && state.isEmailValid && passwordValid, + passwordValidationState: PasswordValidationState( + password: value, + isPasswordValid: passwordValid, + state: validationResult, + ), ),); } - bool _isUsernameValid(String username) => RegExp(r'^[a-zA-Z0-9]+$').hasMatch(username) && username.length <= 30; + bool _isUsernameValid(String username) => RegExp(r'^[a-zA-Z0-9]+$').hasMatch(username) && username.length >= 5 && username.length <= 30; bool _isEmailValid(String email) => EmailValidator.validate(email) && email.length <= 40; diff --git a/lib/features/registration/presentation/bloc/validation_state.dart b/lib/features/registration/presentation/bloc/validation_state.dart index 3f9e1c0..b420cf1 100644 --- a/lib/features/registration/presentation/bloc/validation_state.dart +++ b/lib/features/registration/presentation/bloc/validation_state.dart @@ -1,51 +1,129 @@ import 'package:equatable/equatable.dart'; - class FormValidationState extends Equatable { const FormValidationState({ - this.username = '', - this.email = '', - this.password = '', - this.isUsernameValid = false, - this.isEmailValid = false, - this.isPasswordValid = false, + this.emailValidationState = const EmailValidationState(), + this.usernameValidationState = const UsernameValidationState(), + this.passwordValidationState = const PasswordValidationState(), this.isFormValid = false, }); - final String username; - final String email; - final String password; - final bool isUsernameValid; - final bool isEmailValid; - final bool isPasswordValid; + final EmailValidationState emailValidationState; + final UsernameValidationState usernameValidationState; + final PasswordValidationState passwordValidationState; final bool isFormValid; FormValidationState copyWith({ - String? username, - String? email, - String? password, - bool? isUsernameValid, - bool? isEmailValid, - bool? isPasswordValid, + EmailValidationState? emailValidationState, + UsernameValidationState? usernameValidationState, + PasswordValidationState? passwordValidationState, bool? isFormValid, }) => FormValidationState( - username: username ?? this.username, - email: email ?? this.email, - password: password ?? this.password, - isUsernameValid: isUsernameValid ?? this.isUsernameValid, - isEmailValid: isEmailValid ?? this.isEmailValid, - isPasswordValid: isPasswordValid ?? this.isPasswordValid, + emailValidationState: emailValidationState ?? this.emailValidationState, + usernameValidationState: usernameValidationState ?? this.usernameValidationState, + passwordValidationState: passwordValidationState ?? this.passwordValidationState, isFormValid: isFormValid ?? this.isFormValid, ); @override List get props => [ - username, - email, - password, - isUsernameValid, - isEmailValid, - isPasswordValid, + emailValidationState, + usernameValidationState, + passwordValidationState, isFormValid, ]; } + +enum UsernameValidationStates{ + tooShort, + tooLong, + inappropriateChars, + correct, +} + +class UsernameValidationState extends Equatable { + const UsernameValidationState({ + this.username = '', + this.state = UsernameValidationStates.tooShort, + this.isUsernameValid = false, + }); + + final String username; + final UsernameValidationStates state; + final bool isUsernameValid; + + @override + List get props => [username, state, isUsernameValid]; + + UsernameValidationState copyWith({ + String? username, + UsernameValidationStates? state, + bool? isUsernameValid, + }) => UsernameValidationState( + username: username ?? this.username, + state: state ?? this.state, + isUsernameValid: isUsernameValid ?? this.isUsernameValid, + ); +} + +enum PasswordValidationStates{ + tooShort, + tooLong, + correct, +} + +class PasswordValidationState extends Equatable { + const PasswordValidationState({ + this.password = '', + this.state = PasswordValidationStates.tooShort, + this.isPasswordValid = false, + }); + + final String password; + final PasswordValidationStates state; + final bool isPasswordValid; + + @override + List get props => [password, state, isPasswordValid]; + + PasswordValidationState copyWith({ + String? password, + PasswordValidationStates? state, + bool? isPasswordValid, + }) => PasswordValidationState( + password: password ?? this.password, + state: state ?? this.state, + isPasswordValid: isPasswordValid ?? this.isPasswordValid, + ); +} + +enum EmailValidationStates{ + wrongFormat, + tooLong, + correct, +} + +class EmailValidationState extends Equatable { + const EmailValidationState({ + this.email = '', + this.state = EmailValidationStates.wrongFormat, + this.isEmailValid = false, + }); + + final String email; + final EmailValidationStates state; + final bool isEmailValid; + + @override + List get props => [email, state, isEmailValid]; + + EmailValidationState copyWith({ + String? email, + EmailValidationStates? state, + bool? isEmailValid, + }) => EmailValidationState( + email: email ?? this.email, + state: state ?? this.state, + isEmailValid: isEmailValid ?? this.isEmailValid, + ); +} From 15a49f5ad507109402549dcf1ed397b73e0da9a7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:55:26 +0200 Subject: [PATCH 080/475] Rewrite username textfield --- .../presentation/widgets/username_field.dart | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/features/registration/presentation/widgets/username_field.dart b/lib/features/registration/presentation/widgets/username_field.dart index 1eb2f28..5222430 100644 --- a/lib/features/registration/presentation/widgets/username_field.dart +++ b/lib/features/registration/presentation/widgets/username_field.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation_state.dart'; class UsernameTextField extends StatelessWidget { const UsernameTextField({super.key, required this.controller}); @@ -6,19 +9,37 @@ class UsernameTextField extends StatelessWidget { final TextEditingController controller; @override - Widget build(BuildContext context) => SizedBox( - width: MediaQuery.of(context).size.width/1.5, - child: TextField( - controller: controller, - enableSuggestions: false, - autocorrect: false, - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: 'Username', - hintStyle: TextStyle( - color: Colors.grey, + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state){ + String? errorText; + + if(state.usernameValidationState.state == UsernameValidationStates.correct || state.usernameValidationState.username.isEmpty){ + errorText = null; + } else if(state.usernameValidationState.state == UsernameValidationStates.tooShort){ + errorText = 'The username lenght is too short'; + } else if(state.usernameValidationState.state == UsernameValidationStates.tooLong){ + errorText = 'The username lenght is too long'; + }else{ + errorText = 'Username must contain only letters and numbers'; + } + + return SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: TextField( + controller: controller, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + errorText: errorText, + errorMaxLines: 3, + border: const OutlineInputBorder(), + hintText: 'Username', + hintStyle: const TextStyle( + color: Colors.grey, + ), + ), ), - ), - ), + ); + } ); } From dacdc8b294e195ae58fe9491cde3031b97f842ef Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 19:10:04 +0200 Subject: [PATCH 081/475] Reorder folder structure --- lib/core/dependency_injection.dart | 4 ++-- lib/features/application/poetlum_app.dart | 4 ++-- .../bloc/{ => registation}/register_cubit.dart | 2 +- .../bloc/{ => registation}/register_state.dart | 0 .../bloc/{ => validation}/validation_cubit.dart | 2 +- .../bloc/{ => validation}/validation_state.dart | 0 .../pages/registration/registration_page.dart | 8 ++++---- .../registration/presentation/widgets/username_field.dart | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) rename lib/features/registration/presentation/bloc/{ => registation}/register_cubit.dart (97%) rename lib/features/registration/presentation/bloc/{ => registation}/register_state.dart (100%) rename lib/features/registration/presentation/bloc/{ => validation}/validation_cubit.dart (98%) rename lib/features/registration/presentation/bloc/{ => validation}/validation_state.dart (100%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 18168e0..ed56014 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -10,8 +10,8 @@ import 'package:poetlum/features/registration/data/data_sources/remote/firebase_ import 'package:poetlum/features/registration/data/repository/firebase_repository_impl.dart'; import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; -import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; GetIt getIt = GetIt.instance; diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index d695d8e..023a892 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -6,8 +6,8 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; -import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/registration/presentation/pages/registration/registration_page.dart'; class PoetlumApp extends StatelessWidget { diff --git a/lib/features/registration/presentation/bloc/register_cubit.dart b/lib/features/registration/presentation/bloc/registation/register_cubit.dart similarity index 97% rename from lib/features/registration/presentation/bloc/register_cubit.dart rename to lib/features/registration/presentation/bloc/registation/register_cubit.dart index 7603a02..179036e 100644 --- a/lib/features/registration/presentation/bloc/register_cubit.dart +++ b/lib/features/registration/presentation/bloc/registation/register_cubit.dart @@ -1,7 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/registration/domain/usecases/register_user_params.dart'; import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; -import 'package:poetlum/features/registration/presentation/bloc/register_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/registation/register_state.dart'; class RegisterCubit extends Cubit { RegisterCubit(this._registerUserUseCase) : super(const RegisterState()); diff --git a/lib/features/registration/presentation/bloc/register_state.dart b/lib/features/registration/presentation/bloc/registation/register_state.dart similarity index 100% rename from lib/features/registration/presentation/bloc/register_state.dart rename to lib/features/registration/presentation/bloc/registation/register_state.dart diff --git a/lib/features/registration/presentation/bloc/validation_cubit.dart b/lib/features/registration/presentation/bloc/validation/validation_cubit.dart similarity index 98% rename from lib/features/registration/presentation/bloc/validation_cubit.dart rename to lib/features/registration/presentation/bloc/validation/validation_cubit.dart index 0f2263a..bf5e201 100644 --- a/lib/features/registration/presentation/bloc/validation_cubit.dart +++ b/lib/features/registration/presentation/bloc/validation/validation_cubit.dart @@ -1,6 +1,6 @@ import 'package:email_validator/email_validator.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; class FormValidationCubit extends Cubit { FormValidationCubit() : super(const FormValidationState()); diff --git a/lib/features/registration/presentation/bloc/validation_state.dart b/lib/features/registration/presentation/bloc/validation/validation_state.dart similarity index 100% rename from lib/features/registration/presentation/bloc/validation_state.dart rename to lib/features/registration/presentation/bloc/validation/validation_state.dart diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index 1cb90fd..6660948 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:poetlum/features/registration/presentation/bloc/register_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/register_state.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/registation/register_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/password_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/username_field.dart'; diff --git a/lib/features/registration/presentation/widgets/username_field.dart b/lib/features/registration/presentation/widgets/username_field.dart index 5222430..334d23b 100644 --- a/lib/features/registration/presentation/widgets/username_field.dart +++ b/lib/features/registration/presentation/widgets/username_field.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; class UsernameTextField extends StatelessWidget { const UsernameTextField({super.key, required this.controller}); From 1bd5c8808800f48566c9555e77d64aeda80a4c43 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:56:14 +0200 Subject: [PATCH 082/475] Rework validation process --- .../bloc/validation/validation_cubit.dart | 81 ++++-------- .../bloc/validation/validation_state.dart | 123 ++++-------------- 2 files changed, 53 insertions(+), 151 deletions(-) diff --git a/lib/features/registration/presentation/bloc/validation/validation_cubit.dart b/lib/features/registration/presentation/bloc/validation/validation_cubit.dart index bf5e201..3fec473 100644 --- a/lib/features/registration/presentation/bloc/validation/validation_cubit.dart +++ b/lib/features/registration/presentation/bloc/validation/validation_cubit.dart @@ -1,78 +1,49 @@ -import 'package:email_validator/email_validator.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validators.dart'; class FormValidationCubit extends Cubit { - FormValidationCubit() : super(const FormValidationState()); + FormValidationCubit({ + required this.usernameValidator, + required this.emailValidator, + required this.passwordValidator, + }) + : super(const FormValidationState()); - void usernameChanged(String value) { - final usernameValid = _isUsernameValid(value); - late UsernameValidationStates validationResult; - - if(value.length < 5){ - validationResult = UsernameValidationStates.tooShort; - } else if(value.length > 30){ - validationResult = UsernameValidationStates.tooLong; - } else if(!RegExp(r'^[a-zA-Z0-9]+$').hasMatch(value)){ - validationResult = UsernameValidationStates.inappropriateChars; - } else{ - validationResult = UsernameValidationStates.correct; - } + final Validator usernameValidator; + final Validator emailValidator; + final Validator passwordValidator; + void usernameChanged(String value) { + final result = usernameValidator.validate(value); emit(state.copyWith( - usernameValidationState: UsernameValidationState( - username: value, - isUsernameValid: usernameValid, - state: validationResult, + usernameValidationState: ValidationState( + value: value, + isValid: result.isValid, + errorMessage: result.errorMessage, ), ),); } void emailChanged(String value) { - final emailValid = _isEmailValid(value); - late EmailValidationStates validationResult; - - if(!EmailValidator.validate(value)){ - validationResult = EmailValidationStates.wrongFormat; - } else if(value.length > 40){ - validationResult = EmailValidationStates.tooLong; - } else{ - validationResult = EmailValidationStates.correct; - } - + final result = emailValidator.validate(value); emit(state.copyWith( - emailValidationState: EmailValidationState( - email: value, - isEmailValid: emailValid, - state: validationResult, + emailValidationState: ValidationState( + value: value, + isValid: result.isValid, + errorMessage: result.errorMessage, ), ),); } void passwordChanged(String value) { - final passwordValid = _isPasswordValid(value); - late PasswordValidationStates validationResult; - - if(value.length < 8){ - validationResult = PasswordValidationStates.tooShort; - } else if(value.length > 20){ - validationResult = PasswordValidationStates.tooLong; - } else{ - validationResult = PasswordValidationStates.correct; - } - + final result = passwordValidator.validate(value); emit(state.copyWith( - passwordValidationState: PasswordValidationState( - password: value, - isPasswordValid: passwordValid, - state: validationResult, + passwordValidationState: ValidationState( + value: value, + isValid: result.isValid, + errorMessage: result.errorMessage, ), ),); } - - bool _isUsernameValid(String username) => RegExp(r'^[a-zA-Z0-9]+$').hasMatch(username) && username.length >= 5 && username.length <= 30; - - bool _isEmailValid(String email) => EmailValidator.validate(email) && email.length <= 40; - - bool _isPasswordValid(String password) => password.length >= 8 && password.length <= 20; } diff --git a/lib/features/registration/presentation/bloc/validation/validation_state.dart b/lib/features/registration/presentation/bloc/validation/validation_state.dart index b420cf1..f366c3a 100644 --- a/lib/features/registration/presentation/bloc/validation/validation_state.dart +++ b/lib/features/registration/presentation/bloc/validation/validation_state.dart @@ -2,21 +2,21 @@ import 'package:equatable/equatable.dart'; class FormValidationState extends Equatable { const FormValidationState({ - this.emailValidationState = const EmailValidationState(), - this.usernameValidationState = const UsernameValidationState(), - this.passwordValidationState = const PasswordValidationState(), + this.emailValidationState = const ValidationState(), + this.usernameValidationState = const ValidationState(), + this.passwordValidationState = const ValidationState(), this.isFormValid = false, }); - final EmailValidationState emailValidationState; - final UsernameValidationState usernameValidationState; - final PasswordValidationState passwordValidationState; + final ValidationState emailValidationState; + final ValidationState usernameValidationState; + final ValidationState passwordValidationState; final bool isFormValid; FormValidationState copyWith({ - EmailValidationState? emailValidationState, - UsernameValidationState? usernameValidationState, - PasswordValidationState? passwordValidationState, + ValidationState? emailValidationState, + ValidationState? usernameValidationState, + ValidationState? passwordValidationState, bool? isFormValid, }) => FormValidationState( emailValidationState: emailValidationState ?? this.emailValidationState, @@ -34,96 +34,27 @@ class FormValidationState extends Equatable { ]; } -enum UsernameValidationStates{ - tooShort, - tooLong, - inappropriateChars, - correct, -} - -class UsernameValidationState extends Equatable { - const UsernameValidationState({ - this.username = '', - this.state = UsernameValidationStates.tooShort, - this.isUsernameValid = false, - }); - - final String username; - final UsernameValidationStates state; - final bool isUsernameValid; - - @override - List get props => [username, state, isUsernameValid]; - - UsernameValidationState copyWith({ - String? username, - UsernameValidationStates? state, - bool? isUsernameValid, - }) => UsernameValidationState( - username: username ?? this.username, - state: state ?? this.state, - isUsernameValid: isUsernameValid ?? this.isUsernameValid, - ); -} - -enum PasswordValidationStates{ - tooShort, - tooLong, - correct, -} - -class PasswordValidationState extends Equatable { - const PasswordValidationState({ - this.password = '', - this.state = PasswordValidationStates.tooShort, - this.isPasswordValid = false, +class ValidationState extends Equatable { + const ValidationState({ + this.value = '', + this.isValid = false, + this.errorMessage, }); - final String password; - final PasswordValidationStates state; - final bool isPasswordValid; + final String value; + final bool isValid; + final String? errorMessage; @override - List get props => [password, state, isPasswordValid]; - - PasswordValidationState copyWith({ - String? password, - PasswordValidationStates? state, - bool? isPasswordValid, - }) => PasswordValidationState( - password: password ?? this.password, - state: state ?? this.state, - isPasswordValid: isPasswordValid ?? this.isPasswordValid, - ); -} - -enum EmailValidationStates{ - wrongFormat, - tooLong, - correct, -} - -class EmailValidationState extends Equatable { - const EmailValidationState({ - this.email = '', - this.state = EmailValidationStates.wrongFormat, - this.isEmailValid = false, - }); - - final String email; - final EmailValidationStates state; - final bool isEmailValid; - - @override - List get props => [email, state, isEmailValid]; - - EmailValidationState copyWith({ - String? email, - EmailValidationStates? state, - bool? isEmailValid, - }) => EmailValidationState( - email: email ?? this.email, - state: state ?? this.state, - isEmailValid: isEmailValid ?? this.isEmailValid, + List get props => [value, isValid, errorMessage]; + + ValidationState copyWith({ + String? value, + bool? isValid, + String? errorMessage, + }) => ValidationState( + value: value ?? this.value, + isValid: isValid ?? this.isValid, + errorMessage: errorMessage ?? this.errorMessage, ); } From 36f0ebb110a15ef3a64f19c997f525491a69956c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:56:21 +0200 Subject: [PATCH 083/475] Create validators --- .../bloc/validation/validators.dart | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/features/registration/presentation/bloc/validation/validators.dart diff --git a/lib/features/registration/presentation/bloc/validation/validators.dart b/lib/features/registration/presentation/bloc/validation/validators.dart new file mode 100644 index 0000000..e2332bf --- /dev/null +++ b/lib/features/registration/presentation/bloc/validation/validators.dart @@ -0,0 +1,40 @@ +import 'package:email_validator/email_validator.dart'; + +abstract class Validator { + ValidationResult validate(T value); +} + +class ValidationResult { + ValidationResult(this.isValid, {this.errorMessage}); + + final bool isValid; + final String? errorMessage; +} + +class UsernameValidator implements Validator { + @override + ValidationResult validate(String value) { + if (value.length < 5) return ValidationResult(false, errorMessage: 'Username is too short'); + if (value.length > 30) return ValidationResult(false, errorMessage: 'Username is too long'); + if (!RegExp(r'^[a-zA-Z0-9]+$').hasMatch(value)) return ValidationResult(false, errorMessage: 'Invalid characters in username'); + return ValidationResult(true); + } +} + +class LocalEmailValidator implements Validator { + @override + ValidationResult validate(String value) { + if (!EmailValidator.validate(value)) return ValidationResult(false, errorMessage: 'Email format is wrong'); + if (value.length > 40) return ValidationResult(false, errorMessage: 'Email is too long'); + return ValidationResult(true); + } +} + +class PasswordValidator implements Validator { + @override + ValidationResult validate(String value) { + if (value.length < 8) return ValidationResult(false, errorMessage: 'Password is too short'); + if (value.length > 20) return ValidationResult(false, errorMessage: 'Password is too long'); + return ValidationResult(true); + } +} From 2892029a22e0571390e4aa55e31c3e24e252f518 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:56:42 +0200 Subject: [PATCH 084/475] Add all stuff in the DI --- lib/core/dependency_injection.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index ed56014..08e4cf5 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -12,6 +12,7 @@ import 'package:poetlum/features/registration/domain/repository/firebase_reposit import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validators.dart'; GetIt getIt = GetIt.instance; @@ -35,8 +36,17 @@ void initializeDependencies() { ..registerSingleton(GetPoemsUseCase(getIt())) ..registerSingleton(RegisterUserUseCase(getIt())) + // Validators + ..registerLazySingleton(() => UsernameValidator()) + ..registerLazySingleton(() => LocalEmailValidator()) + ..registerLazySingleton(() => PasswordValidator()) + // Bloc ..registerFactory(() => RemotePoemBloc(getIt())) ..registerFactory(() => RegisterCubit(getIt())) - ..registerFactory(() => FormValidationCubit()); + ..registerFactory(() => FormValidationCubit( + usernameValidator: getIt(), + emailValidator: getIt(), + passwordValidator: getIt(), + ),); } From 1937b0bdefcfd036ca1720659c2e1d10dd4e1d2c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:56:52 +0200 Subject: [PATCH 085/475] Change email textfield logic --- .../presentation/widgets/email_field.dart | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/features/registration/presentation/widgets/email_field.dart b/lib/features/registration/presentation/widgets/email_field.dart index b6b6a86..ad481c4 100644 --- a/lib/features/registration/presentation/widgets/email_field.dart +++ b/lib/features/registration/presentation/widgets/email_field.dart @@ -1,5 +1,7 @@ -import 'package:email_validator/email_validator.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; class EmailTextField extends StatelessWidget { const EmailTextField({super.key, required this.controller}); @@ -7,17 +9,19 @@ class EmailTextField extends StatelessWidget { final TextEditingController controller; @override - Widget build(BuildContext context) => SizedBox( - width: MediaQuery.of(context).size.width/1.5, - child: Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - child: TextFormField( + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state)=> SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: TextField( controller: controller, - validator: (value) => EmailValidator.validate(value!) ? null : 'Please enter a valid email', - decoration: const InputDecoration( - border: OutlineInputBorder(), + decoration: InputDecoration( + errorMaxLines: 3, + errorText: state.emailValidationState.isValid + ? null + : state.emailValidationState.errorMessage, + border: const OutlineInputBorder(), hintText: 'Email', - hintStyle: TextStyle( + hintStyle: const TextStyle( color: Colors.grey, ), ), From ccd1e37b1e195d0c5795726885146113600be024 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:56:57 +0200 Subject: [PATCH 086/475] Change password textfield logic --- .../presentation/widgets/password_field.dart | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/lib/features/registration/presentation/widgets/password_field.dart b/lib/features/registration/presentation/widgets/password_field.dart index 8e3981b..85007f1 100644 --- a/lib/features/registration/presentation/widgets/password_field.dart +++ b/lib/features/registration/presentation/widgets/password_field.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; class PasswordTextField extends StatefulWidget { const PasswordTextField({super.key, required this.controller}); @@ -13,26 +16,32 @@ class _PasswordTextFieldState extends State { bool _isPasswordVisible = false; @override - Widget build(BuildContext context) => SizedBox( - width: MediaQuery.of(context).size.width/1.5, - child: TextField( - controller: widget.controller, - obscureText: !_isPasswordVisible, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: 'Password', - hintStyle: const TextStyle( - color: Colors.grey, - ), - suffixIcon: IconButton( - icon: Icon( - _isPasswordVisible - ? Icons.visibility - : Icons.visibility_off, + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) => SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: TextField( + controller: widget.controller, + obscureText: !_isPasswordVisible, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + errorMaxLines: 3, + errorText: state.passwordValidationState.isValid + ? null + : state.passwordValidationState.errorMessage, + border: const OutlineInputBorder(), + hintText: 'Password', + hintStyle: const TextStyle( + color: Colors.grey, + ), + suffixIcon: IconButton( + icon: Icon( + _isPasswordVisible + ? Icons.visibility + : Icons.visibility_off, + ), + onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), ), - onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), ), ), ), From a349bbf97adf82bb9b4652475617551b8357374f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:57:03 +0200 Subject: [PATCH 087/475] Change username textfield logic --- .../presentation/widgets/username_field.dart | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/lib/features/registration/presentation/widgets/username_field.dart b/lib/features/registration/presentation/widgets/username_field.dart index 334d23b..7a762da 100644 --- a/lib/features/registration/presentation/widgets/username_field.dart +++ b/lib/features/registration/presentation/widgets/username_field.dart @@ -10,36 +10,24 @@ class UsernameTextField extends StatelessWidget { @override Widget build(BuildContext context) => BlocBuilder( - builder: (context, state){ - String? errorText; - - if(state.usernameValidationState.state == UsernameValidationStates.correct || state.usernameValidationState.username.isEmpty){ - errorText = null; - } else if(state.usernameValidationState.state == UsernameValidationStates.tooShort){ - errorText = 'The username lenght is too short'; - } else if(state.usernameValidationState.state == UsernameValidationStates.tooLong){ - errorText = 'The username lenght is too long'; - }else{ - errorText = 'Username must contain only letters and numbers'; - } - - return SizedBox( - width: MediaQuery.of(context).size.width/1.5, - child: TextField( - controller: controller, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - errorText: errorText, - errorMaxLines: 3, - border: const OutlineInputBorder(), - hintText: 'Username', - hintStyle: const TextStyle( - color: Colors.grey, - ), + builder: (context, state)=> SizedBox( + width: MediaQuery.of(context).size.width/1.5, + child: TextField( + controller: controller, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + errorText: state.usernameValidationState.isValid + ? null + : state.usernameValidationState.errorMessage, + errorMaxLines: 3, + border: const OutlineInputBorder(), + hintText: 'Username', + hintStyle: const TextStyle( + color: Colors.grey, ), ), - ); - } + ), + ), ); } From 62f288b85f4bebbd5c17f91322d8092597185c7e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 21:40:30 +0200 Subject: [PATCH 088/475] Update gradle build sdk version --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 3130fd9..240fbf0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -28,7 +28,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { namespace "com.example.poetlum" - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 34 ndkVersion flutter.ndkVersion compileOptions { From 8f96c4f63b1211a62bf15570ca5e7caab4af212f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 21:40:46 +0200 Subject: [PATCH 089/475] Fix inactive register button --- .../bloc/validation/validation_cubit.dart | 2 +- .../bloc/validation/validation_state.dart | 34 ++++++++++++------- .../pages/registration/registration_page.dart | 7 ++-- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/features/registration/presentation/bloc/validation/validation_cubit.dart b/lib/features/registration/presentation/bloc/validation/validation_cubit.dart index 3fec473..5ed0fa7 100644 --- a/lib/features/registration/presentation/bloc/validation/validation_cubit.dart +++ b/lib/features/registration/presentation/bloc/validation/validation_cubit.dart @@ -8,7 +8,7 @@ class FormValidationCubit extends Cubit { required this.emailValidator, required this.passwordValidator, }) - : super(const FormValidationState()); + : super(FormValidationState()); final Validator usernameValidator; final Validator emailValidator; diff --git a/lib/features/registration/presentation/bloc/validation/validation_state.dart b/lib/features/registration/presentation/bloc/validation/validation_state.dart index f366c3a..e7590ab 100644 --- a/lib/features/registration/presentation/bloc/validation/validation_state.dart +++ b/lib/features/registration/presentation/bloc/validation/validation_state.dart @@ -1,12 +1,17 @@ +// ignore_for_file: must_be_immutable + import 'package:equatable/equatable.dart'; class FormValidationState extends Equatable { - const FormValidationState({ + FormValidationState({ this.emailValidationState = const ValidationState(), this.usernameValidationState = const ValidationState(), this.passwordValidationState = const ValidationState(), - this.isFormValid = false, - }); + bool? isFormValid, + }) : isFormValid = isFormValid ?? + emailValidationState.isValid && + usernameValidationState.isValid && + passwordValidationState.isValid; final ValidationState emailValidationState; final ValidationState usernameValidationState; @@ -19,11 +24,14 @@ class FormValidationState extends Equatable { ValidationState? passwordValidationState, bool? isFormValid, }) => FormValidationState( - emailValidationState: emailValidationState ?? this.emailValidationState, - usernameValidationState: usernameValidationState ?? this.usernameValidationState, - passwordValidationState: passwordValidationState ?? this.passwordValidationState, - isFormValid: isFormValid ?? this.isFormValid, - ); + emailValidationState: emailValidationState ?? this.emailValidationState, + usernameValidationState: usernameValidationState ?? this.usernameValidationState, + passwordValidationState: passwordValidationState ?? this.passwordValidationState, + isFormValid: isFormValid ?? + (emailValidationState ?? this.emailValidationState).isValid && + (usernameValidationState ?? this.usernameValidationState).isValid && + (passwordValidationState ?? this.passwordValidationState).isValid, + ); @override List get props => [ @@ -52,9 +60,9 @@ class ValidationState extends Equatable { String? value, bool? isValid, String? errorMessage, - }) => ValidationState( - value: value ?? this.value, - isValid: isValid ?? this.isValid, - errorMessage: errorMessage ?? this.errorMessage, - ); + })=> ValidationState( + value: value ?? this.value, + isValid: isValid ?? this.isValid, + errorMessage: errorMessage ?? this.errorMessage, + ); } diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index 6660948..ac925b7 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -35,7 +35,9 @@ class RegistrationPage extends StatelessWidget { return Scaffold( body: SafeArea( child: BlocBuilder( - builder: (context, validationState) => Row( + builder: (context, validationState){ + print(validationState.isFormValid); + return Row( children: [ Expanded( child: Column( @@ -103,7 +105,8 @@ class RegistrationPage extends StatelessWidget { ), ), ], - ), + ); + }, ), ), ); From 22dde89132746bef38360f7277c6d5b05b094349 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 22:48:50 +0200 Subject: [PATCH 090/475] Add required fields --- .../presentation/bloc/registation/register_cubit.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/registration/presentation/bloc/registation/register_cubit.dart b/lib/features/registration/presentation/bloc/registation/register_cubit.dart index 179036e..1dd364f 100644 --- a/lib/features/registration/presentation/bloc/registation/register_cubit.dart +++ b/lib/features/registration/presentation/bloc/registation/register_cubit.dart @@ -8,7 +8,7 @@ class RegisterCubit extends Cubit { final RegisterUserUseCase _registerUserUseCase; - Future register(String username, String email, String password) async { + Future register({required String username, required String email, required String password}) async { if (username.isEmpty || email.isEmpty || password.isEmpty) { emit(state.copyWith(status: RegisterStatus.error, errorMessage: 'Please fill in all required fields')); return; From 6f3ce1e01b3bb008ffcd1ba60fce3678502ae6b3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 13 Nov 2023 22:49:31 +0200 Subject: [PATCH 091/475] Move register button to a separate widget --- .../pages/registration/registration_page.dart | 132 +++++------------- .../presentation/widgets/register_button.dart | 70 ++++++++++ 2 files changed, 106 insertions(+), 96 deletions(-) create mode 100644 lib/features/registration/presentation/widgets/register_button.dart diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/registration/presentation/pages/registration/registration_page.dart index ac925b7..040d70d 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/registration/presentation/pages/registration/registration_page.dart @@ -1,12 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/registation/register_state.dart'; import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; import 'package:poetlum/features/registration/presentation/widgets/password_field.dart'; +import 'package:poetlum/features/registration/presentation/widgets/register_button.dart'; import 'package:poetlum/features/registration/presentation/widgets/username_field.dart'; class RegistrationPage extends StatelessWidget { @@ -34,103 +31,46 @@ class RegistrationPage extends StatelessWidget { return Scaffold( body: SafeArea( - child: BlocBuilder( - builder: (context, validationState){ - print(validationState.isFormValid); - return Row( - children: [ - Expanded( - child: Column( + child: Row( + children: [ + Expanded( + child: Column( + children: [ + const Spacer(), + const Text( + 'Registration', + style: TextStyle( + fontSize: 22, + ), + ), + const Spacer(flex: 2,), + + UsernameTextField(controller: _usernameController), + const Spacer(), + + EmailTextField(controller: _emailController), + const Spacer(), + + PasswordTextField(controller: _passwordController), + const Spacer(flex: 2,), + + const RegisterButton(), + const Spacer(), + + Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - const Spacer(), - const Text( - 'Registration', - style: TextStyle( - fontSize: 22, - ), - ), - const Spacer(flex: 2,), - - UsernameTextField(controller: _usernameController), - const Spacer(), - - EmailTextField(controller: _emailController), - const Spacer(), - - PasswordTextField(controller: _passwordController), - const Spacer(flex: 2,), - - BlocConsumer( - listener: (context, registerState) { - if (registerState.status == RegisterStatus.success) { - _showPositiveToast(); - } else if (registerState.status == RegisterStatus.error) { - _showNegativeToast(registerState.errorMessage ?? 'Unknown error'); - } - }, - builder: (context, state) => state.status == RegisterStatus.submitting - ? const CircularProgressIndicator() - : FilledButton.tonal( - onPressed: validationState.isFormValid - ? () { - context.read().register( - _usernameController.text, - _emailController.text, - _passwordController.text, - ); - } - : null, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Text( - 'Register', - style: TextStyle( - fontSize: 16, - ), - ), - ), - ), - ), - const Spacer(), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Already have an account?'), - TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), - ], - ), - const Spacer(flex: 12,), + const Text('Already have an account?'), + TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), ], ), - ), - ], - ); - }, + const Spacer(flex: 12,), + ], + ), + ), + ], ), ), ); } - - Future _showPositiveToast() async{ - await Fluttertoast.showToast( - msg: 'Your registration was successful', - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.green, - textColor: Colors.white, - fontSize: 16, - ); - } - - Future _showNegativeToast(String error) async{ - await Fluttertoast.showToast( - msg: error, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16, - ); - } } diff --git a/lib/features/registration/presentation/widgets/register_button.dart b/lib/features/registration/presentation/widgets/register_button.dart new file mode 100644 index 0000000..cca0323 --- /dev/null +++ b/lib/features/registration/presentation/widgets/register_button.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/registation/register_state.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; + +class RegisterButton extends StatelessWidget { + const RegisterButton({ + super.key, + }); + + @override + Widget build(BuildContext context)=> BlocBuilder( + builder: (_, validationState)=> BlocConsumer( + listener: (__, registerState) { + if (registerState.status == RegisterStatus.success) { + _showPositiveToast(); + } else if (registerState.status == RegisterStatus.error) { + _showNegativeToast(registerState.errorMessage ?? 'Unknown error'); + } + }, + builder: (context, state) => state.status == RegisterStatus.submitting + ? const CircularProgressIndicator() + : FilledButton.tonal( + onPressed: validationState.isFormValid + ? () { + context.read().register( + username: validationState.usernameValidationState.value, + email: validationState.emailValidationState.value, + password: validationState.passwordValidationState.value, + ); + } + : null, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text( + 'Register', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + ), + ); + + Future _showPositiveToast() async{ + await Fluttertoast.showToast( + msg: 'Your registration was successful', + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + ); + } + + Future _showNegativeToast(String error) async{ + await Fluttertoast.showToast( + msg: error, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); + } +} From 5267bd48507dc877e2a36e096cc6f7ad0e81fd81 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 15 Nov 2023 15:55:48 +0200 Subject: [PATCH 092/475] Rename folder --- lib/core/dependency_injection.dart | 14 +++++++------- lib/features/application/poetlum_app.dart | 6 +++--- .../data/data_sources/remote/firebase_service.dart | 0 .../data/repository/firebase_repository_impl.dart | 4 ++-- .../domain/repository/firebase_repository.dart | 0 .../domain/usecases/register_user_params.dart | 0 .../domain/usecases/register_user_usecase.dart | 4 ++-- .../bloc/registation/register_cubit.dart | 6 +++--- .../bloc/registation/register_state.dart | 0 .../bloc/validation/validation_cubit.dart | 4 ++-- .../bloc/validation/validation_state.dart | 0 .../presentation/bloc/validation/validators.dart | 0 .../pages/registration/registration_page.dart | 10 +++++----- .../presentation/widgets/email_field.dart | 4 ++-- .../presentation/widgets/password_field.dart | 4 ++-- .../presentation/widgets/register_button.dart | 8 ++++---- .../presentation/widgets/username_field.dart | 4 ++-- pubspec.lock | 12 ++++++------ 18 files changed, 40 insertions(+), 40 deletions(-) rename lib/features/{registration => authorization}/data/data_sources/remote/firebase_service.dart (100%) rename lib/features/{registration => authorization}/data/repository/firebase_repository_impl.dart (66%) rename lib/features/{registration => authorization}/domain/repository/firebase_repository.dart (100%) rename lib/features/{registration => authorization}/domain/usecases/register_user_params.dart (100%) rename lib/features/{registration => authorization}/domain/usecases/register_user_usecase.dart (71%) rename lib/features/{registration => authorization}/presentation/bloc/registation/register_cubit.dart (76%) rename lib/features/{registration => authorization}/presentation/bloc/registation/register_state.dart (100%) rename lib/features/{registration => authorization}/presentation/bloc/validation/validation_cubit.dart (86%) rename lib/features/{registration => authorization}/presentation/bloc/validation/validation_state.dart (100%) rename lib/features/{registration => authorization}/presentation/bloc/validation/validators.dart (100%) rename lib/features/{registration => authorization}/presentation/pages/registration/registration_page.dart (82%) rename lib/features/{registration => authorization}/presentation/widgets/email_field.dart (81%) rename lib/features/{registration => authorization}/presentation/widgets/password_field.dart (88%) rename lib/features/{registration => authorization}/presentation/widgets/register_button.dart (84%) rename lib/features/{registration => authorization}/presentation/widgets/username_field.dart (82%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 08e4cf5..dfc51a4 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -1,18 +1,18 @@ import 'package:dio/dio.dart'; import 'package:get_it/get_it.dart'; +import 'package:poetlum/features/authorization/data/data_sources/remote/firebase_service.dart'; +import 'package:poetlum/features/authorization/data/repository/firebase_repository_impl.dart'; +import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; import 'package:poetlum/features/poems_feed/data/repository/poem_repository_impl.dart'; import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/realtime_database/domain/entities/database_manager.dart'; -import 'package:poetlum/features/registration/data/data_sources/remote/firebase_service.dart'; -import 'package:poetlum/features/registration/data/repository/firebase_repository_impl.dart'; -import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; -import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; -import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validators.dart'; GetIt getIt = GetIt.instance; diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 023a892..2102e8e 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -4,11 +4,11 @@ import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; -import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/pages/registration/registration_page.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); diff --git a/lib/features/registration/data/data_sources/remote/firebase_service.dart b/lib/features/authorization/data/data_sources/remote/firebase_service.dart similarity index 100% rename from lib/features/registration/data/data_sources/remote/firebase_service.dart rename to lib/features/authorization/data/data_sources/remote/firebase_service.dart diff --git a/lib/features/registration/data/repository/firebase_repository_impl.dart b/lib/features/authorization/data/repository/firebase_repository_impl.dart similarity index 66% rename from lib/features/registration/data/repository/firebase_repository_impl.dart rename to lib/features/authorization/data/repository/firebase_repository_impl.dart index 493fdcf..c2f9f8d 100644 --- a/lib/features/registration/data/repository/firebase_repository_impl.dart +++ b/lib/features/authorization/data/repository/firebase_repository_impl.dart @@ -1,5 +1,5 @@ -import 'package:poetlum/features/registration/data/data_sources/remote/firebase_service.dart'; -import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; +import 'package:poetlum/features/authorization/data/data_sources/remote/firebase_service.dart'; +import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; class FirebaseRepositoryImpl implements FirebaseRepository{ FirebaseRepositoryImpl(this._firebaseService); diff --git a/lib/features/registration/domain/repository/firebase_repository.dart b/lib/features/authorization/domain/repository/firebase_repository.dart similarity index 100% rename from lib/features/registration/domain/repository/firebase_repository.dart rename to lib/features/authorization/domain/repository/firebase_repository.dart diff --git a/lib/features/registration/domain/usecases/register_user_params.dart b/lib/features/authorization/domain/usecases/register_user_params.dart similarity index 100% rename from lib/features/registration/domain/usecases/register_user_params.dart rename to lib/features/authorization/domain/usecases/register_user_params.dart diff --git a/lib/features/registration/domain/usecases/register_user_usecase.dart b/lib/features/authorization/domain/usecases/register_user_usecase.dart similarity index 71% rename from lib/features/registration/domain/usecases/register_user_usecase.dart rename to lib/features/authorization/domain/usecases/register_user_usecase.dart index a25d3ca..007374c 100644 --- a/lib/features/registration/domain/usecases/register_user_usecase.dart +++ b/lib/features/authorization/domain/usecases/register_user_usecase.dart @@ -1,6 +1,6 @@ import 'package:poetlum/core/usecases/usecase.dart'; -import 'package:poetlum/features/registration/domain/repository/firebase_repository.dart'; -import 'package:poetlum/features/registration/domain/usecases/register_user_params.dart'; +import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register_user_params.dart'; class RegisterUserUseCase implements UseCase{ RegisterUserUseCase(this._firebaseRepository); diff --git a/lib/features/registration/presentation/bloc/registation/register_cubit.dart b/lib/features/authorization/presentation/bloc/registation/register_cubit.dart similarity index 76% rename from lib/features/registration/presentation/bloc/registation/register_cubit.dart rename to lib/features/authorization/presentation/bloc/registation/register_cubit.dart index 1dd364f..3d5cbb8 100644 --- a/lib/features/registration/presentation/bloc/registation/register_cubit.dart +++ b/lib/features/authorization/presentation/bloc/registation/register_cubit.dart @@ -1,7 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/domain/usecases/register_user_params.dart'; -import 'package:poetlum/features/registration/domain/usecases/register_user_usecase.dart'; -import 'package:poetlum/features/registration/presentation/bloc/registation/register_state.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register_user_params.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; class RegisterCubit extends Cubit { RegisterCubit(this._registerUserUseCase) : super(const RegisterState()); diff --git a/lib/features/registration/presentation/bloc/registation/register_state.dart b/lib/features/authorization/presentation/bloc/registation/register_state.dart similarity index 100% rename from lib/features/registration/presentation/bloc/registation/register_state.dart rename to lib/features/authorization/presentation/bloc/registation/register_state.dart diff --git a/lib/features/registration/presentation/bloc/validation/validation_cubit.dart b/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart similarity index 86% rename from lib/features/registration/presentation/bloc/validation/validation_cubit.dart rename to lib/features/authorization/presentation/bloc/validation/validation_cubit.dart index 5ed0fa7..74e78d7 100644 --- a/lib/features/registration/presentation/bloc/validation/validation_cubit.dart +++ b/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart @@ -1,6 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validators.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; class FormValidationCubit extends Cubit { FormValidationCubit({ diff --git a/lib/features/registration/presentation/bloc/validation/validation_state.dart b/lib/features/authorization/presentation/bloc/validation/validation_state.dart similarity index 100% rename from lib/features/registration/presentation/bloc/validation/validation_state.dart rename to lib/features/authorization/presentation/bloc/validation/validation_state.dart diff --git a/lib/features/registration/presentation/bloc/validation/validators.dart b/lib/features/authorization/presentation/bloc/validation/validators.dart similarity index 100% rename from lib/features/registration/presentation/bloc/validation/validators.dart rename to lib/features/authorization/presentation/bloc/validation/validators.dart diff --git a/lib/features/registration/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart similarity index 82% rename from lib/features/registration/presentation/pages/registration/registration_page.dart rename to lib/features/authorization/presentation/pages/registration/registration_page.dart index 040d70d..050fcc0 100644 --- a/lib/features/registration/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/widgets/email_field.dart'; -import 'package:poetlum/features/registration/presentation/widgets/password_field.dart'; -import 'package:poetlum/features/registration/presentation/widgets/register_button.dart'; -import 'package:poetlum/features/registration/presentation/widgets/username_field.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; class RegistrationPage extends StatelessWidget { RegistrationPage({super.key}); diff --git a/lib/features/registration/presentation/widgets/email_field.dart b/lib/features/authorization/presentation/widgets/email_field.dart similarity index 81% rename from lib/features/registration/presentation/widgets/email_field.dart rename to lib/features/authorization/presentation/widgets/email_field.dart index ad481c4..e023536 100644 --- a/lib/features/registration/presentation/widgets/email_field.dart +++ b/lib/features/authorization/presentation/widgets/email_field.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; class EmailTextField extends StatelessWidget { const EmailTextField({super.key, required this.controller}); diff --git a/lib/features/registration/presentation/widgets/password_field.dart b/lib/features/authorization/presentation/widgets/password_field.dart similarity index 88% rename from lib/features/registration/presentation/widgets/password_field.dart rename to lib/features/authorization/presentation/widgets/password_field.dart index 85007f1..3e5cc9c 100644 --- a/lib/features/registration/presentation/widgets/password_field.dart +++ b/lib/features/authorization/presentation/widgets/password_field.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; class PasswordTextField extends StatefulWidget { const PasswordTextField({super.key, required this.controller}); diff --git a/lib/features/registration/presentation/widgets/register_button.dart b/lib/features/authorization/presentation/widgets/register_button.dart similarity index 84% rename from lib/features/registration/presentation/widgets/register_button.dart rename to lib/features/authorization/presentation/widgets/register_button.dart index cca0323..2f08d3b 100644 --- a/lib/features/registration/presentation/widgets/register_button.dart +++ b/lib/features/authorization/presentation/widgets/register_button.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:poetlum/features/registration/presentation/bloc/registation/register_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/registation/register_state.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; class RegisterButton extends StatelessWidget { const RegisterButton({ diff --git a/lib/features/registration/presentation/widgets/username_field.dart b/lib/features/authorization/presentation/widgets/username_field.dart similarity index 82% rename from lib/features/registration/presentation/widgets/username_field.dart rename to lib/features/authorization/presentation/widgets/username_field.dart index 7a762da..6d905a6 100644 --- a/lib/features/registration/presentation/widgets/username_field.dart +++ b/lib/features/authorization/presentation/widgets/username_field.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/registration/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; class UsernameTextField extends StatelessWidget { const UsernameTextField({super.key, required this.controller}); diff --git a/pubspec.lock b/pubspec.lock index 12de637..36b2ed9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.0" build_resolvers: dependency: transitive description: @@ -444,10 +444,10 @@ packages: dependency: "direct main" description: name: fluttertoast - sha256: "69a5c4fcfe9a163bc927ff386c0dedd62575913bba942d0ce80c1fd092885255" + sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 url: "https://pub.dev" source: hosted - version: "8.2.3" + version: "8.2.4" frontend_server_client: dependency: transitive description: @@ -660,10 +660,10 @@ packages: dependency: transitive description: name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.0.5" pub_semver: dependency: transitive description: From ad9c3b9da3960d1b93aa66e71ee36a228fc8b90c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:03:27 +0200 Subject: [PATCH 093/475] Create login page template --- lib/features/application/poetlum_app.dart | 4 +- .../presentation/pages/login/login_page.dart | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 lib/features/authorization/presentation/pages/login/login_page.dart diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 2102e8e..f9906bc 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -6,7 +6,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; +import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; @@ -23,7 +23,7 @@ class PoetlumApp extends StatelessWidget { child: GetMaterialApp( title: 'Poetlum', theme: theme(), - home: RegistrationPage(), + home: LoginPage(), ), ); } diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart new file mode 100644 index 0000000..a26564e --- /dev/null +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; + +class LoginPage extends StatelessWidget { + LoginPage({super.key}); + + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + + @override + Widget build(BuildContext context) => Scaffold( + body: SafeArea( + child: Row( + children: [ + Expanded( + child: Column( + children: [ + const Spacer(), + const Text( + 'Login', + style: TextStyle( + fontSize: 22, + ), + ), + const Spacer(flex: 2,), + + EmailTextField(controller: _emailController), + const Spacer(), + + PasswordTextField(controller: _passwordController), + const Spacer(flex: 2,), + + const RegisterButton(), + const Spacer(), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Don't have an account?"), + TextButton(onPressed: (){}, child: const Text('Register', style: TextStyle(decoration: TextDecoration.underline),),), + ], + ), + const Spacer(flex: 12,), + ], + ), + ) + ], + ), + ), + ); +} From 47293ad27015b4a393ebaceb9ea242c0344f3a51 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:40:12 +0200 Subject: [PATCH 094/475] Rewrite RegisterButton logic --- lib/core/dependency_injection.dart | 2 +- lib/features/application/poetlum_app.dart | 6 +-- .../bloc/registation/register_cubit.dart | 4 +- .../bloc/registation/register_state.dart | 8 ++-- .../bloc/validation/validation_cubit.dart | 39 ++++++++++++++++++- .../presentation/pages/login/login_page.dart | 6 +-- .../pages/registration/registration_page.dart | 7 +++- .../presentation/widgets/email_field.dart | 2 +- .../presentation/widgets/password_field.dart | 2 +- .../presentation/widgets/register_button.dart | 26 ++++++++----- .../presentation/widgets/username_field.dart | 2 +- 11 files changed, 74 insertions(+), 30 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index dfc51a4..8747e9a 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -44,7 +44,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt())) ..registerFactory(() => RegisterCubit(getIt())) - ..registerFactory(() => FormValidationCubit( + ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), passwordValidator: getIt(), diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index f9906bc..30829f8 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -6,7 +6,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; +import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; @@ -18,12 +18,12 @@ class PoetlumApp extends StatelessWidget { providers: [ BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), BlocProvider(create:(context) => getIt(),), - BlocProvider(create:(context) => getIt()), + BlocProvider(create:(context) => getIt()), ], child: GetMaterialApp( title: 'Poetlum', theme: theme(), - home: LoginPage(), + home: RegistrationPage(), ), ); } diff --git a/lib/features/authorization/presentation/bloc/registation/register_cubit.dart b/lib/features/authorization/presentation/bloc/registation/register_cubit.dart index 3d5cbb8..ae6d0af 100644 --- a/lib/features/authorization/presentation/bloc/registation/register_cubit.dart +++ b/lib/features/authorization/presentation/bloc/registation/register_cubit.dart @@ -3,8 +3,8 @@ import 'package:poetlum/features/authorization/domain/usecases/register_user_par import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; -class RegisterCubit extends Cubit { - RegisterCubit(this._registerUserUseCase) : super(const RegisterState()); +class RegisterCubit extends Cubit { + RegisterCubit(this._registerUserUseCase) : super(const AuthState()); final RegisterUserUseCase _registerUserUseCase; diff --git a/lib/features/authorization/presentation/bloc/registation/register_state.dart b/lib/features/authorization/presentation/bloc/registation/register_state.dart index f6f6295..3013f1f 100644 --- a/lib/features/authorization/presentation/bloc/registation/register_state.dart +++ b/lib/features/authorization/presentation/bloc/registation/register_state.dart @@ -2,8 +2,8 @@ import 'package:equatable/equatable.dart'; enum RegisterStatus { initial, submitting, success, error } -class RegisterState extends Equatable { - const RegisterState({ +class AuthState extends Equatable { + const AuthState({ this.status = RegisterStatus.initial, this.errorMessage, }); @@ -11,10 +11,10 @@ class RegisterState extends Equatable { final RegisterStatus status; final String? errorMessage; - RegisterState copyWith({ + AuthState copyWith({ RegisterStatus? status, String? errorMessage, - }) => RegisterState( + }) => AuthState( status: status ?? this.status, errorMessage: errorMessage, ); diff --git a/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart b/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart index 74e78d7..95d39aa 100644 --- a/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart +++ b/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart @@ -2,8 +2,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; -class FormValidationCubit extends Cubit { - FormValidationCubit({ +abstract class FormValidationCubit{} + +class RegisterFormValidationCubit extends Cubit implements FormValidationCubit{ + RegisterFormValidationCubit({ required this.usernameValidator, required this.emailValidator, required this.passwordValidator, @@ -47,3 +49,36 @@ class FormValidationCubit extends Cubit { ),); } } + +class LoginValidationCubit extends Cubit implements FormValidationCubit{ + LoginValidationCubit({ + required this.emailValidator, + required this.passwordValidator, + }) + : super(FormValidationState()); + + final Validator emailValidator; + final Validator passwordValidator; + + void emailChanged(String value) { + final result = emailValidator.validate(value); + emit(state.copyWith( + emailValidationState: ValidationState( + value: value, + isValid: result.isValid, + errorMessage: result.errorMessage, + ), + ),); + } + + void passwordChanged(String value) { + final result = passwordValidator.validate(value); + emit(state.copyWith( + passwordValidationState: ValidationState( + value: value, + isValid: result.isValid, + errorMessage: result.errorMessage, + ), + ),); + } +} diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index a26564e..647e5b2 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; +//import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; class LoginPage extends StatelessWidget { LoginPage({super.key}); @@ -30,9 +30,9 @@ class LoginPage extends StatelessWidget { const Spacer(), PasswordTextField(controller: _passwordController), - const Spacer(flex: 2,), + const Spacer(), - const RegisterButton(), + //const AuthButton(text: 'Login',), const Spacer(), Row( diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 050fcc0..b9809c1 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; @@ -15,7 +18,7 @@ class RegistrationPage extends StatelessWidget { @override Widget build(BuildContext context) { - final formCubit = context.read(); + final formCubit = context.read(); _usernameController.addListener(() { formCubit.usernameChanged(_usernameController.text); @@ -54,7 +57,7 @@ class RegistrationPage extends StatelessWidget { PasswordTextField(controller: _passwordController), const Spacer(flex: 2,), - const RegisterButton(), + const AuthButton(text: 'Register'), const Spacer(), Row( diff --git a/lib/features/authorization/presentation/widgets/email_field.dart b/lib/features/authorization/presentation/widgets/email_field.dart index e023536..e9b5a20 100644 --- a/lib/features/authorization/presentation/widgets/email_field.dart +++ b/lib/features/authorization/presentation/widgets/email_field.dart @@ -9,7 +9,7 @@ class EmailTextField extends StatelessWidget { final TextEditingController controller; @override - Widget build(BuildContext context) => BlocBuilder( + Widget build(BuildContext context) => BlocBuilder( builder: (context, state)=> SizedBox( width: MediaQuery.of(context).size.width/1.5, child: TextField( diff --git a/lib/features/authorization/presentation/widgets/password_field.dart b/lib/features/authorization/presentation/widgets/password_field.dart index 3e5cc9c..891a502 100644 --- a/lib/features/authorization/presentation/widgets/password_field.dart +++ b/lib/features/authorization/presentation/widgets/password_field.dart @@ -16,7 +16,7 @@ class _PasswordTextFieldState extends State { bool _isPasswordVisible = false; @override - Widget build(BuildContext context) => BlocBuilder( + Widget build(BuildContext context) => BlocBuilder( builder: (context, state) => SizedBox( width: MediaQuery.of(context).size.width/1.5, child: TextField( diff --git a/lib/features/authorization/presentation/widgets/register_button.dart b/lib/features/authorization/presentation/widgets/register_button.dart index 2f08d3b..744bbdf 100644 --- a/lib/features/authorization/presentation/widgets/register_button.dart +++ b/lib/features/authorization/presentation/widgets/register_button.dart @@ -3,17 +3,23 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; -class RegisterButton extends StatelessWidget { - const RegisterButton({ - super.key, +class AuthButton< + InputCubit extends Cubit, + InputState extends AuthState, + FormCubit extends Cubit, + FormState extends FormValidationState + > extends StatelessWidget { + const AuthButton({ + super.key, required this.text, }); + final String text; + @override - Widget build(BuildContext context)=> BlocBuilder( - builder: (_, validationState)=> BlocConsumer( + Widget build(BuildContext context)=> BlocBuilder( + builder: (_, validationState)=> BlocConsumer( listener: (__, registerState) { if (registerState.status == RegisterStatus.success) { _showPositiveToast(); @@ -33,11 +39,11 @@ class RegisterButton extends StatelessWidget { ); } : null, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Text( - 'Register', - style: TextStyle( + text, + style: const TextStyle( fontSize: 16, ), ), diff --git a/lib/features/authorization/presentation/widgets/username_field.dart b/lib/features/authorization/presentation/widgets/username_field.dart index 6d905a6..a317113 100644 --- a/lib/features/authorization/presentation/widgets/username_field.dart +++ b/lib/features/authorization/presentation/widgets/username_field.dart @@ -9,7 +9,7 @@ class UsernameTextField extends StatelessWidget { final TextEditingController controller; @override - Widget build(BuildContext context) => BlocBuilder( + Widget build(BuildContext context) => BlocBuilder( builder: (context, state)=> SizedBox( width: MediaQuery.of(context).size.width/1.5, child: TextField( From 9860b7112acc2dbfa51699eb39f074b7a141871f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:42:09 +0200 Subject: [PATCH 095/475] Rename files --- lib/core/dependency_injection.dart | 2 +- lib/features/application/poetlum_app.dart | 2 +- .../register_cubit.dart => authorization/auth_cubit.dart} | 2 +- .../register_state.dart => authorization/auth_state.dart} | 0 .../presentation/pages/registration/registration_page.dart | 4 ++-- .../authorization/presentation/widgets/register_button.dart | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) rename lib/features/authorization/presentation/bloc/{registation/register_cubit.dart => authorization/auth_cubit.dart} (97%) rename lib/features/authorization/presentation/bloc/{registation/register_state.dart => authorization/auth_state.dart} (100%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 8747e9a..41766fe 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -4,7 +4,7 @@ import 'package:poetlum/features/authorization/data/data_sources/remote/firebase import 'package:poetlum/features/authorization/data/repository/firebase_repository_impl.dart'; import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 30829f8..42483a9 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -4,7 +4,7 @@ import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; diff --git a/lib/features/authorization/presentation/bloc/registation/register_cubit.dart b/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart similarity index 97% rename from lib/features/authorization/presentation/bloc/registation/register_cubit.dart rename to lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart index ae6d0af..5a13abe 100644 --- a/lib/features/authorization/presentation/bloc/registation/register_cubit.dart +++ b/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart @@ -1,7 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/authorization/domain/usecases/register_user_params.dart'; import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; class RegisterCubit extends Cubit { RegisterCubit(this._registerUserUseCase) : super(const AuthState()); diff --git a/lib/features/authorization/presentation/bloc/registation/register_state.dart b/lib/features/authorization/presentation/bloc/authorization/auth_state.dart similarity index 100% rename from lib/features/authorization/presentation/bloc/registation/register_state.dart rename to lib/features/authorization/presentation/bloc/authorization/auth_state.dart diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index b9809c1..25a838f 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; diff --git a/lib/features/authorization/presentation/widgets/register_button.dart b/lib/features/authorization/presentation/widgets/register_button.dart index 744bbdf..9a396f0 100644 --- a/lib/features/authorization/presentation/widgets/register_button.dart +++ b/lib/features/authorization/presentation/widgets/register_button.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/registation/register_cubit.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/registation/register_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; class AuthButton< From 2a5d9034398327ee71f5c263651316e190f55a97 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:44:43 +0200 Subject: [PATCH 096/475] Rename cubit --- lib/core/dependency_injection.dart | 2 +- lib/features/application/poetlum_app.dart | 6 +++--- .../presentation/bloc/authorization/auth_cubit.dart | 4 ++-- .../authorization/presentation/pages/login/login_page.dart | 4 ++-- .../presentation/pages/registration/registration_page.dart | 7 ++++++- .../presentation/widgets/register_button.dart | 2 +- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 41766fe..f95274b 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -43,7 +43,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt())) - ..registerFactory(() => RegisterCubit(getIt())) + ..registerFactory(() => AuthCubit(getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 42483a9..8697ac4 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -6,7 +6,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; -import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; +import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; @@ -17,13 +17,13 @@ class PoetlumApp extends StatelessWidget { Widget build(BuildContext context) => MultiBlocProvider( providers: [ BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), - BlocProvider(create:(context) => getIt(),), + BlocProvider(create:(context) => getIt(),), BlocProvider(create:(context) => getIt()), ], child: GetMaterialApp( title: 'Poetlum', theme: theme(), - home: RegistrationPage(), + home: LoginPage(), ), ); } diff --git a/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart b/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart index 5a13abe..7995b1a 100644 --- a/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart +++ b/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart @@ -3,8 +3,8 @@ import 'package:poetlum/features/authorization/domain/usecases/register_user_par import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; -class RegisterCubit extends Cubit { - RegisterCubit(this._registerUserUseCase) : super(const AuthState()); +class AuthCubit extends Cubit { + AuthCubit(this._registerUserUseCase) : super(const AuthState()); final RegisterUserUseCase _registerUserUseCase; diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 647e5b2..a695de0 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -//import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; class LoginPage extends StatelessWidget { LoginPage({super.key}); @@ -32,7 +32,7 @@ class LoginPage extends StatelessWidget { PasswordTextField(controller: _passwordController), const Spacer(), - //const AuthButton(text: 'Login',), + const AuthButton(text: 'Login',), const Spacer(), Row( diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 25a838f..270d8a6 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -57,7 +57,12 @@ class RegistrationPage extends StatelessWidget { PasswordTextField(controller: _passwordController), const Spacer(flex: 2,), - const AuthButton(text: 'Register'), + const AuthButton< + AuthCubit, + AuthState, + RegisterFormValidationCubit, + FormValidationState + >(text: 'Register'), const Spacer(), Row( diff --git a/lib/features/authorization/presentation/widgets/register_button.dart b/lib/features/authorization/presentation/widgets/register_button.dart index 9a396f0..413a23a 100644 --- a/lib/features/authorization/presentation/widgets/register_button.dart +++ b/lib/features/authorization/presentation/widgets/register_button.dart @@ -32,7 +32,7 @@ class AuthButton< : FilledButton.tonal( onPressed: validationState.isFormValid ? () { - context.read().register( + context.read().register( username: validationState.usernameValidationState.value, email: validationState.emailValidationState.value, password: validationState.passwordValidationState.value, From 7d16446e6b9acf29393c409a86c0c2878694f601 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:40:35 +0200 Subject: [PATCH 097/475] Code refactor and login implementation --- lib/core/dependency_injection.dart | 10 +- lib/features/application/poetlum_app.dart | 1 + .../data_sources/remote/firebase_service.dart | 9 ++ .../repository/firebase_repository_impl.dart | 5 + .../repository/firebase_repository.dart | 1 + .../usecases/login/login_user_params.dart | 9 ++ .../usecases/login/login_user_usecase.dart | 16 +++ .../{ => register}/register_user_params.dart | 0 .../{ => register}/register_user_usecase.dart | 2 +- .../bloc/authorization/auth_cubit.dart | 35 ++++-- .../bloc/authorization/auth_state.dart | 8 +- .../bloc/validation/validation_cubit.dart | 10 +- .../bloc/validation/validation_state.dart | 55 ++++++++- .../presentation/pages/login/login_page.dart | 110 +++++++++++------ .../pages/registration/registration_page.dart | 111 ++++++++++-------- .../presentation/widgets/email_field.dart | 5 +- .../presentation/widgets/password_field.dart | 9 +- .../presentation/widgets/register_button.dart | 34 +++--- .../presentation/widgets/username_field.dart | 2 +- 19 files changed, 297 insertions(+), 135 deletions(-) create mode 100644 lib/features/authorization/domain/usecases/login/login_user_params.dart create mode 100644 lib/features/authorization/domain/usecases/login/login_user_usecase.dart rename lib/features/authorization/domain/usecases/{ => register}/register_user_params.dart (100%) rename lib/features/authorization/domain/usecases/{ => register}/register_user_usecase.dart (94%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index f95274b..09a1e63 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -3,7 +3,8 @@ import 'package:get_it/get_it.dart'; import 'package:poetlum/features/authorization/data/data_sources/remote/firebase_service.dart'; import 'package:poetlum/features/authorization/data/repository/firebase_repository_impl.dart'; import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; -import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; +import 'package:poetlum/features/authorization/domain/usecases/login/login_user_usecase.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register/register_user_usecase.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; @@ -35,6 +36,7 @@ void initializeDependencies() { // Usecase ..registerSingleton(GetPoemsUseCase(getIt())) ..registerSingleton(RegisterUserUseCase(getIt())) + ..registerSingleton(LoginUserUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -43,10 +45,14 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt())) - ..registerFactory(() => AuthCubit(getIt())) + ..registerFactory(() => AuthCubit(getIt(), getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), passwordValidator: getIt(), + ),) + ..registerFactory(() => LoginFormValidationCubit( + emailValidator: getIt(), + passwordValidator: getIt() ),); } diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 8697ac4..2cbd13a 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -19,6 +19,7 @@ class PoetlumApp extends StatelessWidget { BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), BlocProvider(create:(context) => getIt(),), BlocProvider(create:(context) => getIt()), + BlocProvider(create:(context) => getIt()), ], child: GetMaterialApp( title: 'Poetlum', diff --git a/lib/features/authorization/data/data_sources/remote/firebase_service.dart b/lib/features/authorization/data/data_sources/remote/firebase_service.dart index 7040ad1..cbffd8c 100644 --- a/lib/features/authorization/data/data_sources/remote/firebase_service.dart +++ b/lib/features/authorization/data/data_sources/remote/firebase_service.dart @@ -2,6 +2,7 @@ import 'package:firebase_auth/firebase_auth.dart'; abstract class FirebaseService{ Future registerUser({required String username, required String email, required String password}); + Future loginUser({required String email, required String password}); } class FirebaseServiceImpl implements FirebaseService{ @@ -13,4 +14,12 @@ class FirebaseServiceImpl implements FirebaseService{ ); await FirebaseAuth.instance.currentUser!.updateDisplayName(username); } + + @override + Future loginUser({required String email, required String password}) async{ + await FirebaseAuth.instance.signInWithEmailAndPassword( + email: email, + password: password, + ); + } } diff --git a/lib/features/authorization/data/repository/firebase_repository_impl.dart b/lib/features/authorization/data/repository/firebase_repository_impl.dart index c2f9f8d..52310ed 100644 --- a/lib/features/authorization/data/repository/firebase_repository_impl.dart +++ b/lib/features/authorization/data/repository/firebase_repository_impl.dart @@ -10,4 +10,9 @@ class FirebaseRepositoryImpl implements FirebaseRepository{ Future registerUser({required String username, required String email, required String password}) async{ await _firebaseService.registerUser(username: username, email: email, password: password); } + + @override + Future loginUser({required String email, required String password}) async{ + await _firebaseService.loginUser(email: email, password: password); + } } diff --git a/lib/features/authorization/domain/repository/firebase_repository.dart b/lib/features/authorization/domain/repository/firebase_repository.dart index 302a960..84a0c57 100644 --- a/lib/features/authorization/domain/repository/firebase_repository.dart +++ b/lib/features/authorization/domain/repository/firebase_repository.dart @@ -1,3 +1,4 @@ abstract class FirebaseRepository{ Future registerUser({required String username, required String email, required String password}); + Future loginUser({required String email, required String password}); } diff --git a/lib/features/authorization/domain/usecases/login/login_user_params.dart b/lib/features/authorization/domain/usecases/login/login_user_params.dart new file mode 100644 index 0000000..56d4201 --- /dev/null +++ b/lib/features/authorization/domain/usecases/login/login_user_params.dart @@ -0,0 +1,9 @@ +class LoginUserParams { + const LoginUserParams({ + required this.email, + required this.password, + }); + + final String email; + final String password; +} diff --git a/lib/features/authorization/domain/usecases/login/login_user_usecase.dart b/lib/features/authorization/domain/usecases/login/login_user_usecase.dart new file mode 100644 index 0000000..27a8a19 --- /dev/null +++ b/lib/features/authorization/domain/usecases/login/login_user_usecase.dart @@ -0,0 +1,16 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; +import 'package:poetlum/features/authorization/domain/usecases/login/login_user_params.dart'; + +class LoginUserUseCase implements UseCase{ + LoginUserUseCase(this._firebaseRepository); + + final FirebaseRepository _firebaseRepository; + + @override + Future call({LoginUserParams? params}) async{ + if(params != null){ + await _firebaseRepository.loginUser(email: params.email, password: params.password); + } + } +} diff --git a/lib/features/authorization/domain/usecases/register_user_params.dart b/lib/features/authorization/domain/usecases/register/register_user_params.dart similarity index 100% rename from lib/features/authorization/domain/usecases/register_user_params.dart rename to lib/features/authorization/domain/usecases/register/register_user_params.dart diff --git a/lib/features/authorization/domain/usecases/register_user_usecase.dart b/lib/features/authorization/domain/usecases/register/register_user_usecase.dart similarity index 94% rename from lib/features/authorization/domain/usecases/register_user_usecase.dart rename to lib/features/authorization/domain/usecases/register/register_user_usecase.dart index 007374c..8292ded 100644 --- a/lib/features/authorization/domain/usecases/register_user_usecase.dart +++ b/lib/features/authorization/domain/usecases/register/register_user_usecase.dart @@ -1,6 +1,6 @@ import 'package:poetlum/core/usecases/usecase.dart'; import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; -import 'package:poetlum/features/authorization/domain/usecases/register_user_params.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register/register_user_params.dart'; class RegisterUserUseCase implements UseCase{ RegisterUserUseCase(this._firebaseRepository); diff --git a/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart b/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart index 7995b1a..0974c12 100644 --- a/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart +++ b/lib/features/authorization/presentation/bloc/authorization/auth_cubit.dart @@ -1,28 +1,49 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/authorization/domain/usecases/register_user_params.dart'; -import 'package:poetlum/features/authorization/domain/usecases/register_user_usecase.dart'; +import 'package:poetlum/features/authorization/domain/usecases/login/login_user_params.dart'; +import 'package:poetlum/features/authorization/domain/usecases/login/login_user_usecase.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register/register_user_params.dart'; +import 'package:poetlum/features/authorization/domain/usecases/register/register_user_usecase.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; class AuthCubit extends Cubit { - AuthCubit(this._registerUserUseCase) : super(const AuthState()); + AuthCubit(this._registerUserUseCase, this._loginUserUseCase) : super(const AuthState()); final RegisterUserUseCase _registerUserUseCase; + final LoginUserUseCase _loginUserUseCase; + + Future login({required String email, required String password}) async{ + if (email.isEmpty || password.isEmpty) { + emit(state.copyWith(status: AuthStatus.error, errorMessage: 'Please fill in all required fields')); + return; + } + + emit(state.copyWith(status: AuthStatus.submitting)); + + try { + await _loginUserUseCase( + params: LoginUserParams(email: email, password: password), + ); + emit(state.copyWith(status: AuthStatus.success)); + } catch (e) { + emit(state.copyWith(status: AuthStatus.error, errorMessage: e.toString())); + } + } Future register({required String username, required String email, required String password}) async { if (username.isEmpty || email.isEmpty || password.isEmpty) { - emit(state.copyWith(status: RegisterStatus.error, errorMessage: 'Please fill in all required fields')); + emit(state.copyWith(status: AuthStatus.error, errorMessage: 'Please fill in all required fields')); return; } - emit(state.copyWith(status: RegisterStatus.submitting)); + emit(state.copyWith(status: AuthStatus.submitting)); try { await _registerUserUseCase( params: RegisterUserParams(username: username, email: email, password: password), ); - emit(state.copyWith(status: RegisterStatus.success)); + emit(state.copyWith(status: AuthStatus.success)); } catch (e) { - emit(state.copyWith(status: RegisterStatus.error, errorMessage: e.toString())); + emit(state.copyWith(status: AuthStatus.error, errorMessage: e.toString())); } } } diff --git a/lib/features/authorization/presentation/bloc/authorization/auth_state.dart b/lib/features/authorization/presentation/bloc/authorization/auth_state.dart index 3013f1f..5956672 100644 --- a/lib/features/authorization/presentation/bloc/authorization/auth_state.dart +++ b/lib/features/authorization/presentation/bloc/authorization/auth_state.dart @@ -1,18 +1,18 @@ import 'package:equatable/equatable.dart'; -enum RegisterStatus { initial, submitting, success, error } +enum AuthStatus { initial, submitting, success, error } class AuthState extends Equatable { const AuthState({ - this.status = RegisterStatus.initial, + this.status = AuthStatus.initial, this.errorMessage, }); - final RegisterStatus status; + final AuthStatus status; final String? errorMessage; AuthState copyWith({ - RegisterStatus? status, + AuthStatus? status, String? errorMessage, }) => AuthState( status: status ?? this.status, diff --git a/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart b/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart index 95d39aa..2073650 100644 --- a/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart +++ b/lib/features/authorization/presentation/bloc/validation/validation_cubit.dart @@ -4,13 +4,13 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali abstract class FormValidationCubit{} -class RegisterFormValidationCubit extends Cubit implements FormValidationCubit{ +class RegisterFormValidationCubit extends Cubit implements FormValidationCubit{ RegisterFormValidationCubit({ required this.usernameValidator, required this.emailValidator, required this.passwordValidator, }) - : super(FormValidationState()); + : super(RegisterFormValidationState()); final Validator usernameValidator; final Validator emailValidator; @@ -50,12 +50,12 @@ class RegisterFormValidationCubit extends Cubit implements } } -class LoginValidationCubit extends Cubit implements FormValidationCubit{ - LoginValidationCubit({ +class LoginFormValidationCubit extends Cubit implements FormValidationCubit{ + LoginFormValidationCubit({ required this.emailValidator, required this.passwordValidator, }) - : super(FormValidationState()); + : super(LoginFormValidationState()); final Validator emailValidator; final Validator passwordValidator; diff --git a/lib/features/authorization/presentation/bloc/validation/validation_state.dart b/lib/features/authorization/presentation/bloc/validation/validation_state.dart index e7590ab..659a57d 100644 --- a/lib/features/authorization/presentation/bloc/validation/validation_state.dart +++ b/lib/features/authorization/presentation/bloc/validation/validation_state.dart @@ -2,8 +2,49 @@ import 'package:equatable/equatable.dart'; -class FormValidationState extends Equatable { - FormValidationState({ +abstract class AuthFormValidationState{ + final ValidationState emailValidationState = const ValidationState(); + final ValidationState passwordValidationState = const ValidationState(); + final bool isFormValid = false; +} + +class LoginFormValidationState extends Equatable implements AuthFormValidationState{ + LoginFormValidationState({ + this.emailValidationState = const ValidationState(), + this.passwordValidationState = const ValidationState(), + bool? isFormValid, + }) : isFormValid = isFormValid ?? + emailValidationState.isValid && + passwordValidationState.isValid; + @override + final ValidationState emailValidationState; + @override + final ValidationState passwordValidationState; + @override + final bool isFormValid; + + LoginFormValidationState copyWith({ + ValidationState? emailValidationState, + ValidationState? passwordValidationState, + bool? isFormValid, + }) => LoginFormValidationState( + emailValidationState: emailValidationState ?? this.emailValidationState, + passwordValidationState: passwordValidationState ?? this.passwordValidationState, + isFormValid: isFormValid ?? + (emailValidationState ?? this.emailValidationState).isValid && + (passwordValidationState ?? this.passwordValidationState).isValid, + ); + + @override + List get props => [ + emailValidationState, + passwordValidationState, + isFormValid, + ]; +} + +class RegisterFormValidationState extends Equatable implements AuthFormValidationState{ + RegisterFormValidationState({ this.emailValidationState = const ValidationState(), this.usernameValidationState = const ValidationState(), this.passwordValidationState = const ValidationState(), @@ -12,18 +53,20 @@ class FormValidationState extends Equatable { emailValidationState.isValid && usernameValidationState.isValid && passwordValidationState.isValid; - - final ValidationState emailValidationState; final ValidationState usernameValidationState; + @override + final ValidationState emailValidationState; + @override final ValidationState passwordValidationState; + @override final bool isFormValid; - FormValidationState copyWith({ + RegisterFormValidationState copyWith({ ValidationState? emailValidationState, ValidationState? usernameValidationState, ValidationState? passwordValidationState, bool? isFormValid, - }) => FormValidationState( + }) => RegisterFormValidationState( emailValidationState: emailValidationState ?? this.emailValidationState, usernameValidationState: usernameValidationState ?? this.usernameValidationState, passwordValidationState: passwordValidationState ?? this.passwordValidationState, diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index a695de0..d3d47f9 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,52 +1,92 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; -class LoginPage extends StatelessWidget { - LoginPage({super.key}); +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); + @override + void initState() { + super.initState(); + final formCubit = context.read(); + + _emailController.addListener(() { + formCubit.emailChanged(_emailController.text); + }); + + _passwordController.addListener(() { + formCubit.passwordChanged(_passwordController.text); + }); + } + @override Widget build(BuildContext context) => Scaffold( body: SafeArea( - child: Row( - children: [ - Expanded( - child: Column( - children: [ - const Spacer(), - const Text( - 'Login', - style: TextStyle( - fontSize: 22, - ), - ), - const Spacer(flex: 2,), - - EmailTextField(controller: _emailController), - const Spacer(), - - PasswordTextField(controller: _passwordController), - const Spacer(), - - const AuthButton(text: 'Login',), - const Spacer(), - - Row( - mainAxisAlignment: MainAxisAlignment.center, + child: BlocBuilder( + builder: (context, state) => Row( + children: [ + Expanded( + child: Column( children: [ - const Text("Don't have an account?"), - TextButton(onPressed: (){}, child: const Text('Register', style: TextStyle(decoration: TextDecoration.underline),),), + const Spacer(), + const Text( + 'Login', + style: TextStyle( + fontSize: 22, + ), + ), + const Spacer(flex: 2,), + + EmailTextField(controller: _emailController), + const Spacer(), + + PasswordTextField(controller: _passwordController), + const Spacer(), + + + AuthButton< + AuthCubit, + AuthState, + LoginFormValidationCubit, + LoginFormValidationState + >( + isEnabled: state.isFormValid, + text: 'Login', + successfulToastText: 'Your login was successful', + onPressed: () => context.read().login( + email: state.emailValidationState.value, + password: state.passwordValidationState.value, + ), + ), + const Spacer(), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Don't have an account?"), + TextButton(onPressed: (){}, child: const Text('Register', style: TextStyle(decoration: TextDecoration.underline),),), + ], + ), + const Spacer(flex: 12,), ], - ), - const Spacer(flex: 12,), - ], - ), - ) - ], + ), + ) + ], + ), ), ), ); diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 270d8a6..9118179 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -9,15 +9,22 @@ import 'package:poetlum/features/authorization/presentation/widgets/password_fie import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; -class RegistrationPage extends StatelessWidget { - RegistrationPage({super.key}); +class RegistrationPage extends StatefulWidget { + const RegistrationPage({super.key}); + @override + State createState() => _RegistrationPageState(); +} + +class _RegistrationPageState extends State { final TextEditingController _usernameController = TextEditingController(); final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); @override - Widget build(BuildContext context) { + void initState() { + super.initState(); + final formCubit = context.read(); _usernameController.addListener(() { @@ -31,54 +38,66 @@ class RegistrationPage extends StatelessWidget { _passwordController.addListener(() { formCubit.passwordChanged(_passwordController.text); }); + } - return Scaffold( + @override + Widget build(BuildContext context) => Scaffold( body: SafeArea( - child: Row( - children: [ - Expanded( - child: Column( - children: [ - const Spacer(), - const Text( - 'Registration', - style: TextStyle( - fontSize: 22, + child: BlocBuilder( + builder: (context, state) => Row( + children: [ + Expanded( + child: Column( + children: [ + const Spacer(), + const Text( + 'Registration', + style: TextStyle( + fontSize: 22, + ), + ), + const Spacer(flex: 2,), + + UsernameTextField(controller: _usernameController), + const Spacer(), + + EmailTextField(controller: _emailController), + const Spacer(), + + PasswordTextField(controller: _passwordController), + const Spacer(flex: 2,), + + AuthButton< + AuthCubit, + AuthState, + RegisterFormValidationCubit, + RegisterFormValidationState + >( + isEnabled: state.isFormValid, + text: 'Register', + successfulToastText: 'Your registration was successful', + onPressed: () => context.read().register( + username: state.usernameValidationState.value, + email: state.emailValidationState.value, + password: state.passwordValidationState.value, + ), ), - ), - const Spacer(flex: 2,), - - UsernameTextField(controller: _usernameController), - const Spacer(), - - EmailTextField(controller: _emailController), - const Spacer(), - - PasswordTextField(controller: _passwordController), - const Spacer(flex: 2,), - - const AuthButton< - AuthCubit, - AuthState, - RegisterFormValidationCubit, - FormValidationState - >(text: 'Register'), - const Spacer(), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Already have an account?'), - TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), - ], - ), - const Spacer(flex: 12,), - ], + const Spacer(), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Already have an account?'), + TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), + ], + ), + const Spacer(flex: 12,), + ], + ), ), - ), - ], + ], + ), ), ), ); - } } diff --git a/lib/features/authorization/presentation/widgets/email_field.dart b/lib/features/authorization/presentation/widgets/email_field.dart index e9b5a20..7d71760 100644 --- a/lib/features/authorization/presentation/widgets/email_field.dart +++ b/lib/features/authorization/presentation/widgets/email_field.dart @@ -1,15 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; -class EmailTextField extends StatelessWidget { +class EmailTextField, ValidationState extends AuthFormValidationState> extends StatelessWidget { const EmailTextField({super.key, required this.controller}); final TextEditingController controller; @override - Widget build(BuildContext context) => BlocBuilder( + Widget build(BuildContext context) => BlocBuilder( builder: (context, state)=> SizedBox( width: MediaQuery.of(context).size.width/1.5, child: TextField( diff --git a/lib/features/authorization/presentation/widgets/password_field.dart b/lib/features/authorization/presentation/widgets/password_field.dart index 891a502..b4a9871 100644 --- a/lib/features/authorization/presentation/widgets/password_field.dart +++ b/lib/features/authorization/presentation/widgets/password_field.dart @@ -1,22 +1,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; -class PasswordTextField extends StatefulWidget { +class PasswordTextField, ValidationState extends AuthFormValidationState> extends StatefulWidget { const PasswordTextField({super.key, required this.controller}); final TextEditingController controller; @override - State createState() => _PasswordTextFieldState(); + State createState() => _PasswordTextFieldState(); } -class _PasswordTextFieldState extends State { +class _PasswordTextFieldState, ValidationState extends AuthFormValidationState> extends State { bool _isPasswordVisible = false; @override - Widget build(BuildContext context) => BlocBuilder( + Widget build(BuildContext context) => BlocBuilder( builder: (context, state) => SizedBox( width: MediaQuery.of(context).size.width/1.5, child: TextField( diff --git a/lib/features/authorization/presentation/widgets/register_button.dart b/lib/features/authorization/presentation/widgets/register_button.dart index 413a23a..d143505 100644 --- a/lib/features/authorization/presentation/widgets/register_button.dart +++ b/lib/features/authorization/presentation/widgets/register_button.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; @@ -9,35 +8,31 @@ class AuthButton< InputCubit extends Cubit, InputState extends AuthState, FormCubit extends Cubit, - FormState extends FormValidationState + FormState extends AuthFormValidationState > extends StatelessWidget { const AuthButton({ - super.key, required this.text, + super.key, required this.text, required this.onPressed, required this.isEnabled, required this.successfulToastText, }); final String text; + final void Function() onPressed; + final bool isEnabled; + final String successfulToastText; @override - Widget build(BuildContext context)=> BlocBuilder( - builder: (_, validationState)=> BlocConsumer( + Widget build(BuildContext context)=> BlocConsumer( listener: (__, registerState) { - if (registerState.status == RegisterStatus.success) { - _showPositiveToast(); - } else if (registerState.status == RegisterStatus.error) { + if (registerState.status == AuthStatus.success) { + _showPositiveToast(successfulToastText); + } else if (registerState.status == AuthStatus.error) { _showNegativeToast(registerState.errorMessage ?? 'Unknown error'); } }, - builder: (context, state) => state.status == RegisterStatus.submitting + builder: (context, state) => state.status == AuthStatus.submitting ? const CircularProgressIndicator() : FilledButton.tonal( - onPressed: validationState.isFormValid - ? () { - context.read().register( - username: validationState.usernameValidationState.value, - email: validationState.emailValidationState.value, - password: validationState.passwordValidationState.value, - ); - } + onPressed: isEnabled + ? onPressed : null, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), @@ -49,12 +44,11 @@ class AuthButton< ), ), ), - ), ); - Future _showPositiveToast() async{ + Future _showPositiveToast(String text) async{ await Fluttertoast.showToast( - msg: 'Your registration was successful', + msg: text, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, backgroundColor: Colors.green, diff --git a/lib/features/authorization/presentation/widgets/username_field.dart b/lib/features/authorization/presentation/widgets/username_field.dart index a317113..6d41bf4 100644 --- a/lib/features/authorization/presentation/widgets/username_field.dart +++ b/lib/features/authorization/presentation/widgets/username_field.dart @@ -9,7 +9,7 @@ class UsernameTextField extends StatelessWidget { final TextEditingController controller; @override - Widget build(BuildContext context) => BlocBuilder( + Widget build(BuildContext context) => BlocBuilder( builder: (context, state)=> SizedBox( width: MediaQuery.of(context).size.width/1.5, child: TextField( From a329ede062e6e9ffcc1dce4fe9163e8787d5a56e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 16 Nov 2023 18:48:34 +0200 Subject: [PATCH 098/475] Rework registration page --- .../pages/registration/registration_page.dart | 138 +++++++++++------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 9118179..b549c83 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -9,6 +9,86 @@ import 'package:poetlum/features/authorization/presentation/widgets/password_fie import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; +class _Header extends StatelessWidget { + const _Header(); + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/10, + child: const Column( + children: [ + Spacer(), + Text( + 'Registration', + style: TextStyle( + fontSize: 22, + ), + ), + Spacer(), + ], + ), + ); +} + +class _Form extends StatelessWidget { + const _Form(this.usernameController, this.emailController, this.passwordController); + + final TextEditingController usernameController; + final TextEditingController emailController; + final TextEditingController passwordController; + + @override + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) => SizedBox( + height: MediaQuery.of(context).size.height/2.75, + child: Column( + children: [ + UsernameTextField(controller: usernameController), + const Spacer(), + + EmailTextField(controller: emailController), + const Spacer(), + + PasswordTextField(controller: passwordController), + const Spacer(), + + AuthButton< + AuthCubit, + AuthState, + RegisterFormValidationCubit, + RegisterFormValidationState + >( + isEnabled: state.isFormValid, + text: 'Register', + successfulToastText: 'Your registration was successful', + onPressed: () => context.read().register( + username: state.usernameValidationState.value, + email: state.emailValidationState.value, + password: state.passwordValidationState.value, + ), + ), + ], + ), + ), + ); +} + +class _Footer extends StatelessWidget { + const _Footer(); + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/10, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Already have an account?'), + TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), + ], + ), + ); +} + class RegistrationPage extends StatefulWidget { const RegistrationPage({super.key}); @@ -44,57 +124,17 @@ class _RegistrationPageState extends State { Widget build(BuildContext context) => Scaffold( body: SafeArea( child: BlocBuilder( - builder: (context, state) => Row( + builder: (context, state) => Column( children: [ - Expanded( - child: Column( - children: [ - const Spacer(), - const Text( - 'Registration', - style: TextStyle( - fontSize: 22, - ), - ), - const Spacer(flex: 2,), - - UsernameTextField(controller: _usernameController), - const Spacer(), - - EmailTextField(controller: _emailController), - const Spacer(), - - PasswordTextField(controller: _passwordController), - const Spacer(flex: 2,), - - AuthButton< - AuthCubit, - AuthState, - RegisterFormValidationCubit, - RegisterFormValidationState - >( - isEnabled: state.isFormValid, - text: 'Register', - successfulToastText: 'Your registration was successful', - onPressed: () => context.read().register( - username: state.usernameValidationState.value, - email: state.emailValidationState.value, - password: state.passwordValidationState.value, - ), - ), - const Spacer(), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Already have an account?'), - TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), - ], - ), - const Spacer(flex: 12,), - ], - ), + const _Header(), + + _Form( + _usernameController, + _emailController, + _passwordController, ), + + const _Footer(), ], ), ), From e57c80b076d578952876cfbf48625e48cec1b8a1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 16 Nov 2023 19:58:54 +0200 Subject: [PATCH 099/475] Indentation fix --- .../pages/registration/registration_page.dart | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index b549c83..e8d7d73 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -44,29 +44,29 @@ class _Form extends StatelessWidget { child: Column( children: [ UsernameTextField(controller: usernameController), - const Spacer(), - - EmailTextField(controller: emailController), - const Spacer(), - - PasswordTextField(controller: passwordController), - const Spacer(), - - AuthButton< - AuthCubit, - AuthState, - RegisterFormValidationCubit, - RegisterFormValidationState - >( - isEnabled: state.isFormValid, - text: 'Register', - successfulToastText: 'Your registration was successful', - onPressed: () => context.read().register( - username: state.usernameValidationState.value, - email: state.emailValidationState.value, - password: state.passwordValidationState.value, - ), + const Spacer(), + + EmailTextField(controller: emailController), + const Spacer(), + + PasswordTextField(controller: passwordController), + const Spacer(), + + AuthButton< + AuthCubit, + AuthState, + RegisterFormValidationCubit, + RegisterFormValidationState + >( + isEnabled: state.isFormValid, + text: 'Register', + successfulToastText: 'Your registration was successful', + onPressed: () => context.read().register( + username: state.usernameValidationState.value, + email: state.emailValidationState.value, + password: state.passwordValidationState.value, ), + ), ], ), ), @@ -122,22 +122,22 @@ class _RegistrationPageState extends State { @override Widget build(BuildContext context) => Scaffold( - body: SafeArea( - child: BlocBuilder( - builder: (context, state) => Column( - children: [ - const _Header(), - - _Form( - _usernameController, - _emailController, - _passwordController, - ), - - const _Footer(), - ], - ), + body: SafeArea( + child: BlocBuilder( + builder: (context, state) => Column( + children: [ + const _Header(), + + _Form( + _usernameController, + _emailController, + _passwordController, + ), + + const _Footer(), + ], ), ), - ); + ), + ); } From e98564b3b531ab69a1073ff15d338f25b5bb2098 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 16 Nov 2023 19:59:01 +0200 Subject: [PATCH 100/475] Login page rework --- .../presentation/pages/login/login_page.dart | 133 +++++++++++------- 1 file changed, 85 insertions(+), 48 deletions(-) diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index d3d47f9..8767d67 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -8,6 +8,81 @@ import 'package:poetlum/features/authorization/presentation/widgets/email_field. import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; +class _Header extends StatelessWidget { + const _Header(); + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/10, + child: const Column( + children: [ + Spacer(), + Text( + 'Login', + style: TextStyle( + fontSize: 22, + ), + ), + Spacer(), + ], + ), + ); +} + +class _Form extends StatelessWidget { + const _Form(this.emailController, this.passwordController); + + final TextEditingController emailController; + final TextEditingController passwordController; + + @override + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) => SizedBox( + height: MediaQuery.of(context).size.height/3.5, + child: Column( + children: [ + EmailTextField(controller: emailController), + const Spacer(), + + PasswordTextField(controller: passwordController), + const Spacer(), + + AuthButton< + AuthCubit, + AuthState, + LoginFormValidationCubit, + LoginFormValidationState + >( + isEnabled: state.isFormValid, + text: 'Login', + successfulToastText: 'Your login was successful', + onPressed: () => context.read().login( + email: state.emailValidationState.value, + password: state.passwordValidationState.value, + ), + ), + ], + ), + ), + ); +} + +class _Footer extends StatelessWidget { + const _Footer(); + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/10, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Don't have an account?"), + TextButton(onPressed: (){}, child: const Text('Register', style: TextStyle(decoration: TextDecoration.underline),),), + ], + ), + ); +} + class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -37,56 +112,18 @@ class _LoginPageState extends State { Widget build(BuildContext context) => Scaffold( body: SafeArea( child: BlocBuilder( - builder: (context, state) => Row( + builder: (context, state) => Column( children: [ - Expanded( - child: Column( - children: [ - const Spacer(), - const Text( - 'Login', - style: TextStyle( - fontSize: 22, - ), - ), - const Spacer(flex: 2,), - - EmailTextField(controller: _emailController), - const Spacer(), - - PasswordTextField(controller: _passwordController), - const Spacer(), - - - AuthButton< - AuthCubit, - AuthState, - LoginFormValidationCubit, - LoginFormValidationState - >( - isEnabled: state.isFormValid, - text: 'Login', - successfulToastText: 'Your login was successful', - onPressed: () => context.read().login( - email: state.emailValidationState.value, - password: state.passwordValidationState.value, - ), - ), - const Spacer(), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text("Don't have an account?"), - TextButton(onPressed: (){}, child: const Text('Register', style: TextStyle(decoration: TextDecoration.underline),),), - ], - ), - const Spacer(flex: 12,), - ], - ), - ) + const _Header(), + + _Form( + _emailController, + _passwordController, + ), + + const _Footer(), ], - ), + ), ), ), ); From 7ccfd90820a0cb25d47d1623a19a889eb94522d2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:07:33 +0200 Subject: [PATCH 101/475] Create InitBlocs widget --- lib/features/application/poetlum_app.dart | 15 ++---------- .../presentation/init_blocs.dart | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 lib/features/multi_bloc_provider/presentation/init_blocs.dart diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 2cbd13a..2b0873c 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,26 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; -import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; -import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; -import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; -import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @override - Widget build(BuildContext context) => MultiBlocProvider( - providers: [ - BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), - BlocProvider(create:(context) => getIt(),), - BlocProvider(create:(context) => getIt()), - BlocProvider(create:(context) => getIt()), - ], + Widget build(BuildContext context) => InitBlocs( child: GetMaterialApp( title: 'Poetlum', theme: theme(), diff --git a/lib/features/multi_bloc_provider/presentation/init_blocs.dart b/lib/features/multi_bloc_provider/presentation/init_blocs.dart new file mode 100644 index 0000000..800e2a4 --- /dev/null +++ b/lib/features/multi_bloc_provider/presentation/init_blocs.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; +import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; + +class InitBlocs extends StatelessWidget { + const InitBlocs({super.key, required this.child}); + + final Widget child; + + @override + Widget build(BuildContext context) => MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), + BlocProvider(create:(context) => getIt(),), + BlocProvider(create:(context) => getIt()), + BlocProvider(create:(context) => getIt()), + ], + child: child, + ); +} From 63b0a0538ce728ed9e8c2059e9862129ab62ba41 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 17 Nov 2023 20:27:43 +0200 Subject: [PATCH 102/475] Create AuthRepository --- .../data/repository/auth_repository_impl.dart | 7 +++++++ .../authorization/domain/repository/auth_repository.dart | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 lib/features/authorization/data/repository/auth_repository_impl.dart create mode 100644 lib/features/authorization/domain/repository/auth_repository.dart diff --git a/lib/features/authorization/data/repository/auth_repository_impl.dart b/lib/features/authorization/data/repository/auth_repository_impl.dart new file mode 100644 index 0000000..d60c3f9 --- /dev/null +++ b/lib/features/authorization/data/repository/auth_repository_impl.dart @@ -0,0 +1,7 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:poetlum/features/authorization/domain/repository/auth_repository.dart'; + +class AuthenticationRepositoryImpl implements AuthenticationRepository{ + @override + Stream get authStateChanges => FirebaseAuth.instance.authStateChanges(); +} diff --git a/lib/features/authorization/domain/repository/auth_repository.dart b/lib/features/authorization/domain/repository/auth_repository.dart new file mode 100644 index 0000000..8a1ee8d --- /dev/null +++ b/lib/features/authorization/domain/repository/auth_repository.dart @@ -0,0 +1,5 @@ +import 'package:firebase_auth/firebase_auth.dart'; + +abstract class AuthenticationRepository{ + Stream get authStateChanges; +} From 633fa08e7ec52a5045de40c458db04b21c613b6e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 17 Nov 2023 20:28:41 +0200 Subject: [PATCH 103/475] Create AuthWrapper --- .../presentation/widgets/auth_wrapper.dart | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 lib/features/application/presentation/widgets/auth_wrapper.dart diff --git a/lib/features/application/presentation/widgets/auth_wrapper.dart b/lib/features/application/presentation/widgets/auth_wrapper.dart new file mode 100644 index 0000000..930c68a --- /dev/null +++ b/lib/features/application/presentation/widgets/auth_wrapper.dart @@ -0,0 +1,34 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/authorization/domain/repository/auth_repository.dart'; +import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; +import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; + +class AuthWrapper extends StatelessWidget { + const AuthWrapper({super.key}); + + @override + Widget build(BuildContext context) { + final authService = getIt(); + + return StreamBuilder( + stream: authService.authStateChanges, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.active) { + final user = snapshot.data; + if (user == null) { + return const RegistrationPage(); + } + return const PoemsFeed(); + } + + return const Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), + ); + }, + ); + } +} From 036be59192fab239b327738b6123d753f104bbbf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 17 Nov 2023 20:28:50 +0200 Subject: [PATCH 104/475] Register stuff in the DI --- lib/core/dependency_injection.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 09a1e63..21c8977 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -1,7 +1,9 @@ import 'package:dio/dio.dart'; import 'package:get_it/get_it.dart'; import 'package:poetlum/features/authorization/data/data_sources/remote/firebase_service.dart'; +import 'package:poetlum/features/authorization/data/repository/auth_repository_impl.dart'; import 'package:poetlum/features/authorization/data/repository/firebase_repository_impl.dart'; +import 'package:poetlum/features/authorization/domain/repository/auth_repository.dart'; import 'package:poetlum/features/authorization/domain/repository/firebase_repository.dart'; import 'package:poetlum/features/authorization/domain/usecases/login/login_user_usecase.dart'; import 'package:poetlum/features/authorization/domain/usecases/register/register_user_usecase.dart'; @@ -30,6 +32,7 @@ void initializeDependencies() { ..registerSingleton(FirebaseServiceImpl()) // Repository + ..registerSingleton(AuthenticationRepositoryImpl()) ..registerSingleton(PoemRepositoryImpl(getIt())) ..registerSingleton(FirebaseRepositoryImpl(getIt())) From 9eb3c57d413dc34b2f680fee112d8c07a73fd973 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 17 Nov 2023 20:28:56 +0200 Subject: [PATCH 105/475] Add Auth resolver --- lib/features/application/poetlum_app.dart | 24 ++--------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 2b0873c..1827df9 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; -import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; +import 'package:poetlum/features/application/presentation/widgets/auth_wrapper.dart'; import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; class PoetlumApp extends StatelessWidget { @@ -13,26 +12,7 @@ class PoetlumApp extends StatelessWidget { child: GetMaterialApp( title: 'Poetlum', theme: theme(), - home: LoginPage(), + home: const AuthWrapper(), ), ); } - -class PoetlumHomePage extends StatefulWidget { - const PoetlumHomePage({super.key, required this.title}); - - final String title; - - @override - State createState() => _PoetlumHomePageState(); -} - -class _PoetlumHomePageState extends State { - @override - Widget build(BuildContext context) => Scaffold( - appBar: const CustomAppBar( - title: 'Poetlum', - ), - body: const Placeholder(), - ); -} From b7c9ef22c95caaa7aa08b5bfcceb30a4690c1e77 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:14:33 +0200 Subject: [PATCH 106/475] Create constants files --- lib/core/constants/navigator_constants.dart | 4 ++++ lib/core/constants/{constants.dart => poems_constants.dart} | 0 2 files changed, 4 insertions(+) create mode 100644 lib/core/constants/navigator_constants.dart rename lib/core/constants/{constants.dart => poems_constants.dart} (100%) diff --git a/lib/core/constants/navigator_constants.dart b/lib/core/constants/navigator_constants.dart new file mode 100644 index 0000000..f2d2e9e --- /dev/null +++ b/lib/core/constants/navigator_constants.dart @@ -0,0 +1,4 @@ +const String authWrapperPageConstant = '/auth_wrapper'; +const String registerPageConstant = '/register'; +const String loginPageConstant = '/login'; +const String poemsFeedPageConstant = '/poems_feed'; diff --git a/lib/core/constants/constants.dart b/lib/core/constants/poems_constants.dart similarity index 100% rename from lib/core/constants/constants.dart rename to lib/core/constants/poems_constants.dart From bb97768a6a046d99155363615f4ab15201d82949 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:17:10 +0200 Subject: [PATCH 107/475] Create routes --- lib/features/application/poetlum_app.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 1827df9..57a3225 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/auth_wrapper.dart'; +import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; +import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; +import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @@ -12,7 +16,13 @@ class PoetlumApp extends StatelessWidget { child: GetMaterialApp( title: 'Poetlum', theme: theme(), - home: const AuthWrapper(), + initialRoute: authWrapperPageConstant, + routes: { + authWrapperPageConstant:(_) => const AuthWrapper(), + registerPageConstant: (_) => const RegistrationPage(), + loginPageConstant: (_) => const LoginPage(), + poemsFeedPageConstant: (_) => const PoemsFeed(), + }, ), ); } From 6d195edb517653752795e9ee5af8190e2b43746c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:17:40 +0200 Subject: [PATCH 108/475] Add redirect --- .../authorization/presentation/widgets/register_button.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/features/authorization/presentation/widgets/register_button.dart b/lib/features/authorization/presentation/widgets/register_button.dart index d143505..fa1582d 100644 --- a/lib/features/authorization/presentation/widgets/register_button.dart +++ b/lib/features/authorization/presentation/widgets/register_button.dart @@ -11,11 +11,12 @@ class AuthButton< FormState extends AuthFormValidationState > extends StatelessWidget { const AuthButton({ - super.key, required this.text, required this.onPressed, required this.isEnabled, required this.successfulToastText, + super.key, required this.text, required this.onPressed, required this.isEnabled, required this.successfulToastText, required this.navigateOnSuccess, }); final String text; final void Function() onPressed; + final void Function() navigateOnSuccess; final bool isEnabled; final String successfulToastText; @@ -24,6 +25,7 @@ class AuthButton< listener: (__, registerState) { if (registerState.status == AuthStatus.success) { _showPositiveToast(successfulToastText); + navigateOnSuccess(); } else if (registerState.status == AuthStatus.error) { _showNegativeToast(registerState.errorMessage ?? 'Unknown error'); } From 2702123142b54527260dc716f854460f942f8cc8 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:18:18 +0200 Subject: [PATCH 109/475] Redirect on successful login --- .../presentation/pages/login/login_page.dart | 22 ++++++++++++++----- .../pages/registration/registration_page.dart | 12 +++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 8767d67..83b190e 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; @@ -56,10 +57,13 @@ class _Form extends StatelessWidget { isEnabled: state.isFormValid, text: 'Login', successfulToastText: 'Your login was successful', - onPressed: () => context.read().login( - email: state.emailValidationState.value, - password: state.passwordValidationState.value, - ), + onPressed: () { + context.read().login( + email: state.emailValidationState.value, + password: state.passwordValidationState.value, + ); + }, + navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, poemsFeedPageConstant, (route) => false), ), ], ), @@ -77,7 +81,15 @@ class _Footer extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("Don't have an account?"), - TextButton(onPressed: (){}, child: const Text('Register', style: TextStyle(decoration: TextDecoration.underline),),), + TextButton( + onPressed: (){ + Navigator.pushNamedAndRemoveUntil(context, registerPageConstant, (r) => false); + }, + child: const Text( + 'Register', + style: TextStyle(decoration: TextDecoration.underline), + ), + ), ], ), ); diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index e8d7d73..3f1b35a 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; @@ -66,6 +67,7 @@ class _Form extends StatelessWidget { email: state.emailValidationState.value, password: state.passwordValidationState.value, ), + navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, poemsFeedPageConstant, (route) => false), ), ], ), @@ -83,7 +85,15 @@ class _Footer extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Already have an account?'), - TextButton(onPressed: (){}, child: const Text('Login', style: TextStyle(decoration: TextDecoration.underline),),), + TextButton( + onPressed: (){ + Navigator.pushNamedAndRemoveUntil(context, loginPageConstant, (r) => false); + }, + child: const Text( + 'Login', + style: TextStyle(decoration: TextDecoration.underline), + ), + ), ], ), ); From 8eef91749a21ba4c66311d18cabbbbb4b40ce92f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:18:31 +0200 Subject: [PATCH 110/475] Change constants file --- .../poems_feed/data/data_sources/remote/poem_api_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart index bd5a3ca..65c1b2f 100644 --- a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart +++ b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart @@ -1,5 +1,5 @@ import 'package:dio/dio.dart'; -import 'package:poetlum/core/constants/constants.dart'; +import 'package:poetlum/core/constants/poems_constants.dart'; import 'package:poetlum/features/poems_feed/data/models/poem.dart'; import 'package:retrofit/retrofit.dart'; From 5dd036d20f92dfdd064b85a1d1e76a17075846c2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 16:54:00 +0200 Subject: [PATCH 111/475] Rename AuthButton --- .../authorization/presentation/pages/login/login_page.dart | 2 +- .../presentation/pages/registration/registration_page.dart | 2 +- .../widgets/{register_button.dart => auth_button.dart} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename lib/features/authorization/presentation/widgets/{register_button.dart => auth_button.dart} (100%) diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 83b190e..97cc8aa 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -7,7 +7,7 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; class _Header extends StatelessWidget { const _Header(); diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 3f1b35a..daf7685 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -7,7 +7,7 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -import 'package:poetlum/features/authorization/presentation/widgets/register_button.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; class _Header extends StatelessWidget { diff --git a/lib/features/authorization/presentation/widgets/register_button.dart b/lib/features/authorization/presentation/widgets/auth_button.dart similarity index 100% rename from lib/features/authorization/presentation/widgets/register_button.dart rename to lib/features/authorization/presentation/widgets/auth_button.dart From 3c715a870c20a63e466411aacedd28e704ba3eff Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 16:54:11 +0200 Subject: [PATCH 112/475] Create drawer template --- .../presentation/widgets/AppBar/app_bar.dart | 4 +- .../presentation/pages/home/poems_feed.dart | 161 ++++++++++++++++++ 2 files changed, 163 insertions(+), 2 deletions(-) diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index 1006db6..71dbf89 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart'; +//import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -9,7 +9,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) => AppBar( - leading: const MenuButton(), + //leading: const MenuButton(), actions: const [ SettingsButton(), ], diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 4199791..5fc0da1 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -1,10 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_card.dart'; +class CustomSpacer extends StatelessWidget { + const CustomSpacer({super.key, required this.height}); + + final double height; + + @override + Widget build(BuildContext context) => SizedBox(height: height); +} + class PoemsFeed extends StatelessWidget { const PoemsFeed({super.key}); @@ -13,6 +23,157 @@ class PoemsFeed extends StatelessWidget { appBar: const CustomAppBar( title: 'Poetlum', ), + drawer: Drawer( + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: ListView( + children: [ + CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), + + const Center( + child: Text( + 'Search settings', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold + ), + ) + ), + + CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), + + LayoutBuilder( + builder: (context, constraints) { + double width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: const TextField( + decoration: InputDecoration( + border: OutlineInputBorder(), + hintText: 'Author', + hintStyle: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + ); + }, + ), + + CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), + + LayoutBuilder( + builder: (context, constraints) { + double width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: const TextField( + decoration: InputDecoration( + border: OutlineInputBorder(), + hintText: 'Title', + hintStyle: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + ); + }, + ), + + CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), + + LayoutBuilder( + builder: (context, constraints) { + double width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: TextField( + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: 'Number of lines', + hintStyle: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + ); + }, + ), + + CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), + + LayoutBuilder( + builder: (context, constraints) { + double width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: TextField( + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: InputDecoration( + border: OutlineInputBorder(), + hintText: 'Result count', + hintStyle: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + ); + }, + ), + + CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), + + CheckboxListTile( + value: true, + onChanged: (value){}, + title: Text('Return random poems?'), + controlAffinity: ListTileControlAffinity.leading, + ), + + CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), + + LayoutBuilder( + builder: (context, constraints) { + double width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: FilledButton( + onPressed: (){}, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.5), + child: Text( + 'Search', + style: TextStyle( + fontSize: 16 + ), + ), + ) + ), + ), + ); + }, + ) + ], + ), + ), + ), body: _buildBody(), ); From 5b84ca09c740015ab87798ead37924b03842e033 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 18:05:34 +0200 Subject: [PATCH 113/475] Decompoze the Drawer --- .../presentation/pages/home/poems_feed.dart | 160 +----------------- .../widgets/drawer/custom_checkbox_tile.dart | 13 ++ .../widgets/drawer/custom_drawer.dart | 46 +++++ .../widgets/drawer/custom_header.dart | 20 +++ .../widgets/drawer/custom_search_buttond.dart | 30 ++++ .../widgets/drawer/custom_textfield.dart | 38 +++++ 6 files changed, 152 insertions(+), 155 deletions(-) create mode 100644 lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart create mode 100644 lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart create mode 100644 lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart create mode 100644 lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart create mode 100644 lib/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 5fc0da1..e5c7ba8 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -1,18 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_card.dart'; class CustomSpacer extends StatelessWidget { - const CustomSpacer({super.key, required this.height}); + const CustomSpacer({super.key, required this.heightFactor}); + final double heightFactor; - final double height; @override - Widget build(BuildContext context) => SizedBox(height: height); + Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); } class PoemsFeed extends StatelessWidget { @@ -23,157 +23,7 @@ class PoemsFeed extends StatelessWidget { appBar: const CustomAppBar( title: 'Poetlum', ), - drawer: Drawer( - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: ListView( - children: [ - CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), - - const Center( - child: Text( - 'Search settings', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold - ), - ) - ), - - CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), - - LayoutBuilder( - builder: (context, constraints) { - double width = constraints.maxWidth / 1.35; - - return Align( - child: SizedBox( - width: width, - child: const TextField( - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'Author', - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - ), - ); - }, - ), - - CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), - - LayoutBuilder( - builder: (context, constraints) { - double width = constraints.maxWidth / 1.35; - - return Align( - child: SizedBox( - width: width, - child: const TextField( - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'Title', - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - ), - ); - }, - ), - - CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), - - LayoutBuilder( - builder: (context, constraints) { - double width = constraints.maxWidth / 1.35; - - return Align( - child: SizedBox( - width: width, - child: TextField( - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: 'Number of lines', - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - ), - ); - }, - ), - - CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), - - LayoutBuilder( - builder: (context, constraints) { - double width = constraints.maxWidth / 1.35; - - return Align( - child: SizedBox( - width: width, - child: TextField( - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'Result count', - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - ), - ); - }, - ), - - CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), - - CheckboxListTile( - value: true, - onChanged: (value){}, - title: Text('Return random poems?'), - controlAffinity: ListTileControlAffinity.leading, - ), - - CustomSpacer(height: MediaQuery.of(context).size.height * 0.04), - - LayoutBuilder( - builder: (context, constraints) { - double width = constraints.maxWidth / 1.35; - - return Align( - child: SizedBox( - width: width, - child: FilledButton( - onPressed: (){}, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12.5), - child: Text( - 'Search', - style: TextStyle( - fontSize: 16 - ), - ), - ) - ), - ), - ); - }, - ) - ], - ), - ), - ), + drawer: CustomDrawer(), body: _buildBody(), ); diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart new file mode 100644 index 0000000..ad89e79 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class CustomCheckboxTile extends StatelessWidget { + const CustomCheckboxTile({super.key}); + + @override + Widget build(BuildContext context) => CheckboxListTile( + value: true, + onChanged: (value){}, + title: const Text('Return random poems?'), + controlAffinity: ListTileControlAffinity.leading, + ); +} diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart new file mode 100644 index 0000000..4710961 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; + +class CustomDrawer extends StatelessWidget { + CustomDrawer({super.key}); + + final TextEditingController _authorController = TextEditingController(); + final TextEditingController _titleController = TextEditingController(); + final TextEditingController _numberOfLinesController = TextEditingController(); + final TextEditingController _resultCountController = TextEditingController(); + + @override + Widget build(BuildContext context) => Drawer( + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: ListView( + children: [ + const CustomDrawerHeader(), + + CustomTextField(hintText: 'Author', controller: _authorController), + const CustomSpacer(heightFactor: 0.04), + + CustomTextField(hintText: 'Title', controller: _titleController), + const CustomSpacer(heightFactor: 0.04), + + CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), + const CustomSpacer(heightFactor: 0.04), + + CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), + const CustomSpacer(heightFactor: 0.04), + + const CustomCheckboxTile(), + const CustomSpacer(heightFactor: 0.04), + + CustomSearchButton( + onPressed: () {}, + ), + ], + ), + ), + ); +} diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart new file mode 100644 index 0000000..203ab38 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; + +class CustomDrawerHeader extends StatelessWidget { + const CustomDrawerHeader({super.key}); + + @override + Widget build(BuildContext context) => const Column( + children: [ + CustomSpacer(heightFactor: 0.04), + Center( + child: Text( + 'Search settings', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + ), + ), + CustomSpacer(heightFactor: 0.04), + ], + ); +} diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart new file mode 100644 index 0000000..0f8e1c2 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class CustomSearchButton extends StatelessWidget { + const CustomSearchButton({super.key, required this.onPressed}); + + final void Function() onPressed; + + @override + Widget build(BuildContext context) => LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: FilledButton( + onPressed: onPressed, + child: const Padding( + padding: EdgeInsets.symmetric(vertical: 12.5), + child: Text( + 'Search', + style: TextStyle(fontSize: 16), + ), + ), + ), + ), + ); + }, + ); +} diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart new file mode 100644 index 0000000..850af5a --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class CustomTextField extends StatelessWidget { + const CustomTextField({ + super.key, + required this.hintText, + this.isNumberInput = false, + required this.controller, + }); + + final String hintText; + final bool isNumberInput; + final TextEditingController controller; + + @override + Widget build(BuildContext context) => LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: TextField( + controller: controller, + keyboardType: isNumberInput ? TextInputType.number : TextInputType.text, + inputFormatters: isNumberInput ? [FilteringTextInputFormatter.digitsOnly] : null, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: hintText, + hintStyle: const TextStyle(color: Colors.grey), + ), + ), + ), + ); + }, + ); +} From dd7072e1701d7d0073dc3a349530eb54e756f89a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 18 Nov 2023 18:10:31 +0200 Subject: [PATCH 114/475] Delete unused button --- .../presentation/widgets/AppBar/app_bar.dart | 2 -- .../widgets/AppBar/buttons/menu_buton.dart | 29 ------------------- 2 files changed, 31 deletions(-) delete mode 100644 lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index 71dbf89..d2a08d9 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -//import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -9,7 +8,6 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) => AppBar( - //leading: const MenuButton(), actions: const [ SettingsButton(), ], diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart b/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart deleted file mode 100644 index 1d86ec2..0000000 --- a/lib/features/application/presentation/widgets/AppBar/buttons/menu_buton.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart'; - -class MenuButton extends StatefulWidget { - const MenuButton({super.key}); - - @override - State createState() => _MenuButtonState(); -} - -class _MenuButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { - @override - Widget build(BuildContext context) => RotationTransition( - turns: rotationAnimation, - child: IconButton( - onPressed: (){ - playAnimation(); - // ... - }, - icon: const Icon(Icons.menu), - ), - ); - - @override - void dispose() { - rotationController.dispose(); - super.dispose(); - } -} From 12168afcdf63af32177f5786bfa905e3b77f923e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 12:46:13 +0200 Subject: [PATCH 115/475] Add value to the checkbox --- .../widgets/drawer/custom_checkbox_tile.dart | 9 ++++++--- .../widgets/drawer/custom_drawer.dart | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart index ad89e79..961bb5d 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart @@ -1,12 +1,15 @@ import 'package:flutter/material.dart'; class CustomCheckboxTile extends StatelessWidget { - const CustomCheckboxTile({super.key}); + const CustomCheckboxTile({super.key, required this.value, required this.onChanged}); + + final bool? value; + final ValueChanged onChanged; @override Widget build(BuildContext context) => CheckboxListTile( - value: true, - onChanged: (value){}, + value: value, + onChanged: onChanged, title: const Text('Return random poems?'), controlAffinity: ListTileControlAffinity.leading, ); diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 4710961..3f94c2b 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -5,13 +5,19 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_h import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; -class CustomDrawer extends StatelessWidget { +class CustomDrawer extends StatefulWidget { CustomDrawer({super.key}); + @override + State createState() => _CustomDrawerState(); +} + +class _CustomDrawerState extends State { final TextEditingController _authorController = TextEditingController(); final TextEditingController _titleController = TextEditingController(); final TextEditingController _numberOfLinesController = TextEditingController(); final TextEditingController _resultCountController = TextEditingController(); + bool? _isRandom = false; @override Widget build(BuildContext context) => Drawer( @@ -33,14 +39,18 @@ class CustomDrawer extends StatelessWidget { CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), const CustomSpacer(heightFactor: 0.04), - const CustomCheckboxTile(), + CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), const CustomSpacer(heightFactor: 0.04), CustomSearchButton( - onPressed: () {}, + onPressed: () { + print(_isRandom); + }, ), ], ), ), ); + + void _toggleCheckbox(bool? value) => setState(() => _isRandom = value); } From f8a71a3608c91bda983b3c205af7deb489eb6222 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 13:23:11 +0200 Subject: [PATCH 116/475] Rename getPoems to getInitialPoems --- lib/core/dependency_injection.dart | 2 +- .../multi_bloc_provider/presentation/init_blocs.dart | 2 +- .../data/data_sources/remote/poem_api_service.dart | 2 +- .../data/repository/poem_repository_impl.dart | 4 ++-- .../poems_feed/domain/repository/poem_repository.dart | 2 +- .../poems_feed/domain/usecases/get_poems_usecase.dart | 6 +++--- .../bloc/poem/remote/remote_poem_bloc.dart | 10 +++++----- .../bloc/poem/remote/remote_poem_event.dart | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 21c8977..1b92577 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -37,7 +37,7 @@ void initializeDependencies() { ..registerSingleton(FirebaseRepositoryImpl(getIt())) // Usecase - ..registerSingleton(GetPoemsUseCase(getIt())) + ..registerSingleton(GetInitialPoemsUseCase(getIt())) ..registerSingleton(RegisterUserUseCase(getIt())) ..registerSingleton(LoginUserUseCase(getIt())) diff --git a/lib/features/multi_bloc_provider/presentation/init_blocs.dart b/lib/features/multi_bloc_provider/presentation/init_blocs.dart index 800e2a4..aa756f0 100644 --- a/lib/features/multi_bloc_provider/presentation/init_blocs.dart +++ b/lib/features/multi_bloc_provider/presentation/init_blocs.dart @@ -14,7 +14,7 @@ class InitBlocs extends StatelessWidget { @override Widget build(BuildContext context) => MultiBlocProvider( providers: [ - BlocProvider(create: (context) => getIt()..add(const GetPoemsEvent())), + BlocProvider(create: (context) => getIt()..add(const GetInitialPoemsEvent())), BlocProvider(create:(context) => getIt(),), BlocProvider(create:(context) => getIt()), BlocProvider(create:(context) => getIt()), diff --git a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart index 65c1b2f..54136f6 100644 --- a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart +++ b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart @@ -10,5 +10,5 @@ abstract class PoemApiService{ factory PoemApiService(Dio dio) = _PoemApiService; @GET('random/$defaultPoemsCount') - Future>> getPoems(); + Future>> getInitialPoems(); } diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index 69ef792..2c59557 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -12,9 +12,9 @@ class PoemRepositoryImpl implements PoemRepository{ final PoemApiService _poemApiService; @override - Future>> getPoems() async { + Future>> getInitialPoems() async { try{ - final httpResponse = await _poemApiService.getPoems(); + final httpResponse = await _poemApiService.getInitialPoems(); if(httpResponse.response.statusCode == HttpStatus.ok){ return DataSuccess(httpResponse.data); diff --git a/lib/features/poems_feed/domain/repository/poem_repository.dart b/lib/features/poems_feed/domain/repository/poem_repository.dart index c47482f..b4d21cb 100644 --- a/lib/features/poems_feed/domain/repository/poem_repository.dart +++ b/lib/features/poems_feed/domain/repository/poem_repository.dart @@ -2,5 +2,5 @@ import 'package:poetlum/core/resources/data_state.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; abstract class PoemRepository{ - Future>> getPoems(); + Future>> getInitialPoems(); } diff --git a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart index c684e40..3350251 100644 --- a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart +++ b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart @@ -3,11 +3,11 @@ import 'package:poetlum/core/usecases/usecase.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; -class GetPoemsUseCase implements UseCase>, void>{ - GetPoemsUseCase(this._poemRepository); +class GetInitialPoemsUseCase implements UseCase>, void>{ + GetInitialPoemsUseCase(this._poemRepository); final PoemRepository _poemRepository; @override - Future>> call({void params}) => _poemRepository.getPoems(); + Future>> call({void params}) => _poemRepository.getInitialPoems(); } diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart index 8b8e13f..f0f4ff1 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -7,14 +7,14 @@ import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; class RemotePoemBloc extends Bloc{ - RemotePoemBloc(this._getPoemsUseCase): super(const RemotePoemLoading()){ - on(onGetPoems); + RemotePoemBloc(this._getInitialPoemsUseCase): super(const RemotePoemLoading()){ + on(onGetInitialPoems); } - final GetPoemsUseCase _getPoemsUseCase; + final GetInitialPoemsUseCase _getInitialPoemsUseCase; - Future onGetPoems(GetPoemsEvent event, Emitter emitter) async{ - final dataState = await _getPoemsUseCase(); + Future onGetInitialPoems(GetInitialPoemsEvent event, Emitter emitter) async{ + final dataState = await _getInitialPoemsUseCase(); if(dataState is DataSuccess && dataState.data!.isNotEmpty){ emit( diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart index 2ba0efa..12bc7ae 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart @@ -2,6 +2,6 @@ abstract class RemotePoemEvent{ const RemotePoemEvent(); } -class GetPoemsEvent extends RemotePoemEvent{ - const GetPoemsEvent(); +class GetInitialPoemsEvent extends RemotePoemEvent{ + const GetInitialPoemsEvent(); } From 00c62e750e95f890a1432e3b40df111efaaea3b9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 13:26:42 +0200 Subject: [PATCH 117/475] Add const modifier --- .../poems_feed/presentation/widgets/drawer/custom_drawer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 3f94c2b..c5bdb15 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -6,7 +6,7 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_s import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; class CustomDrawer extends StatefulWidget { - CustomDrawer({super.key}); + const CustomDrawer({super.key}); @override State createState() => _CustomDrawerState(); From 7e7a6fa583d2fb62b02637885bc2761aba806947 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:32:43 +0200 Subject: [PATCH 118/475] Update api service for api calls --- .../poems_feed/data/data_sources/remote/poem_api_service.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart index 54136f6..82e8a4e 100644 --- a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart +++ b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart @@ -11,4 +11,7 @@ abstract class PoemApiService{ @GET('random/$defaultPoemsCount') Future>> getInitialPoems(); + + @GET('/{query}') + Future>> getPoems(@Path('query') String query); } From 4dc654ee848218fb3dd8cd2e11a417c759cf0c40 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:32:51 +0200 Subject: [PATCH 119/475] Update poem repo --- .../data/repository/poem_repository_impl.dart | 49 +++++++++++++++++++ .../domain/repository/poem_repository.dart | 1 + 2 files changed, 50 insertions(+) diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index 2c59557..516bd30 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -32,4 +32,53 @@ class PoemRepositoryImpl implements PoemRepository{ return DataFailed(error); } } + + @override + Future>> getPoems({String? author, String? title, String? lineCount, String? poemCount}) async { + print('repo $author, $title, $lineCount, $poemCount'); + + final outputFields = []; + final searchTerms = []; + + if (author != null && author.isNotEmpty) { + outputFields.add('author'); + searchTerms.add(author); + } + if (title != null && title.isNotEmpty) { + outputFields.add('title'); + searchTerms.add(title); + } + if (lineCount != null && lineCount.isNotEmpty) { + outputFields.add('linecount'); + searchTerms.add(lineCount); + } + if (poemCount != null && poemCount.isNotEmpty) { + outputFields.add('poemcount'); + searchTerms.add(poemCount); + } + + final outputFieldsPart = outputFields.join(','); + final searchTermsPart = searchTerms.join(';'); + final query = '$outputFieldsPart/$searchTermsPart'; + print('query $query'); + + try { + final httpResponse = await _poemApiService.getPoems(query); + + if (httpResponse.response.statusCode == HttpStatus.ok) { + return DataSuccess(httpResponse.data); + } else { + return DataFailed( + DioException( + requestOptions: httpResponse.response.requestOptions, + response: httpResponse.response, + type: DioExceptionType.badResponse, + error: httpResponse.response.statusMessage, + ), + ); + } + } on DioException catch (error) { + return DataFailed(error); + } + } } diff --git a/lib/features/poems_feed/domain/repository/poem_repository.dart b/lib/features/poems_feed/domain/repository/poem_repository.dart index b4d21cb..9633022 100644 --- a/lib/features/poems_feed/domain/repository/poem_repository.dart +++ b/lib/features/poems_feed/domain/repository/poem_repository.dart @@ -3,4 +3,5 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; abstract class PoemRepository{ Future>> getInitialPoems(); + Future>> getPoems({String? author, String? title, String? lineCount, String? poemCount}); } From 3ada27e172ac828081730aa998bd9744266100e4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:33:21 +0200 Subject: [PATCH 120/475] Create new usecase --- .../domain/usecases/get_poems_usecase.dart | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart index 3350251..1ea2b02 100644 --- a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart +++ b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart @@ -11,3 +11,43 @@ class GetInitialPoemsUseCase implements UseCase>, voi @override Future>> call({void params}) => _poemRepository.getInitialPoems(); } + +class GetPoemsUseCaseParams { + const GetPoemsUseCaseParams({ + this.author, + this.title, + this.lineCount, + this.poemCount, + }); + + final String? author; + final String? title; + final String? lineCount; + final String? poemCount; +} + +class GetPoemsUseCase implements UseCase>, GetPoemsUseCaseParams>{ + GetPoemsUseCase(this._poemRepository); + + final PoemRepository _poemRepository; + + @override + Future>> call({GetPoemsUseCaseParams? params}){ + if( + params != null && + (params.author != '' && + params.title != '' && + params.lineCount != '' && + params.poemCount != '') + ){ + return _poemRepository.getPoems( + author: params.author, + title: params.title, + lineCount: params.lineCount, + poemCount: params.poemCount, + ); + } else{ + return _poemRepository.getInitialPoems(); + } + } +} From 245486935f7fda10166cb72829e8bb5e620c7d6e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:33:30 +0200 Subject: [PATCH 121/475] Update bloc --- .../bloc/poem/remote/remote_poem_bloc.dart | 32 ++++++++++++++++++- .../bloc/poem/remote/remote_poem_event.dart | 14 ++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart index f0f4ff1..d17f08e 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -7,11 +7,13 @@ import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; class RemotePoemBloc extends Bloc{ - RemotePoemBloc(this._getInitialPoemsUseCase): super(const RemotePoemLoading()){ + RemotePoemBloc(this._getInitialPoemsUseCase, this._getPoemsUseCase): super(const RemotePoemLoading()){ on(onGetInitialPoems); + on(onGetPoems); } final GetInitialPoemsUseCase _getInitialPoemsUseCase; + final GetPoemsUseCase _getPoemsUseCase; Future onGetInitialPoems(GetInitialPoemsEvent event, Emitter emitter) async{ final dataState = await _getInitialPoemsUseCase(); @@ -28,4 +30,32 @@ class RemotePoemBloc extends Bloc{ ); } } + + Future onGetPoems(GetPoemsEvent event, Emitter emitter) async{ + print('onGetPoems ${event.author} ${event.title} ${event.lineCount} ${event.poemCount}'); + emit(const RemotePoemLoading()); + + final dataState = await _getPoemsUseCase( + params: GetPoemsUseCaseParams( + author: event.author, + lineCount: event.lineCount, + poemCount: event.poemCount, + title: event.title, + ), + ); + + print('datastate, ${dataState.data}'); + + if(dataState is DataSuccess && dataState.data!.isNotEmpty){ + emit( + RemotePoemDone(dataState.data!), + ); + } + + if(dataState is DataFailed){ + emit( + RemotePoemError(dataState.error!), + ); + } + } } diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart index 12bc7ae..cb491e3 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart @@ -5,3 +5,17 @@ abstract class RemotePoemEvent{ class GetInitialPoemsEvent extends RemotePoemEvent{ const GetInitialPoemsEvent(); } + +class GetPoemsEvent extends RemotePoemEvent{ + const GetPoemsEvent({ + this.author, + this.title, + this.lineCount, + this.poemCount, + }); + + final String? author; + final String? title; + final String? lineCount; + final String? poemCount; +} From a5b3a6888596ff289e44524ebad20fddb6c10ee9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:37:04 +0200 Subject: [PATCH 122/475] Add api call on button press --- .../presentation/widgets/drawer/custom_drawer.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index c5bdb15..d5c9479 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; @@ -44,7 +47,14 @@ class _CustomDrawerState extends State { CustomSearchButton( onPressed: () { - print(_isRandom); + BlocProvider.of(context).add( + GetPoemsEvent( + author: _authorController.text, + title: _titleController.text, + lineCount: _numberOfLinesController.text, + poemCount: _resultCountController.text, + ), + ); }, ), ], From 20759378a66549a54d9133aac9b8a89e89016627 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:37:16 +0200 Subject: [PATCH 123/475] Register stuff in the DI --- lib/core/dependency_injection.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 1b92577..424f997 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -38,6 +38,7 @@ void initializeDependencies() { // Usecase ..registerSingleton(GetInitialPoemsUseCase(getIt())) + ..registerSingleton(GetPoemsUseCase(getIt())) ..registerSingleton(RegisterUserUseCase(getIt())) ..registerSingleton(LoginUserUseCase(getIt())) @@ -47,7 +48,7 @@ void initializeDependencies() { ..registerLazySingleton(() => PasswordValidator()) // Bloc - ..registerFactory(() => RemotePoemBloc(getIt())) + ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), From 131e9d42bf91748fba64725a6e8fd70e7f163c6b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:38:44 +0200 Subject: [PATCH 124/475] Remove print statements --- .../poems_feed/data/repository/poem_repository_impl.dart | 3 --- .../presentation/bloc/poem/remote/remote_poem_bloc.dart | 3 --- 2 files changed, 6 deletions(-) diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index 516bd30..7d5bb6c 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -35,8 +35,6 @@ class PoemRepositoryImpl implements PoemRepository{ @override Future>> getPoems({String? author, String? title, String? lineCount, String? poemCount}) async { - print('repo $author, $title, $lineCount, $poemCount'); - final outputFields = []; final searchTerms = []; @@ -60,7 +58,6 @@ class PoemRepositoryImpl implements PoemRepository{ final outputFieldsPart = outputFields.join(','); final searchTermsPart = searchTerms.join(';'); final query = '$outputFieldsPart/$searchTermsPart'; - print('query $query'); try { final httpResponse = await _poemApiService.getPoems(query); diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart index d17f08e..2df534f 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -32,7 +32,6 @@ class RemotePoemBloc extends Bloc{ } Future onGetPoems(GetPoemsEvent event, Emitter emitter) async{ - print('onGetPoems ${event.author} ${event.title} ${event.lineCount} ${event.poemCount}'); emit(const RemotePoemLoading()); final dataState = await _getPoemsUseCase( @@ -44,8 +43,6 @@ class RemotePoemBloc extends Bloc{ ), ); - print('datastate, ${dataState.data}'); - if(dataState is DataSuccess && dataState.data!.isNotEmpty){ emit( RemotePoemDone(dataState.data!), From bf1028aa442b4acec5f167641cc261759d60cf10 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:43:54 +0200 Subject: [PATCH 125/475] Usecase condition rework --- .../domain/usecases/get_poems_usecase.dart | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart index 1ea2b02..090f6f7 100644 --- a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart +++ b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart @@ -34,20 +34,22 @@ class GetPoemsUseCase implements UseCase>, GetPoemsUs @override Future>> call({GetPoemsUseCaseParams? params}){ if( - params != null && - (params.author != '' && - params.title != '' && - params.lineCount != '' && - params.poemCount != '') + params == null || + ( + params.author == '' && + params.title == '' && + params.lineCount == '' && + params.poemCount == '' + ) ){ + return _poemRepository.getInitialPoems(); + } else{ return _poemRepository.getPoems( author: params.author, title: params.title, lineCount: params.lineCount, poemCount: params.poemCount, ); - } else{ - return _poemRepository.getInitialPoems(); } - } + } } From f4cf6727b87fe48065330e78b27437c7fd7bd9b7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:46:10 +0200 Subject: [PATCH 126/475] Remove nullable properties --- .../domain/usecases/get_poems_usecase.dart | 16 ++++++++-------- .../bloc/poem/remote/remote_poem_event.dart | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart index 090f6f7..74e47f5 100644 --- a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart +++ b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart @@ -14,16 +14,16 @@ class GetInitialPoemsUseCase implements UseCase>, voi class GetPoemsUseCaseParams { const GetPoemsUseCaseParams({ - this.author, - this.title, - this.lineCount, - this.poemCount, + required this.author, + required this.title, + required this.lineCount, + required this.poemCount, }); - final String? author; - final String? title; - final String? lineCount; - final String? poemCount; + final String author; + final String title; + final String lineCount; + final String poemCount; } class GetPoemsUseCase implements UseCase>, GetPoemsUseCaseParams>{ diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart index cb491e3..c676e51 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart @@ -8,14 +8,14 @@ class GetInitialPoemsEvent extends RemotePoemEvent{ class GetPoemsEvent extends RemotePoemEvent{ const GetPoemsEvent({ - this.author, - this.title, - this.lineCount, - this.poemCount, + required this.author, + required this.title, + required this.lineCount, + required this.poemCount, }); - final String? author; - final String? title; - final String? lineCount; - final String? poemCount; + final String author; + final String title; + final String lineCount; + final String poemCount; } From de34916aca13fc5b00c5125becdf595d94e8e4d1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:35:46 +0200 Subject: [PATCH 127/475] Create user entity --- .../poems_feed/domain/entities/firebase_user.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/features/poems_feed/domain/entities/firebase_user.dart diff --git a/lib/features/poems_feed/domain/entities/firebase_user.dart b/lib/features/poems_feed/domain/entities/firebase_user.dart new file mode 100644 index 0000000..c04b67d --- /dev/null +++ b/lib/features/poems_feed/domain/entities/firebase_user.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; + +class FirebaseUserEntity extends Equatable{ + const FirebaseUserEntity({this.username, this.email}); + + final String? username; + final String? email; + + @override + List get props => [ + username, + email, + ]; +} From 73e0f88a7e42d43ff8b0263bfa309dc6e8c2fead Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:35:57 +0200 Subject: [PATCH 128/475] Create user model --- lib/features/poems_feed/data/models/firebase_user.dart | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/features/poems_feed/data/models/firebase_user.dart diff --git a/lib/features/poems_feed/data/models/firebase_user.dart b/lib/features/poems_feed/data/models/firebase_user.dart new file mode 100644 index 0000000..8efddc3 --- /dev/null +++ b/lib/features/poems_feed/data/models/firebase_user.dart @@ -0,0 +1,8 @@ +import 'package:poetlum/features/poems_feed/domain/entities/firebase_user.dart'; + +class FirebaserUserModel extends FirebaseUserEntity{ + const FirebaserUserModel({ + super.username = '', + super.email = '', + }); +} From cbf6119304bf29c189d97925f17fbde6269e8bb1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:36:07 +0200 Subject: [PATCH 129/475] Create user repository --- .../data/repository/user_repository_impl.dart | 16 ++++++++++++++++ .../domain/repository/user_repository.dart | 5 +++++ 2 files changed, 21 insertions(+) create mode 100644 lib/features/poems_feed/data/repository/user_repository_impl.dart create mode 100644 lib/features/poems_feed/domain/repository/user_repository.dart diff --git a/lib/features/poems_feed/data/repository/user_repository_impl.dart b/lib/features/poems_feed/data/repository/user_repository_impl.dart new file mode 100644 index 0000000..c6bbc1c --- /dev/null +++ b/lib/features/poems_feed/data/repository/user_repository_impl.dart @@ -0,0 +1,16 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; + +class UserRepositoryImpl implements UserRepository{ + UserRepositoryImpl(this._firebaseAuth); + + final FirebaseAuth _firebaseAuth; + + @override + FirebaserUserModel getCurrentUser() { + final firebaseUser = _firebaseAuth.currentUser; + + return FirebaserUserModel(username: firebaseUser?.displayName); + } +} diff --git a/lib/features/poems_feed/domain/repository/user_repository.dart b/lib/features/poems_feed/domain/repository/user_repository.dart new file mode 100644 index 0000000..ffaac64 --- /dev/null +++ b/lib/features/poems_feed/domain/repository/user_repository.dart @@ -0,0 +1,5 @@ +import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; + +abstract class UserRepository{ + FirebaserUserModel getCurrentUser(); +} From 7f03d55b64b92721341b381b3f1e39f577a145af Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:36:25 +0200 Subject: [PATCH 130/475] Add username to the header --- .../presentation/pages/home/poems_feed.dart | 4 ++- .../widgets/drawer/custom_drawer.dart | 7 +++-- .../widgets/drawer/custom_header.dart | 28 +++++++++++++------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index e5c7ba8..b46c54b 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -1,6 +1,8 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; @@ -23,7 +25,7 @@ class PoemsFeed extends StatelessWidget { appBar: const CustomAppBar( title: 'Poetlum', ), - drawer: CustomDrawer(), + drawer: CustomDrawer(UserRepositoryImpl(FirebaseAuth.instance)), body: _buildBody(), ); diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index d5c9479..61a5802 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; @@ -9,7 +10,9 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_s import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; class CustomDrawer extends StatefulWidget { - const CustomDrawer({super.key}); + const CustomDrawer(this._userRepository, {super.key}); + + final UserRepository _userRepository; @override State createState() => _CustomDrawerState(); @@ -28,7 +31,7 @@ class _CustomDrawerState extends State { height: MediaQuery.of(context).size.height, child: ListView( children: [ - const CustomDrawerHeader(), + CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), CustomTextField(hintText: 'Author', controller: _authorController), const CustomSpacer(heightFactor: 0.04), diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart index 203ab38..fc3f77e 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart @@ -1,20 +1,30 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; class CustomDrawerHeader extends StatelessWidget { - const CustomDrawerHeader({super.key}); + const CustomDrawerHeader({super.key, required this.user}); + + final FirebaserUserModel user; @override - Widget build(BuildContext context) => const Column( + Widget build(BuildContext context) => Column( children: [ - CustomSpacer(heightFactor: 0.04), - Center( - child: Text( - 'Search settings', - style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), - ), + const CustomSpacer(heightFactor: 0.02), + + Text( + 'Hello, ${user.username}', + style: const TextStyle(fontSize: 20), + ), + + const CustomSpacer(heightFactor: 0.02), + + const Text( + 'Search settings', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), ), - CustomSpacer(heightFactor: 0.04), + + const CustomSpacer(heightFactor: 0.04), ], ); } From cb23f98de17db6f1c25567af1efa046d0cab2acb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 19 Nov 2023 18:12:37 +0200 Subject: [PATCH 131/475] Add error handling --- lib/core/resources/data_state.dart | 5 +++-- .../data/repository/poem_repository_impl.dart | 12 ++++++++++-- .../bloc/poem/remote/remote_poem_bloc.dart | 4 ++-- .../bloc/poem/remote/remote_poem_state.dart | 4 +++- .../presentation/pages/home/poems_feed.dart | 15 ++++++++++++++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/core/resources/data_state.dart b/lib/core/resources/data_state.dart index e84b45f..b09d4e1 100644 --- a/lib/core/resources/data_state.dart +++ b/lib/core/resources/data_state.dart @@ -1,10 +1,11 @@ import 'package:dio/dio.dart'; abstract class DataState{ - const DataState({this.data, this.error}); + const DataState({this.data, this.error, this.message}); final T? data; final DioException? error; + final String? message; } class DataSuccess extends DataState{ @@ -12,5 +13,5 @@ class DataSuccess extends DataState{ } class DataFailed extends DataState{ - const DataFailed(DioException error) : super(error: error); + const DataFailed(DioException error, String message) : super(error: error, message: message); } diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index 7d5bb6c..873cb5b 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -26,10 +26,14 @@ class PoemRepositoryImpl implements PoemRepository{ type: DioExceptionType.badResponse, error: httpResponse.response.statusMessage, ), + 'An error occurred during the attempt to connect to the server.' ); } } on DioException catch(error){ - return DataFailed(error); + return DataFailed( + error, + 'An error occurred during the attempt to connect to the server.', + ); } } @@ -72,10 +76,14 @@ class PoemRepositoryImpl implements PoemRepository{ type: DioExceptionType.badResponse, error: httpResponse.response.statusMessage, ), + 'An error occurred during the attempt to connect to the server.', ); } } on DioException catch (error) { - return DataFailed(error); + return DataFailed( + error, + 'An error occurred. Please ensure that you have provided the correct search settings.', + ); } } } diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart index 2df534f..fe91fbc 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -26,7 +26,7 @@ class RemotePoemBloc extends Bloc{ if(dataState is DataFailed){ emit( - RemotePoemError(dataState.error!), + RemotePoemError(dataState.error!, dataState.message!), ); } } @@ -51,7 +51,7 @@ class RemotePoemBloc extends Bloc{ if(dataState is DataFailed){ emit( - RemotePoemError(dataState.error!), + RemotePoemError(dataState.error!, dataState.message!), ); } } diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart index 40bd944..d8ab1be 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart @@ -24,5 +24,7 @@ class RemotePoemDone extends RemotePoemState{ } class RemotePoemError extends RemotePoemState{ - const RemotePoemError(DioException error) : super(error: error); + const RemotePoemError(DioException error, this.message) : super(error: error); + + final String message; } diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index b46c54b..88448a3 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -36,7 +36,7 @@ class PoemsFeed extends StatelessWidget { } if(state is RemotePoemError){ - return const Center(child: Icon(Icons.refresh)); + return _buildErrorBody(state.message); } if(state is RemotePoemDone){ @@ -51,4 +51,17 @@ class PoemsFeed extends StatelessWidget { return const SizedBox(); }, ); + + Widget _buildErrorBody(String error) => Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Text(error), + ), + const Icon(Icons.refresh), + ], + ), + ); } From 2e2bd3a7656c79477be4d0e4793de1b8a4d8a6ae Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:25:10 +0200 Subject: [PATCH 132/475] Add refresh button functionality --- .../presentation/pages/home/poems_feed.dart | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 88448a3..704bf93 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_card.dart'; @@ -26,17 +27,17 @@ class PoemsFeed extends StatelessWidget { title: 'Poetlum', ), drawer: CustomDrawer(UserRepositoryImpl(FirebaseAuth.instance)), - body: _buildBody(), + body: _buildBody(context), ); - BlocBuilder _buildBody() => BlocBuilder( + BlocBuilder _buildBody(BuildContext context) => BlocBuilder( builder: (_, state){ if(state is RemotePoemLoading){ return const Center(child: CircularProgressIndicator(),); } if(state is RemotePoemError){ - return _buildErrorBody(state.message); + return _buildErrorBody(state.message, context); } if(state is RemotePoemDone){ @@ -52,7 +53,7 @@ class PoemsFeed extends StatelessWidget { }, ); - Widget _buildErrorBody(String error) => Center( + Widget _buildErrorBody(String error, BuildContext context) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -60,7 +61,15 @@ class PoemsFeed extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 30), child: Text(error), ), - const Icon(Icons.refresh), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: IconButton.filledTonal( + onPressed: (){ + BlocProvider.of(context).add(const GetInitialPoemsEvent()); + }, + icon: const Icon(Icons.refresh), + ), + ), ], ), ); From 021b336e52524025f94a170e536bf71831c0e062 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:26:21 +0200 Subject: [PATCH 133/475] Code refactor --- .../poems_feed/presentation/pages/home/poems_feed.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 704bf93..01967ca 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -64,9 +64,7 @@ class PoemsFeed extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(vertical: 20), child: IconButton.filledTonal( - onPressed: (){ - BlocProvider.of(context).add(const GetInitialPoemsEvent()); - }, + onPressed: () => BlocProvider.of(context).add(const GetInitialPoemsEvent()), icon: const Icon(Icons.refresh), ), ), From 9e51801d9557659ee10be56162684cadae27bf95 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:27:17 +0200 Subject: [PATCH 134/475] Add RemotePoemLoading emittng --- .../presentation/bloc/poem/remote/remote_poem_bloc.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart index fe91fbc..95d82e1 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -16,6 +16,8 @@ class RemotePoemBloc extends Bloc{ final GetPoemsUseCase _getPoemsUseCase; Future onGetInitialPoems(GetInitialPoemsEvent event, Emitter emitter) async{ + emit(const RemotePoemLoading()); + final dataState = await _getInitialPoemsUseCase(); if(dataState is DataSuccess && dataState.data!.isNotEmpty){ From 6e79b114b7484b2f0d60db22dd79dac256440890 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:35:16 +0200 Subject: [PATCH 135/475] Change fontsize --- .../poems_feed/presentation/pages/home/poems_feed.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 01967ca..28eec76 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -59,7 +59,12 @@ class PoemsFeed extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text(error), + child: Text( + error, + style: const TextStyle( + fontSize: 16, + ), + ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 20), From b99e121581abb3ef905b04bbdabe132bbb3f00d5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:12:58 +0200 Subject: [PATCH 136/475] Add random search functionality --- .../data/repository/poem_repository_impl.dart | 28 ++++++++++++++----- .../domain/repository/poem_repository.dart | 2 +- .../domain/usecases/get_poems_usecase.dart | 6 +++- .../bloc/poem/remote/remote_poem_bloc.dart | 1 + .../bloc/poem/remote/remote_poem_event.dart | 2 ++ .../widgets/drawer/custom_drawer.dart | 1 + 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index 873cb5b..c6be19a 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:poetlum/core/constants/poems_constants.dart'; import 'package:poetlum/core/resources/data_state.dart'; import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; import 'package:poetlum/features/poems_feed/data/models/poem.dart'; @@ -38,31 +39,44 @@ class PoemRepositoryImpl implements PoemRepository{ } @override - Future>> getPoems({String? author, String? title, String? lineCount, String? poemCount}) async { + Future>> getPoems({required String author, required String title, required String lineCount, required String poemCount, required bool isRandom}) async { final outputFields = []; final searchTerms = []; - if (author != null && author.isNotEmpty) { + if (author.isNotEmpty) { outputFields.add('author'); searchTerms.add(author); } - if (title != null && title.isNotEmpty) { + if (title.isNotEmpty) { outputFields.add('title'); searchTerms.add(title); } - if (lineCount != null && lineCount.isNotEmpty) { + if (lineCount.isNotEmpty) { outputFields.add('linecount'); searchTerms.add(lineCount); } - if (poemCount != null && poemCount.isNotEmpty) { - outputFields.add('poemcount'); - searchTerms.add(poemCount); + if (poemCount.isNotEmpty) { + if(isRandom){ + print('changed to random poemCount != null'); + outputFields.add('random'); + searchTerms.add(poemCount); + } else{ + outputFields.add('poemcount'); + searchTerms.add(poemCount); + } + } + if(poemCount.isEmpty && isRandom){ + print('changed to random poemCount == null'); + outputFields.add('random'); + searchTerms.add(defaultPoemsCount.toString()); } final outputFieldsPart = outputFields.join(','); final searchTermsPart = searchTerms.join(';'); final query = '$outputFieldsPart/$searchTermsPart'; + print(query); + try { final httpResponse = await _poemApiService.getPoems(query); diff --git a/lib/features/poems_feed/domain/repository/poem_repository.dart b/lib/features/poems_feed/domain/repository/poem_repository.dart index 9633022..aed8320 100644 --- a/lib/features/poems_feed/domain/repository/poem_repository.dart +++ b/lib/features/poems_feed/domain/repository/poem_repository.dart @@ -3,5 +3,5 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; abstract class PoemRepository{ Future>> getInitialPoems(); - Future>> getPoems({String? author, String? title, String? lineCount, String? poemCount}); + Future>> getPoems({required String author, required String title, required String lineCount, required String poemCount, required bool isRandom}); } diff --git a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart index 74e47f5..08d4705 100644 --- a/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart +++ b/lib/features/poems_feed/domain/usecases/get_poems_usecase.dart @@ -18,12 +18,14 @@ class GetPoemsUseCaseParams { required this.title, required this.lineCount, required this.poemCount, + required this.isRandom, }); final String author; final String title; final String lineCount; final String poemCount; + final bool isRandom; } class GetPoemsUseCase implements UseCase>, GetPoemsUseCaseParams>{ @@ -39,7 +41,8 @@ class GetPoemsUseCase implements UseCase>, GetPoemsUs params.author == '' && params.title == '' && params.lineCount == '' && - params.poemCount == '' + params.poemCount == '' && + !params.isRandom ) ){ return _poemRepository.getInitialPoems(); @@ -49,6 +52,7 @@ class GetPoemsUseCase implements UseCase>, GetPoemsUs title: params.title, lineCount: params.lineCount, poemCount: params.poemCount, + isRandom: params.isRandom, ); } } diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart index 95d82e1..de3d287 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart @@ -42,6 +42,7 @@ class RemotePoemBloc extends Bloc{ lineCount: event.lineCount, poemCount: event.poemCount, title: event.title, + isRandom: event.isRandom, ), ); diff --git a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart index c676e51..ea2c097 100644 --- a/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart +++ b/lib/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart @@ -12,10 +12,12 @@ class GetPoemsEvent extends RemotePoemEvent{ required this.title, required this.lineCount, required this.poemCount, + required this.isRandom, }); final String author; final String title; final String lineCount; final String poemCount; + final bool isRandom; } diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 61a5802..5518845 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -56,6 +56,7 @@ class _CustomDrawerState extends State { title: _titleController.text, lineCount: _numberOfLinesController.text, poemCount: _resultCountController.text, + isRandom: _isRandom ?? false, ), ); }, From 7b7d383b1c9e74901105e74e4d1dfe7421a7f682 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:33:28 +0200 Subject: [PATCH 137/475] Refactor function --- .../data/repository/poem_repository_impl.dart | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index c6be19a..dc1c31a 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -27,7 +27,7 @@ class PoemRepositoryImpl implements PoemRepository{ type: DioExceptionType.badResponse, error: httpResponse.response.statusMessage, ), - 'An error occurred during the attempt to connect to the server.' + 'An error occurred during the attempt to connect to the server.', ); } } on DioException catch(error){ @@ -40,41 +40,7 @@ class PoemRepositoryImpl implements PoemRepository{ @override Future>> getPoems({required String author, required String title, required String lineCount, required String poemCount, required bool isRandom}) async { - final outputFields = []; - final searchTerms = []; - - if (author.isNotEmpty) { - outputFields.add('author'); - searchTerms.add(author); - } - if (title.isNotEmpty) { - outputFields.add('title'); - searchTerms.add(title); - } - if (lineCount.isNotEmpty) { - outputFields.add('linecount'); - searchTerms.add(lineCount); - } - if (poemCount.isNotEmpty) { - if(isRandom){ - print('changed to random poemCount != null'); - outputFields.add('random'); - searchTerms.add(poemCount); - } else{ - outputFields.add('poemcount'); - searchTerms.add(poemCount); - } - } - if(poemCount.isEmpty && isRandom){ - print('changed to random poemCount == null'); - outputFields.add('random'); - searchTerms.add(defaultPoemsCount.toString()); - } - - final outputFieldsPart = outputFields.join(','); - final searchTermsPart = searchTerms.join(';'); - final query = '$outputFieldsPart/$searchTermsPart'; - + final query = _buildQuery(author, title, lineCount, poemCount, isRandom); print(query); try { @@ -100,4 +66,28 @@ class PoemRepositoryImpl implements PoemRepository{ ); } } + + String _buildQuery(String author, String title, String lineCount, String poemCount, bool isRandom) { + final outputFields = []; + final searchTerms = []; + + _addQueryPart(outputFields, searchTerms, 'author', author); + _addQueryPart(outputFields, searchTerms, 'title', title); + _addQueryPart(outputFields, searchTerms, 'linecount', lineCount); + + if (poemCount.isNotEmpty || isRandom) { + final field = isRandom ? 'random' : 'poemcount'; + final count = poemCount.isNotEmpty ? poemCount : defaultPoemsCount.toString(); + _addQueryPart(outputFields, searchTerms, field, count); + } + + return '${outputFields.join(',')}/${searchTerms.join(';')}'; + } + + void _addQueryPart(List fields, List terms, String field, String term) { + if (term.isNotEmpty) { + fields.add(field); + terms.add(term); + } + } } From 80572668eea20c5af94df4aa76750e7464246ad8 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:33:38 +0200 Subject: [PATCH 138/475] Remove print statement --- .../poems_feed/data/repository/poem_repository_impl.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index dc1c31a..478abdf 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -41,7 +41,6 @@ class PoemRepositoryImpl implements PoemRepository{ @override Future>> getPoems({required String author, required String title, required String lineCount, required String poemCount, required bool isRandom}) async { final query = _buildQuery(author, title, lineCount, poemCount, isRandom); - print(query); try { final httpResponse = await _poemApiService.getPoems(query); From e7af85a0856e0fab36fd5de24f5cc7c23236429e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:11:51 +0200 Subject: [PATCH 139/475] Create navbar --- .../presentation/pages/home/poems_feed.dart | 12 ++++++++++++ pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 3 files changed, 21 insertions(+) diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 28eec76..49438c0 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -1,6 +1,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; @@ -18,6 +19,7 @@ class CustomSpacer extends StatelessWidget { Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); } + class PoemsFeed extends StatelessWidget { const PoemsFeed({super.key}); @@ -28,6 +30,16 @@ class PoemsFeed extends StatelessWidget { ), drawer: CustomDrawer(UserRepositoryImpl(FirebaseAuth.instance)), body: _buildBody(context), + bottomNavigationBar: GNav( + onTabChange: (value) { + + }, + gap: 12, + tabs: const [ + GButton(icon: Icons.menu, text: 'Menu'), + GButton(icon: Icons.bookmark_outline_rounded, text: 'Saved poems'), + ], + ), ); BlocBuilder _buildBody(BuildContext context) => BlocBuilder( diff --git a/pubspec.lock b/pubspec.lock index 36b2ed9..d0c33cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -480,6 +480,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + google_nav_bar: + dependency: "direct main" + description: + name: google_nav_bar + sha256: "1c8e3882fa66ee7b74c24320668276ca23affbd58f0b14a24c1e5590f4d07ab0" + url: "https://pub.dev" + source: hosted + version: "5.0.6" graphs: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fcb27dd..33c9238 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,6 +50,7 @@ dependencies: flutter_bloc: ^8.1.3 email_validator: ^2.1.17 fluttertoast: ^8.0.9 + google_nav_bar: ^5.0.6 dev_dependencies: flutter_test: From 9b3fd72a1540e3d644d89e63a0836fe1aced3b97 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:24:29 +0200 Subject: [PATCH 140/475] Rename PoemsFeed page --- lib/features/application/poetlum_app.dart | 2 +- .../application/presentation/widgets/auth_wrapper.dart | 2 +- .../poems_feed/presentation/pages/home/poems_feed.dart | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 57a3225..9e27b5d 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -21,7 +21,7 @@ class PoetlumApp extends StatelessWidget { authWrapperPageConstant:(_) => const AuthWrapper(), registerPageConstant: (_) => const RegistrationPage(), loginPageConstant: (_) => const LoginPage(), - poemsFeedPageConstant: (_) => const PoemsFeed(), + poemsFeedPageConstant: (_) => const PoemsFeedPage(), }, ), ); diff --git a/lib/features/application/presentation/widgets/auth_wrapper.dart b/lib/features/application/presentation/widgets/auth_wrapper.dart index 930c68a..db7b2fe 100644 --- a/lib/features/application/presentation/widgets/auth_wrapper.dart +++ b/lib/features/application/presentation/widgets/auth_wrapper.dart @@ -20,7 +20,7 @@ class AuthWrapper extends StatelessWidget { if (user == null) { return const RegistrationPage(); } - return const PoemsFeed(); + return const PoemsFeedPage(); } return const Scaffold( diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 49438c0..0eb5734 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -20,8 +20,8 @@ class CustomSpacer extends StatelessWidget { } -class PoemsFeed extends StatelessWidget { - const PoemsFeed({super.key}); +class PoemsFeedPage extends StatelessWidget { + const PoemsFeedPage({super.key}); @override Widget build(BuildContext context) => Scaffold( From b9c45556c7caf35089c869206a8ef89d65a7c9ef Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:29:05 +0200 Subject: [PATCH 141/475] Create poem view page template --- .../presentation/pages/poem_view/poem_view.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart new file mode 100644 index 0000000..4218d6b --- /dev/null +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; + +class PoemViewPage extends StatelessWidget { + const PoemViewPage({super.key}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: CustomAppBar(title: 'Poetlum'), + body: Text('Page :)'), + ); +} \ No newline at end of file From f9c8a29a63fd899ca82f1c82413ef47179515b21 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:29:12 +0200 Subject: [PATCH 142/475] Add page constant --- lib/core/constants/navigator_constants.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/constants/navigator_constants.dart b/lib/core/constants/navigator_constants.dart index f2d2e9e..d9511fb 100644 --- a/lib/core/constants/navigator_constants.dart +++ b/lib/core/constants/navigator_constants.dart @@ -2,3 +2,4 @@ const String authWrapperPageConstant = '/auth_wrapper'; const String registerPageConstant = '/register'; const String loginPageConstant = '/login'; const String poemsFeedPageConstant = '/poems_feed'; +const String poemViewPageConstant = '/poem_view'; From 441bbb29cd961eb6ddaa1524097e8e0cf2524b1c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:29:24 +0200 Subject: [PATCH 143/475] Register page --- lib/features/application/poetlum_app.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 9e27b5d..3a4489c 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -7,6 +7,7 @@ import 'package:poetlum/features/authorization/presentation/pages/login/login_pa import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; +import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @@ -22,6 +23,7 @@ class PoetlumApp extends StatelessWidget { registerPageConstant: (_) => const RegistrationPage(), loginPageConstant: (_) => const LoginPage(), poemsFeedPageConstant: (_) => const PoemsFeedPage(), + poemViewPageConstant:(_) => const PoemViewPage(), }, ), ); From b9a7ed375a09c18178b29ffdd219ffcc3a052197 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:29:33 +0200 Subject: [PATCH 144/475] Add gesture detector --- .../presentation/widgets/poem_card.dart | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poem_card.dart index 7012654..ae73b9a 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_card.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class PoemCard extends StatelessWidget { @@ -7,24 +8,29 @@ class PoemCard extends StatelessWidget { final PoemEntity poemEntity; @override - Widget build(BuildContext context) => Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8)), - ), - elevation: 3, - margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _TitleText(title: poemEntity.title), - const SizedBox(height: 8), - _AuthorText(author: poemEntity.author), - const SizedBox(height: 16), - _PoemText(text: poemEntity.text, maxLength: 250), - ], + Widget build(BuildContext context) => GestureDetector( + onTap: () { + Navigator.pushNamed(context, poemViewPageConstant); + }, + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + elevation: 3, + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TitleText(title: poemEntity.title), + const SizedBox(height: 8), + _AuthorText(author: poemEntity.author), + const SizedBox(height: 16), + _PoemText(text: poemEntity.text, maxLength: 250), + ], + ), ), ), ); From acf641cb6839a6b378bafd5ed7ed7484de4f50be Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:43:32 +0200 Subject: [PATCH 145/475] Make appbar text scrollable --- .../presentation/widgets/AppBar/app_bar.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index d2a08d9..b7e1d2c 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -11,10 +11,13 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { actions: const [ SettingsButton(), ], - title: Text( - title, - style: const TextStyle( - fontWeight: FontWeight.bold, + title: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Text( + title, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), ), ), ); From 02b3d6785b3a389a557a8bb74c533e7e8a2a43d8 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:52:26 +0200 Subject: [PATCH 146/475] Move spacer into the widget --- .../poems_feed/presentation/pages/home/poems_feed.dart | 10 ---------- .../poems_feed/presentation/widgets/custom_spacer.dart | 10 ++++++++++ .../presentation/widgets/drawer/custom_drawer.dart | 2 +- .../presentation/widgets/drawer/custom_header.dart | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 lib/features/poems_feed/presentation/widgets/custom_spacer.dart diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 0eb5734..8d0b829 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -10,16 +10,6 @@ import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_card.dart'; -class CustomSpacer extends StatelessWidget { - const CustomSpacer({super.key, required this.heightFactor}); - final double heightFactor; - - - @override - Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); -} - - class PoemsFeedPage extends StatelessWidget { const PoemsFeedPage({super.key}); diff --git a/lib/features/poems_feed/presentation/widgets/custom_spacer.dart b/lib/features/poems_feed/presentation/widgets/custom_spacer.dart new file mode 100644 index 0000000..5adf13a --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/custom_spacer.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class CustomSpacer extends StatelessWidget { + const CustomSpacer({super.key, required this.heightFactor}); + final double heightFactor; + + + @override + Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); +} \ No newline at end of file diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 5518845..204e137 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; -import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart'; diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart index fc3f77e..b20d48c 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; -import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; class CustomDrawerHeader extends StatelessWidget { const CustomDrawerHeader({super.key, required this.user}); From fde906fc01a3bd8bfd401adcdca51730ab717ccf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:07:44 +0200 Subject: [PATCH 147/475] Create poems view page --- .../presentation/widgets/AppBar/app_bar.dart | 11 ++-- .../pages/poem_view/poem_view.dart | 60 +++++++++++++++++-- .../presentation/widgets/poem_card.dart | 4 +- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index b7e1d2c..d2a08d9 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -11,13 +11,10 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { actions: const [ SettingsButton(), ], - title: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Text( - title, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + title: Text( + title, + style: const TextStyle( + fontWeight: FontWeight.bold, ), ), ); diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 4218d6b..8d84510 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,12 +1,62 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; class PoemViewPage extends StatelessWidget { const PoemViewPage({super.key}); @override - Widget build(BuildContext context) => Scaffold( - appBar: CustomAppBar(title: 'Poetlum'), - body: Text('Page :)'), - ); -} \ No newline at end of file + Widget build(BuildContext context){ + final poemEntity = (ModalRoute.of(context)?.settings.arguments ?? const PoemEntity()) as PoemEntity; + + return Scaffold( + appBar: const CustomAppBar(title: 'Poetlum'), + body: SizedBox( + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.02), + Text( + poemEntity.title ?? '', + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const CustomSpacer(heightFactor: 0.02), + Text( + poemEntity.author ?? '', + style: const TextStyle( + fontSize: 22, + fontStyle: FontStyle.italic + ), + ), + const CustomSpacer(heightFactor: 0.02), + Text( + poemEntity.text ?? '', + style: const TextStyle( + fontSize: 16, + ), + ), + const CustomSpacer(heightFactor: 0.02), + Align( + alignment: Alignment.centerRight, + child: Text( + '${poemEntity.linecount ?? 0} lines', + style: const TextStyle( + fontStyle: FontStyle.italic + ), + ), + ), + ], + ), + ), + ), + ) + ); + } +} diff --git a/lib/features/poems_feed/presentation/widgets/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poem_card.dart index ae73b9a..e243dff 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_card.dart @@ -9,9 +9,7 @@ class PoemCard extends StatelessWidget { @override Widget build(BuildContext context) => GestureDetector( - onTap: () { - Navigator.pushNamed(context, poemViewPageConstant); - }, + onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), From 64c7e9166884d459e41ade3287b3f88dda36c0a0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:08:55 +0200 Subject: [PATCH 148/475] Organize imports --- .../authorization/presentation/pages/login/login_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 97cc8aa..e47794e 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -5,9 +5,9 @@ import 'package:poetlum/features/authorization/presentation/bloc/authorization/a import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; class _Header extends StatelessWidget { const _Header(); From 0526e595c9b2c737047f5a715d20533927a9b180 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:27:09 +0200 Subject: [PATCH 149/475] Move PoemCard to another folder --- .../presentation/pages/home/poems_feed.dart | 2 +- .../pages/poem_view/poem_view.dart | 107 +++++++++++++----- .../widgets/{ => poems_feed}/poem_card.dart | 0 3 files changed, 77 insertions(+), 32 deletions(-) rename lib/features/poems_feed/presentation/widgets/{ => poems_feed}/poem_card.dart (100%) diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 8d0b829..1886f88 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -8,7 +8,7 @@ import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_card.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart'; class PoemsFeedPage extends StatelessWidget { const PoemsFeedPage({super.key}); diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 8d84510..df9e351 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -3,11 +3,80 @@ import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +class PoemTitle extends StatelessWidget { + final String title; + + const PoemTitle({super.key, required this.title}); + + @override + Widget build(BuildContext context) { + return Text( + title, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ); + } +} + +class PoemAuthor extends StatelessWidget { + final String author; + + const PoemAuthor({super.key, required this.author}); + + @override + Widget build(BuildContext context) { + return Text( + author, + style: const TextStyle( + fontSize: 22, + fontStyle: FontStyle.italic, + ), + ); + } +} + +class PoemContent extends StatelessWidget { + final String text; + + const PoemContent({super.key, required this.text}); + + @override + Widget build(BuildContext context) { + return Text( + text, + style: const TextStyle( + fontSize: 16, + ), + ); + } +} + +class PoemLineCount extends StatelessWidget { + final int lineCount; + + const PoemLineCount({super.key, required this.lineCount}); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.centerRight, + child: Text( + '$lineCount lines', + style: const TextStyle( + fontStyle: FontStyle.italic, + ), + ), + ); + } +} + class PoemViewPage extends StatelessWidget { const PoemViewPage({super.key}); @override - Widget build(BuildContext context){ + Widget build(BuildContext context) { final poemEntity = (ModalRoute.of(context)?.settings.arguments ?? const PoemEntity()) as PoemEntity; return Scaffold( @@ -20,43 +89,19 @@ class PoemViewPage extends StatelessWidget { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - Text( - poemEntity.title ?? '', - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), + PoemTitle(title: poemEntity.title ?? ''), + const CustomSpacer(heightFactor: 0.02), + PoemAuthor(author: poemEntity.author ?? ''), const CustomSpacer(heightFactor: 0.02), - Text( - poemEntity.author ?? '', - style: const TextStyle( - fontSize: 22, - fontStyle: FontStyle.italic - ), - ), + PoemContent(text: poemEntity.text ?? ''), const CustomSpacer(heightFactor: 0.02), - Text( - poemEntity.text ?? '', - style: const TextStyle( - fontSize: 16, - ), - ), + PoemLineCount(lineCount: poemEntity.linecount ?? 0), const CustomSpacer(heightFactor: 0.02), - Align( - alignment: Alignment.centerRight, - child: Text( - '${poemEntity.linecount ?? 0} lines', - style: const TextStyle( - fontStyle: FontStyle.italic - ), - ), - ), ], ), ), ), - ) + ), ); } } diff --git a/lib/features/poems_feed/presentation/widgets/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poem_card.dart rename to lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart From 6a2ca59023979c844d90cd04ea2f3af09bd71497 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:30:03 +0200 Subject: [PATCH 150/475] Decompoze PoemViewPage --- .../pages/poem_view/poem_view.dart | 73 +------------------ .../widgets/poem_view/poem_author.dart | 16 ++++ .../widgets/poem_view/poem_content.dart | 15 ++++ .../widgets/poem_view/poem_line_count.dart | 18 +++++ .../widgets/poem_view/poem_title.dart | 16 ++++ 5 files changed, 69 insertions(+), 69 deletions(-) create mode 100644 lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart create mode 100644 lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart create mode 100644 lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart create mode 100644 lib/features/poems_feed/presentation/widgets/poem_view/poem_title.dart diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index df9e351..1446f55 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -2,75 +2,10 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; - -class PoemTitle extends StatelessWidget { - final String title; - - const PoemTitle({super.key, required this.title}); - - @override - Widget build(BuildContext context) { - return Text( - title, - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ); - } -} - -class PoemAuthor extends StatelessWidget { - final String author; - - const PoemAuthor({super.key, required this.author}); - - @override - Widget build(BuildContext context) { - return Text( - author, - style: const TextStyle( - fontSize: 22, - fontStyle: FontStyle.italic, - ), - ); - } -} - -class PoemContent extends StatelessWidget { - final String text; - - const PoemContent({super.key, required this.text}); - - @override - Widget build(BuildContext context) { - return Text( - text, - style: const TextStyle( - fontSize: 16, - ), - ); - } -} - -class PoemLineCount extends StatelessWidget { - final int lineCount; - - const PoemLineCount({super.key, required this.lineCount}); - - @override - Widget build(BuildContext context) { - return Align( - alignment: Alignment.centerRight, - child: Text( - '$lineCount lines', - style: const TextStyle( - fontStyle: FontStyle.italic, - ), - ), - ); - } -} +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; class PoemViewPage extends StatelessWidget { const PoemViewPage({super.key}); diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart b/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart new file mode 100644 index 0000000..2d98a36 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class PoemAuthor extends StatelessWidget { + const PoemAuthor({super.key, required this.author}); + + final String author; + + @override + Widget build(BuildContext context) => Text( + author, + style: const TextStyle( + fontSize: 22, + fontStyle: FontStyle.italic, + ), + ); +} \ No newline at end of file diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart b/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart new file mode 100644 index 0000000..2c98be4 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class PoemContent extends StatelessWidget { + const PoemContent({super.key, required this.text}); + + final String text; + + @override + Widget build(BuildContext context) => Text( + text, + style: const TextStyle( + fontSize: 16, + ), + ); +} \ No newline at end of file diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart b/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart new file mode 100644 index 0000000..e88edb4 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class PoemLineCount extends StatelessWidget { + const PoemLineCount({super.key, required this.lineCount}); + + final int lineCount; + + @override + Widget build(BuildContext context) => Align( + alignment: Alignment.centerRight, + child: Text( + '$lineCount lines', + style: const TextStyle( + fontStyle: FontStyle.italic, + ), + ), + ); +} \ No newline at end of file diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_title.dart b/lib/features/poems_feed/presentation/widgets/poem_view/poem_title.dart new file mode 100644 index 0000000..135e592 --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/poem_view/poem_title.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class PoemTitle extends StatelessWidget { + const PoemTitle({super.key, required this.title}); + + final String title; + + @override + Widget build(BuildContext context) => Text( + title, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ); +} From 8717f6ba63803f56999a0558afd494f579014061 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:50:55 +0200 Subject: [PATCH 151/475] Add refresh button --- .../presentation/widgets/AppBar/app_bar.dart | 2 ++ .../AppBar/buttons/refresh_button.dart | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/AppBar/app_bar.dart index d2a08d9..898f463 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/AppBar/app_bar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -9,6 +10,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) => AppBar( actions: const [ + RefreshButton(), SettingsButton(), ], title: Text( diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart new file mode 100644 index 0000000..d2ac25a --- /dev/null +++ b/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; + +class RefreshButton extends StatefulWidget { + const RefreshButton({super.key}); + + @override + State createState() => _RefreshButtonState(); +} + +class _RefreshButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { + @override + Widget build(BuildContext context) => RotationTransition( + turns: rotationAnimation, + child: IconButton( + onPressed: (){ + playAnimation(); + + BlocProvider.of(context).add(const GetInitialPoemsEvent()); + }, + icon: const Icon(Icons.refresh), + ), + ); + + @override + void dispose() { + rotationController.dispose(); + super.dispose(); + } +} From 6e2778b645cf27d07a5d1c4a4149a1e67fcac5df Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:12:12 +0200 Subject: [PATCH 152/475] Add bloc listener --- .../AppBar/buttons/refresh_button.dart | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart index d2ac25a..f004757 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; class RefreshButton extends StatefulWidget { const RefreshButton({super.key}); @@ -13,15 +14,19 @@ class RefreshButton extends StatefulWidget { class _RefreshButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { @override - Widget build(BuildContext context) => RotationTransition( - turns: rotationAnimation, - child: IconButton( - onPressed: (){ - playAnimation(); + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) => RotationTransition( + turns: rotationAnimation, + child: IconButton( + onPressed: state is RemotePoemLoading + ? null + : (){ + playAnimation(); - BlocProvider.of(context).add(const GetInitialPoemsEvent()); - }, - icon: const Icon(Icons.refresh), + BlocProvider.of(context).add(const GetInitialPoemsEvent()); + }, + icon: const Icon(Icons.refresh), + ), ), ); From d59110a0995be18ceaec564817f1b5d311afae8b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:39:07 +0200 Subject: [PATCH 153/475] Add bloc subscription --- .../widgets/drawer/custom_search_buttond.dart | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart index 0f8e1c2..4097e13 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; class CustomSearchButton extends StatelessWidget { const CustomSearchButton({super.key, required this.onPressed}); @@ -6,25 +9,29 @@ class CustomSearchButton extends StatelessWidget { final void Function() onPressed; @override - Widget build(BuildContext context) => LayoutBuilder( - builder: (context, constraints) { - final width = constraints.maxWidth / 1.35; - - return Align( - child: SizedBox( - width: width, - child: FilledButton( - onPressed: onPressed, - child: const Padding( - padding: EdgeInsets.symmetric(vertical: 12.5), - child: Text( - 'Search', - style: TextStyle(fontSize: 16), + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) => LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth / 1.35; + + return Align( + child: state is RemotePoemLoading + ? const CircularProgressIndicator() + : SizedBox( + width: width, + child: FilledButton( + onPressed: onPressed, + child: const Padding( + padding: EdgeInsets.symmetric(vertical: 12.5), + child: Text( + 'Search', + style: TextStyle(fontSize: 16), + ), ), ), ), - ), - ); - }, + ); + }, + ), ); } From 00239a30715d5bbb597bbc83ff5604f42e4700b5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:14:56 +0200 Subject: [PATCH 154/475] Create saved poems page template --- lib/core/constants/navigator_constants.dart | 1 + lib/features/application/poetlum_app.dart | 2 ++ .../presentation/pages/home/poems_feed.dart | 2 +- .../presentation/pages/saved_poems_page.dart | 12 ++++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 lib/features/saved_poems/presentation/pages/saved_poems_page.dart diff --git a/lib/core/constants/navigator_constants.dart b/lib/core/constants/navigator_constants.dart index d9511fb..f0bdaef 100644 --- a/lib/core/constants/navigator_constants.dart +++ b/lib/core/constants/navigator_constants.dart @@ -3,3 +3,4 @@ const String registerPageConstant = '/register'; const String loginPageConstant = '/login'; const String poemsFeedPageConstant = '/poems_feed'; const String poemViewPageConstant = '/poem_view'; +const String savedPoemsPageConstant = '/saved_poems'; diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 3a4489c..87be365 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -8,6 +8,7 @@ import 'package:poetlum/features/authorization/presentation/pages/registration/r import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; +import 'package:poetlum/features/saved_poems/presentation/pages/saved_poems_page.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @@ -24,6 +25,7 @@ class PoetlumApp extends StatelessWidget { loginPageConstant: (_) => const LoginPage(), poemsFeedPageConstant: (_) => const PoemsFeedPage(), poemViewPageConstant:(_) => const PoemViewPage(), + savedPoemsPageConstant:(_) => const SavedPoemsPage(), }, ), ); diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 1886f88..3f06f1b 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -22,7 +22,7 @@ class PoemsFeedPage extends StatelessWidget { body: _buildBody(context), bottomNavigationBar: GNav( onTabChange: (value) { - + }, gap: 12, tabs: const [ diff --git a/lib/features/saved_poems/presentation/pages/saved_poems_page.dart b/lib/features/saved_poems/presentation/pages/saved_poems_page.dart new file mode 100644 index 0000000..5a11de5 --- /dev/null +++ b/lib/features/saved_poems/presentation/pages/saved_poems_page.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; + +class SavedPoemsPage extends StatelessWidget { + const SavedPoemsPage({super.key}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: CustomAppBar(title: 'Poetlum') + , + ); +} From 6a4b0b630d26e53e455aa1187092e67c47d73182 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:44:10 +0200 Subject: [PATCH 155/475] Replace helper widget --- lib/features/application/poetlum_app.dart | 27 ++++++++++------------- lib/main.dart | 5 ++++- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 87be365..da9a4d4 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -5,7 +5,6 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/auth_wrapper.dart'; import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; -import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; import 'package:poetlum/features/saved_poems/presentation/pages/saved_poems_page.dart'; @@ -14,19 +13,17 @@ class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @override - Widget build(BuildContext context) => InitBlocs( - child: GetMaterialApp( - title: 'Poetlum', - theme: theme(), - initialRoute: authWrapperPageConstant, - routes: { - authWrapperPageConstant:(_) => const AuthWrapper(), - registerPageConstant: (_) => const RegistrationPage(), - loginPageConstant: (_) => const LoginPage(), - poemsFeedPageConstant: (_) => const PoemsFeedPage(), - poemViewPageConstant:(_) => const PoemViewPage(), - savedPoemsPageConstant:(_) => const SavedPoemsPage(), - }, - ), + Widget build(BuildContext context) => GetMaterialApp( + title: 'Poetlum', + theme: theme(), + initialRoute: authWrapperPageConstant, + routes: { + authWrapperPageConstant:(_) => const AuthWrapper(), + registerPageConstant: (_) => const RegistrationPage(), + loginPageConstant: (_) => const LoginPage(), + poemsFeedPageConstant: (_) => const PoemsFeedPage(), + poemViewPageConstant:(_) => const PoemViewPage(), + savedPoemsPageConstant:(_) => const SavedPoemsPage(), + }, ); } diff --git a/lib/main.dart b/lib/main.dart index 94f2d64..11ba8a9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:poetlum/features/application/poetlum_app.dart'; import 'package:poetlum/features/dependency_injection/presentation/widgets/init_dependencies.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; +import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; import 'package:poetlum/firebase_options.dart'; void main() async { @@ -13,7 +14,9 @@ void main() async { child: InitFirebaseWidget( options: DefaultFirebaseOptions.currentPlatform, child: const InitCrashlyticsWidget( - child: PoetlumApp(), + child: InitBlocs( + child: PoetlumApp() + ), ), ), ), From f69b69afef32da4bfc527c57e12d02569acc9f1c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:45:26 +0200 Subject: [PATCH 156/475] Move AuthWrapper to another folder --- lib/features/application/poetlum_app.dart | 2 +- .../presentation/{widgets => pages}/auth_wrapper.dart | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/features/application/presentation/{widgets => pages}/auth_wrapper.dart (100%) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index da9a4d4..d42e0ff 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; -import 'package:poetlum/features/application/presentation/widgets/auth_wrapper.dart'; +import 'package:poetlum/features/application/presentation/pages/auth_wrapper.dart'; import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; diff --git a/lib/features/application/presentation/widgets/auth_wrapper.dart b/lib/features/application/presentation/pages/auth_wrapper.dart similarity index 100% rename from lib/features/application/presentation/widgets/auth_wrapper.dart rename to lib/features/application/presentation/pages/auth_wrapper.dart From 78cce096ebfffd938c420114284db93a641235f7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:01:36 +0200 Subject: [PATCH 157/475] Rename app bar folder --- .../presentation/widgets/{AppBar => app_bar}/app_bar.dart | 4 ++-- .../widgets/{AppBar => app_bar}/buttons/refresh_button.dart | 2 +- .../{AppBar => app_bar}/buttons/rotating_button_mixin.dart | 0 .../widgets/{AppBar => app_bar}/buttons/settings_button.dart | 2 +- .../poems_feed/presentation/pages/home/poems_feed.dart | 2 +- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 +- .../saved_poems/presentation/pages/saved_poems_page.dart | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename lib/features/application/presentation/widgets/{AppBar => app_bar}/app_bar.dart (89%) rename lib/features/application/presentation/widgets/{AppBar => app_bar}/buttons/refresh_button.dart (96%) rename lib/features/application/presentation/widgets/{AppBar => app_bar}/buttons/rotating_button_mixin.dart (100%) rename lib/features/application/presentation/widgets/{AppBar => app_bar}/buttons/settings_button.dart (93%) diff --git a/lib/features/application/presentation/widgets/AppBar/app_bar.dart b/lib/features/application/presentation/widgets/app_bar/app_bar.dart similarity index 89% rename from lib/features/application/presentation/widgets/AppBar/app_bar.dart rename to lib/features/application/presentation/widgets/app_bar/app_bar.dart index 898f463..e8e7ea8 100644 --- a/lib/features/application/presentation/widgets/AppBar/app_bar.dart +++ b/lib/features/application/presentation/widgets/app_bar/app_bar.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/settings_button.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { const CustomAppBar({super.key, required this.title}); diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart similarity index 96% rename from lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart rename to lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart index f004757..fc39334 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart b/lib/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart similarity index 100% rename from lib/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart rename to lib/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart diff --git a/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart similarity index 93% rename from lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart rename to lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index ada8d67..2b6068d 100644 --- a/lib/features/application/presentation/widgets/AppBar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; class SettingsButton extends StatefulWidget { const SettingsButton({super.key}); diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart index 3f06f1b..a194a12 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/pages/home/poems_feed.dart @@ -2,7 +2,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 1446f55..3ab81e4 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poems_page.dart b/lib/features/saved_poems/presentation/pages/saved_poems_page.dart index 5a11de5..654344d 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poems_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poems_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/AppBar/app_bar.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; class SavedPoemsPage extends StatelessWidget { const SavedPoemsPage({super.key}); From f867408cc15cbaebbec659d12471d7e500e2efa6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:17:20 +0200 Subject: [PATCH 158/475] Refactor code to use screens --- lib/core/constants/navigator_constants.dart | 3 +- lib/features/application/poetlum_app.dart | 6 +- .../presentation/pages/auth_wrapper.dart | 4 +- .../presentation/pages/screens_wrapper.dart | 41 ++++++++++++ .../presentation/pages/login/login_page.dart | 2 +- .../pages/registration/registration_page.dart | 2 +- .../poems_feed_screen.dart} | 63 ++++++++++++++++--- .../presentation/pages/saved_poems_page.dart | 12 ---- .../screens/saved_poems_screen.dart | 8 +++ 9 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 lib/features/application/presentation/pages/screens_wrapper.dart rename lib/features/poems_feed/presentation/{pages/home/poems_feed.dart => screens/poems_feed_screen.dart} (60%) delete mode 100644 lib/features/saved_poems/presentation/pages/saved_poems_page.dart create mode 100644 lib/features/saved_poems/presentation/screens/saved_poems_screen.dart diff --git a/lib/core/constants/navigator_constants.dart b/lib/core/constants/navigator_constants.dart index f0bdaef..90d5ca0 100644 --- a/lib/core/constants/navigator_constants.dart +++ b/lib/core/constants/navigator_constants.dart @@ -1,6 +1,5 @@ const String authWrapperPageConstant = '/auth_wrapper'; const String registerPageConstant = '/register'; const String loginPageConstant = '/login'; -const String poemsFeedPageConstant = '/poems_feed'; +const String screensWrapperPageConstant = '/screen_wrapper'; const String poemViewPageConstant = '/poem_view'; -const String savedPoemsPageConstant = '/saved_poems'; diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index d42e0ff..5259918 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -3,11 +3,10 @@ import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/application/presentation/pages/auth_wrapper.dart'; +import 'package:poetlum/features/application/presentation/pages/screens_wrapper.dart'; import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; -import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; -import 'package:poetlum/features/saved_poems/presentation/pages/saved_poems_page.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @@ -21,9 +20,8 @@ class PoetlumApp extends StatelessWidget { authWrapperPageConstant:(_) => const AuthWrapper(), registerPageConstant: (_) => const RegistrationPage(), loginPageConstant: (_) => const LoginPage(), - poemsFeedPageConstant: (_) => const PoemsFeedPage(), + screensWrapperPageConstant: (_) => const ScreensWrapper(), poemViewPageConstant:(_) => const PoemViewPage(), - savedPoemsPageConstant:(_) => const SavedPoemsPage(), }, ); } diff --git a/lib/features/application/presentation/pages/auth_wrapper.dart b/lib/features/application/presentation/pages/auth_wrapper.dart index db7b2fe..177b492 100644 --- a/lib/features/application/presentation/pages/auth_wrapper.dart +++ b/lib/features/application/presentation/pages/auth_wrapper.dart @@ -1,9 +1,9 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/application/presentation/pages/screens_wrapper.dart'; import 'package:poetlum/features/authorization/domain/repository/auth_repository.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; -import 'package:poetlum/features/poems_feed/presentation/pages/home/poems_feed.dart'; class AuthWrapper extends StatelessWidget { const AuthWrapper({super.key}); @@ -20,7 +20,7 @@ class AuthWrapper extends StatelessWidget { if (user == null) { return const RegistrationPage(); } - return const PoemsFeedPage(); + return const ScreensWrapper(); } return const Scaffold( diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart new file mode 100644 index 0000000..db2fad6 --- /dev/null +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -0,0 +1,41 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:google_nav_bar/google_nav_bar.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; +import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; +import 'package:poetlum/features/saved_poems/presentation/screens/saved_poems_screen.dart'; + +class ScreensWrapper extends StatefulWidget { + const ScreensWrapper({super.key}); + + @override + State createState() => _ScreensWrapperState(); +} + +class _ScreensWrapperState extends State { + int screenIndex = 0; + + final screens = const [ + PoemsFeedScreen(), + SavedPoemsScreen(), + ]; + + @override + Widget build(BuildContext context) => Scaffold( + appBar: const CustomAppBar( + title: 'Poetlum', + ), + drawer: CustomDrawer(UserRepositoryImpl(FirebaseAuth.instance)), + body: screens[screenIndex], + bottomNavigationBar: GNav( + onTabChange: (value) => setState(() => screenIndex = value), + gap: 12, + tabs: const [ + GButton(icon: Icons.menu, text: 'Menu'), + GButton(icon: Icons.bookmark_outline_rounded, text: 'Saved poems'), + ], + ), + ); +} diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index e47794e..1b8c779 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -63,7 +63,7 @@ class _Form extends StatelessWidget { password: state.passwordValidationState.value, ); }, - navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, poemsFeedPageConstant, (route) => false), + navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, screensWrapperPageConstant, (route) => false), ), ], ), diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index daf7685..c82090f 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -67,7 +67,7 @@ class _Form extends StatelessWidget { email: state.emailValidationState.value, password: state.passwordValidationState.value, ), - navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, poemsFeedPageConstant, (route) => false), + navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, screensWrapperPageConstant, (route) => false), ), ], ), diff --git a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart similarity index 60% rename from lib/features/poems_feed/presentation/pages/home/poems_feed.dart rename to lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index a194a12..da4811e 100644 --- a/lib/features/poems_feed/presentation/pages/home/poems_feed.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -1,17 +1,64 @@ -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:google_nav_bar/google_nav_bar.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; -import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart'; -class PoemsFeedPage extends StatelessWidget { - const PoemsFeedPage({super.key}); +class PoemsFeedScreen extends StatelessWidget { + const PoemsFeedScreen({super.key}); + + @override + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) { + if(state is RemotePoemLoading){ + return const Center(child: CircularProgressIndicator(),); + } + + if(state is RemotePoemError){ + return _buildErrorBody(state.message, context); + } + + if(state is RemotePoemDone){ + return ListView.builder( + itemCount: state.poems!.length, + itemBuilder: (__, index) => PoemCard( + poemEntity: state.poems![index], + ), + ); + } + + return const SizedBox(); + }, + ); + + Widget _buildErrorBody(String error, BuildContext context) => Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Text( + error, + style: const TextStyle( + fontSize: 16, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: IconButton.filledTonal( + onPressed: () => BlocProvider.of(context).add(const GetInitialPoemsEvent()), + icon: const Icon(Icons.refresh), + ), + ), + ], + ), + ); +} + +/*class ScreensWrapper extends StatelessWidget { + const ScreensWrapper({super.key}); @override Widget build(BuildContext context) => Scaffold( @@ -78,4 +125,4 @@ class PoemsFeedPage extends StatelessWidget { ], ), ); -} +}*/ diff --git a/lib/features/saved_poems/presentation/pages/saved_poems_page.dart b/lib/features/saved_poems/presentation/pages/saved_poems_page.dart deleted file mode 100644 index 654344d..0000000 --- a/lib/features/saved_poems/presentation/pages/saved_poems_page.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; - -class SavedPoemsPage extends StatelessWidget { - const SavedPoemsPage({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: CustomAppBar(title: 'Poetlum') - , - ); -} diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart new file mode 100644 index 0000000..0080621 --- /dev/null +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class SavedPoemsScreen extends StatelessWidget { + const SavedPoemsScreen({super.key}); + + @override + Widget build(BuildContext context) => Text('Hey, I\'m a placeholder :D'); +} From 1b05d39aaaec2a2c70acae3685c5f61b15908bb0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:28:58 +0200 Subject: [PATCH 159/475] Install animations package --- pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index d0c33cb..75d6536 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.0" + animations: + dependency: "direct main" + description: + name: animations + sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70 + url: "https://pub.dev" + source: hosted + version: "2.0.8" archive: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 33c9238..5b724f1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: email_validator: ^2.1.17 fluttertoast: ^8.0.9 google_nav_bar: ^5.0.6 + animations: ^2.0.8 dev_dependencies: flutter_test: From e230a93a88e0fcb9590163e489c8bd47add7bade Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:29:06 +0200 Subject: [PATCH 160/475] Add screens transition animation --- .../presentation/pages/screens_wrapper.dart | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index db2fad6..f6e356b 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -1,3 +1,4 @@ +import 'package:animations/animations.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; @@ -28,7 +29,19 @@ class _ScreensWrapperState extends State { title: 'Poetlum', ), drawer: CustomDrawer(UserRepositoryImpl(FirebaseAuth.instance)), - body: screens[screenIndex], + body: PageTransitionSwitcher( + child: screens[screenIndex], + transitionBuilder: ( + child, + primaryAnimation, + secondaryAnimation, + ) => + FadeThroughTransition( + animation: primaryAnimation, + secondaryAnimation: secondaryAnimation, + child: child, + ), + ), bottomNavigationBar: GNav( onTabChange: (value) => setState(() => screenIndex = value), gap: 12, From add182fdbfc4135f48d6a35d1cfd22a9c2f744e6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:25:22 +0200 Subject: [PATCH 161/475] Create firebase api service --- .../remote/firebase_api_service.dart | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart new file mode 100644 index 0000000..2ae5b34 --- /dev/null +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -0,0 +1,29 @@ +import 'package:firebase_database/firebase_database.dart'; +import 'package:poetlum/features/poems_feed/data/models/poem.dart'; + +abstract class FirebaseDatabaseService{ + Future?> getUserPoems(String userId); +} + +class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { + @override + Future?> getUserPoems(String userId) async { + final ref = FirebaseDatabase.instance.ref('$userId/poems'); + final snapshot = await ref.get(); + + if (snapshot.exists) { + final poems = []; + + + for (var child in snapshot.children) { + final poemData = Map.from(child.value as Map); + + poems.add(PoemModel.fromFirebase(poemData)); + } + + return poems; + } else { + return null; + } + } +} From d74066de59aba175b72d9be7944146cd8f9c60cb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:26:10 +0200 Subject: [PATCH 162/475] Create FRTDB repository --- .../data/repository/firebase_db_repository_impl.dart | 12 ++++++++++++ .../domain/repository/firebase_db_repository.dart | 5 +++++ 2 files changed, 17 insertions(+) create mode 100644 lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart create mode 100644 lib/features/saved_poems/domain/repository/firebase_db_repository.dart diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart new file mode 100644 index 0000000..262c29b --- /dev/null +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -0,0 +1,12 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; + +class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ + FirebaseDatabaseRepositoryImpl(this._databaseService); + + final FirebaseDatabaseService _databaseService; + + @override + Future?> getUserPoems(String userId) async => _databaseService.getUserPoems(userId); +} diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart new file mode 100644 index 0000000..9be683f --- /dev/null +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -0,0 +1,5 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +abstract class FirebaseDatabaseRepository { + Future?> getUserPoems(String userId); +} From df8ace06b3309cbd8c26160369c0055856c5ca86 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:26:26 +0200 Subject: [PATCH 163/475] Add fromFirebase constructor --- lib/features/poems_feed/data/models/poem.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/features/poems_feed/data/models/poem.dart b/lib/features/poems_feed/data/models/poem.dart index 6e275b7..7e72f05 100644 --- a/lib/features/poems_feed/data/models/poem.dart +++ b/lib/features/poems_feed/data/models/poem.dart @@ -14,4 +14,11 @@ class PoemModel extends PoemEntity { text: List.from(json['lines'] ?? []).join('\n'), linecount: json['linecount'] != null ? int.parse(json['linecount'].toString()) : 0, ); + + factory PoemModel.fromFirebase(Map json) => PoemModel( + title: json['title'] ?? '', + author: json['author'] ?? '', + text: json['text'] ?? '', + linecount: json['linecount'] != null ? int.parse(json['linecount'].toString()) : 0, + ); } From 4561f64d30d3371755603f5374950bb7ac313298 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:26:51 +0200 Subject: [PATCH 164/475] Create use case for poems fetching --- .../domain/usecases/get_user_poems_usecase.dart | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/get_user_poems_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/get_user_poems_usecase.dart b/lib/features/saved_poems/domain/usecases/get_user_poems_usecase.dart new file mode 100644 index 0000000..967a556 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/get_user_poems_usecase.dart @@ -0,0 +1,17 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; + +class GetUserPoemsUseCase implements UseCase?, String>{ + GetUserPoemsUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future?> call({String? params}) async { + if(params != null){ + return _databaseRepository.getUserPoems(params); + } + return null; + } +} From 5ad9c333e3c352e013ba6d71f1d6bcb435829b0e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:27:03 +0200 Subject: [PATCH 165/475] Create FRTDB cubit --- .../presentation/bloc/firebase_database_cubit.dart | 14 ++++++++++++++ .../presentation/bloc/firebase_database_state.dart | 0 2 files changed, 14 insertions(+) create mode 100644 lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart create mode 100644 lib/features/saved_poems/presentation/bloc/firebase_database_state.dart diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart new file mode 100644 index 0000000..151fcf4 --- /dev/null +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -0,0 +1,14 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; + +class FirebaseDatabaseCubit extends Cubit { + FirebaseDatabaseCubit(this._getUserPoemsUseCase) : super(null); + + final GetUserPoemsUseCase _getUserPoemsUseCase; + + Future getUserPoems(String userId) async{ + await _getUserPoemsUseCase( + params: userId, + ); + } +} diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart new file mode 100644 index 0000000..e69de29 From dc9eea226ec211b87a092a16fb6f5ea897ff32ce Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:27:10 +0200 Subject: [PATCH 166/475] Init cubit --- lib/features/multi_bloc_provider/presentation/init_blocs.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/multi_bloc_provider/presentation/init_blocs.dart b/lib/features/multi_bloc_provider/presentation/init_blocs.dart index aa756f0..893ab13 100644 --- a/lib/features/multi_bloc_provider/presentation/init_blocs.dart +++ b/lib/features/multi_bloc_provider/presentation/init_blocs.dart @@ -5,6 +5,7 @@ import 'package:poetlum/features/authorization/presentation/bloc/authorization/a import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class InitBlocs extends StatelessWidget { const InitBlocs({super.key, required this.child}); @@ -18,6 +19,7 @@ class InitBlocs extends StatelessWidget { BlocProvider(create:(context) => getIt(),), BlocProvider(create:(context) => getIt()), BlocProvider(create:(context) => getIt()), + BlocProvider(create: (context) => getIt()), ], child: child, ); From 1c63eaa3883f9d2936af1589a3b61df03eb972de Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:27:19 +0200 Subject: [PATCH 167/475] Init the stuff in the DI --- lib/core/dependency_injection.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 424f997..cda8139 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -16,6 +16,11 @@ import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.da import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/realtime_database/domain/entities/database_manager.dart'; +import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; +import 'package:poetlum/features/saved_poems/data/repository/firebase_db_repository_impl.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; GetIt getIt = GetIt.instance; @@ -30,17 +35,20 @@ void initializeDependencies() { // API Service ..registerSingleton(PoemApiService(getIt())) ..registerSingleton(FirebaseServiceImpl()) + ..registerSingleton(FirebaseDatabaseServiceImpl()) // Repository ..registerSingleton(AuthenticationRepositoryImpl()) ..registerSingleton(PoemRepositoryImpl(getIt())) ..registerSingleton(FirebaseRepositoryImpl(getIt())) + ..registerSingleton(FirebaseDatabaseRepositoryImpl(getIt())) // Usecase ..registerSingleton(GetInitialPoemsUseCase(getIt())) ..registerSingleton(GetPoemsUseCase(getIt())) ..registerSingleton(RegisterUserUseCase(getIt())) ..registerSingleton(LoginUserUseCase(getIt())) + ..registerSingleton(GetUserPoemsUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -50,6 +58,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) + ..registerFactory(() => FirebaseDatabaseCubit(getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), @@ -57,6 +66,6 @@ void initializeDependencies() { ),) ..registerFactory(() => LoginFormValidationCubit( emailValidator: getIt(), - passwordValidator: getIt() + passwordValidator: getIt(), ),); } From 225f1eb52f50072b7481cab5b44856faf2bb9bea Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:18:18 +0200 Subject: [PATCH 168/475] Add state --- .../bloc/firebase_database_state.dart | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart index e69de29..d368c69 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart @@ -0,0 +1,22 @@ +import 'package:equatable/equatable.dart'; + +enum FirebaseDatabaseStatus{initial, submitting, success} + +class FirebaseDatabaseState extends Equatable{ + const FirebaseDatabaseState({ + this.status = FirebaseDatabaseStatus.initial, + }); + + final FirebaseDatabaseStatus status; + + FirebaseDatabaseState copyWith({ + FirebaseDatabaseStatus? status, + }) => FirebaseDatabaseState( + status: status ?? this.status, + ); + + @override + List get props => [ + status, + ]; +} From da83befd54954c18f848c30db9a5cf84c22400f7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:18:24 +0200 Subject: [PATCH 169/475] Refactor cubit --- .../bloc/firebase_database_cubit.dart | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 151fcf4..33f90b7 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -1,14 +1,22 @@ import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase) : super(null); +class FirebaseDatabaseCubit extends Cubit { + FirebaseDatabaseCubit(this._getUserPoemsUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; - Future getUserPoems(String userId) async{ - await _getUserPoemsUseCase( + Future?> getUserPoems(String userId) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + final poems = await _getUserPoemsUseCase( params: userId, ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + + return poems; } } From 2496ea4737d13d54db546014ae07cafc3f59f0d4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:18:38 +0200 Subject: [PATCH 170/475] Add new cubit support --- .../screens/saved_poems_screen.dart | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 0080621..9ab09d3 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,8 +1,43 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -class SavedPoemsScreen extends StatelessWidget { +class SavedPoemsScreen extends StatefulWidget { const SavedPoemsScreen({super.key}); @override - Widget build(BuildContext context) => Text('Hey, I\'m a placeholder :D'); + State createState() => _SavedPoemsScreenState(); +} + +class _SavedPoemsScreenState extends State { + @override + void initState() { + super.initState(); + initPoems(); + } + + Future initPoems() async{ + final poems = await context.read().getUserPoems(FirebaseAuth.instance.currentUser!.uid); + print(poems); + } + + @override + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) { + if(state.status == FirebaseDatabaseStatus.submitting){ + return const CircularProgressIndicator(); + } else{ + return Column( + children: [ + TextButton(onPressed: () async{ + final poems = await context.read().getUserPoems(FirebaseAuth.instance.currentUser!.uid); + print(poems); + }, child: Text('a')), + ], + ); + } + }, + ); } From 2475c79a0663adbccae5ad1e62beb91279f40349 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:38:27 +0200 Subject: [PATCH 171/475] Refactor firebase user model --- lib/features/poems_feed/data/models/firebase_user.dart | 1 + lib/features/poems_feed/domain/entities/firebase_user.dart | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/data/models/firebase_user.dart b/lib/features/poems_feed/data/models/firebase_user.dart index 8efddc3..1e68ec4 100644 --- a/lib/features/poems_feed/data/models/firebase_user.dart +++ b/lib/features/poems_feed/data/models/firebase_user.dart @@ -4,5 +4,6 @@ class FirebaserUserModel extends FirebaseUserEntity{ const FirebaserUserModel({ super.username = '', super.email = '', + super.userId = '', }); } diff --git a/lib/features/poems_feed/domain/entities/firebase_user.dart b/lib/features/poems_feed/domain/entities/firebase_user.dart index c04b67d..e4c701d 100644 --- a/lib/features/poems_feed/domain/entities/firebase_user.dart +++ b/lib/features/poems_feed/domain/entities/firebase_user.dart @@ -1,14 +1,16 @@ import 'package:equatable/equatable.dart'; class FirebaseUserEntity extends Equatable{ - const FirebaseUserEntity({this.username, this.email}); + const FirebaseUserEntity({this.username, this.email, this.userId}); final String? username; final String? email; + final String? userId; @override List get props => [ username, email, + userId ]; } From 792ab96f0fb0760a4abd22784ec4cb923f571609 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:38:36 +0200 Subject: [PATCH 172/475] Rework repository --- .../poems_feed/data/repository/user_repository_impl.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/poems_feed/data/repository/user_repository_impl.dart b/lib/features/poems_feed/data/repository/user_repository_impl.dart index c6bbc1c..2f6d05c 100644 --- a/lib/features/poems_feed/data/repository/user_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/user_repository_impl.dart @@ -11,6 +11,6 @@ class UserRepositoryImpl implements UserRepository{ FirebaserUserModel getCurrentUser() { final firebaseUser = _firebaseAuth.currentUser; - return FirebaserUserModel(username: firebaseUser?.displayName); + return FirebaserUserModel(username: firebaseUser?.displayName, email: firebaseUser?.email, userId: firebaseUser?.uid); } } From 7ce8e01926810268d947940ee7a644180adeac49 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:38:47 +0200 Subject: [PATCH 173/475] Use repository --- .../presentation/pages/screens_wrapper.dart | 6 +++--- .../presentation/screens/saved_poems_screen.dart | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index f6e356b..caa11b8 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -18,9 +18,9 @@ class ScreensWrapper extends StatefulWidget { class _ScreensWrapperState extends State { int screenIndex = 0; - final screens = const [ - PoemsFeedScreen(), - SavedPoemsScreen(), + final screens = [ + const PoemsFeedScreen(), + SavedPoemsScreen(UserRepositoryImpl(FirebaseAuth.instance)), ]; @override diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 9ab09d3..6e7fc08 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,11 +1,13 @@ -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class SavedPoemsScreen extends StatefulWidget { - const SavedPoemsScreen({super.key}); + const SavedPoemsScreen(this._userRepository, {super.key}); + + final UserRepository _userRepository; @override State createState() => _SavedPoemsScreenState(); @@ -19,7 +21,7 @@ class _SavedPoemsScreenState extends State { } Future initPoems() async{ - final poems = await context.read().getUserPoems(FirebaseAuth.instance.currentUser!.uid); + final poems = await context.read().getUserPoems(widget._userRepository.getCurrentUser().userId!); print(poems); } @@ -32,7 +34,7 @@ class _SavedPoemsScreenState extends State { return Column( children: [ TextButton(onPressed: () async{ - final poems = await context.read().getUserPoems(FirebaseAuth.instance.currentUser!.uid); + final poems = await context.read().getUserPoems(widget._userRepository.getCurrentUser().userId!); print(poems); }, child: Text('a')), ], From e1d8f91aee0f0817745bc5ca1db09bb837c23093 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:43:16 +0200 Subject: [PATCH 174/475] Init UserRepository in the DI --- lib/core/dependency_injection.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index cda8139..3aff6b4 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:get_it/get_it.dart'; import 'package:poetlum/features/authorization/data/data_sources/remote/firebase_service.dart'; import 'package:poetlum/features/authorization/data/repository/auth_repository_impl.dart'; @@ -12,7 +13,9 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; import 'package:poetlum/features/poems_feed/data/repository/poem_repository_impl.dart'; +import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/realtime_database/domain/entities/database_manager.dart'; @@ -42,6 +45,7 @@ void initializeDependencies() { ..registerSingleton(PoemRepositoryImpl(getIt())) ..registerSingleton(FirebaseRepositoryImpl(getIt())) ..registerSingleton(FirebaseDatabaseRepositoryImpl(getIt())) + ..registerSingleton(UserRepositoryImpl(FirebaseAuth.instance)) // Usecase ..registerSingleton(GetInitialPoemsUseCase(getIt())) From 915fe8bf483e534302d0cae02034a3323f4b6e0f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:43:26 +0200 Subject: [PATCH 175/475] Replace init deps widget --- lib/main.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 11ba8a9..61fbdba 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,12 +10,12 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( - InitDependencies( - child: InitFirebaseWidget( - options: DefaultFirebaseOptions.currentPlatform, - child: const InitCrashlyticsWidget( + InitFirebaseWidget( + options: DefaultFirebaseOptions.currentPlatform, + child: const InitDependencies( + child: InitCrashlyticsWidget( child: InitBlocs( - child: PoetlumApp() + child: PoetlumApp(), ), ), ), From 96d6b633d3a046a9294c87641fe9f4ff1aeb40dd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:43:58 +0200 Subject: [PATCH 176/475] Remove tight coupling --- .../application/presentation/pages/screens_wrapper.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index caa11b8..d384e81 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -1,9 +1,8 @@ import 'package:animations/animations.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; -import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/saved_poems/presentation/screens/saved_poems_screen.dart'; @@ -20,7 +19,7 @@ class _ScreensWrapperState extends State { final screens = [ const PoemsFeedScreen(), - SavedPoemsScreen(UserRepositoryImpl(FirebaseAuth.instance)), + SavedPoemsScreen(getIt()), ]; @override @@ -28,7 +27,7 @@ class _ScreensWrapperState extends State { appBar: const CustomAppBar( title: 'Poetlum', ), - drawer: CustomDrawer(UserRepositoryImpl(FirebaseAuth.instance)), + drawer: CustomDrawer(getIt()), body: PageTransitionSwitcher( child: screens[screenIndex], transitionBuilder: ( From faf52c4674afb22ec80609886be294afe5d87229 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:26:18 +0200 Subject: [PATCH 177/475] Add registration check --- lib/core/dependency_injection.dart | 78 +++++++++++++++--------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 3aff6b4..d81e389 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -28,48 +28,50 @@ import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database GetIt getIt = GetIt.instance; void initializeDependencies() { - getIt - // Database - ..registerSingleton(DatabaseManager()) + if(!GetIt.instance.isRegistered()){ + getIt + // Database + ..registerSingleton(DatabaseManager()) - // Dio - ..registerSingleton(Dio()) + // Dio + ..registerSingleton(Dio()) - // API Service - ..registerSingleton(PoemApiService(getIt())) - ..registerSingleton(FirebaseServiceImpl()) - ..registerSingleton(FirebaseDatabaseServiceImpl()) + // API Service + ..registerSingleton(PoemApiService(getIt())) + ..registerSingleton(FirebaseServiceImpl()) + ..registerSingleton(FirebaseDatabaseServiceImpl()) - // Repository - ..registerSingleton(AuthenticationRepositoryImpl()) - ..registerSingleton(PoemRepositoryImpl(getIt())) - ..registerSingleton(FirebaseRepositoryImpl(getIt())) - ..registerSingleton(FirebaseDatabaseRepositoryImpl(getIt())) - ..registerSingleton(UserRepositoryImpl(FirebaseAuth.instance)) + // Repository + ..registerSingleton(AuthenticationRepositoryImpl()) + ..registerSingleton(PoemRepositoryImpl(getIt())) + ..registerSingleton(FirebaseRepositoryImpl(getIt())) + ..registerSingleton(FirebaseDatabaseRepositoryImpl(getIt())) + ..registerSingleton(UserRepositoryImpl(FirebaseAuth.instance)) - // Usecase - ..registerSingleton(GetInitialPoemsUseCase(getIt())) - ..registerSingleton(GetPoemsUseCase(getIt())) - ..registerSingleton(RegisterUserUseCase(getIt())) - ..registerSingleton(LoginUserUseCase(getIt())) - ..registerSingleton(GetUserPoemsUseCase(getIt())) + // Usecase + ..registerSingleton(GetInitialPoemsUseCase(getIt())) + ..registerSingleton(GetPoemsUseCase(getIt())) + ..registerSingleton(RegisterUserUseCase(getIt())) + ..registerSingleton(LoginUserUseCase(getIt())) + ..registerSingleton(GetUserPoemsUseCase(getIt())) - // Validators - ..registerLazySingleton(() => UsernameValidator()) - ..registerLazySingleton(() => LocalEmailValidator()) - ..registerLazySingleton(() => PasswordValidator()) + // Validators + ..registerLazySingleton(() => UsernameValidator()) + ..registerLazySingleton(() => LocalEmailValidator()) + ..registerLazySingleton(() => PasswordValidator()) - // Bloc - ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) - ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => FirebaseDatabaseCubit(getIt())) - ..registerFactory(() => RegisterFormValidationCubit( - usernameValidator: getIt(), - emailValidator: getIt(), - passwordValidator: getIt(), - ),) - ..registerFactory(() => LoginFormValidationCubit( - emailValidator: getIt(), - passwordValidator: getIt(), - ),); + // Bloc + ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) + ..registerFactory(() => AuthCubit(getIt(), getIt())) + ..registerFactory(() => FirebaseDatabaseCubit(getIt())) + ..registerFactory(() => RegisterFormValidationCubit( + usernameValidator: getIt(), + emailValidator: getIt(), + passwordValidator: getIt(), + ),) + ..registerFactory(() => LoginFormValidationCubit( + emailValidator: getIt(), + passwordValidator: getIt(), + ),); + } } From 2e561212c82cc47f8de54dc4f169cb93ac3f5e4f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:51:56 +0200 Subject: [PATCH 178/475] Extend repository --- .../data/repository/firebase_db_repository_impl.dart | 4 ++++ .../saved_poems/domain/repository/firebase_db_repository.dart | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 262c29b..d4b6827 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -1,5 +1,6 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @@ -9,4 +10,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future?> getUserPoems(String userId) async => _databaseService.getUserPoems(userId); + + @override + Future?> getUserCollections(String userId) async => _databaseService.getUserCollections(userId); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 9be683f..5abedda 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -1,5 +1,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; abstract class FirebaseDatabaseRepository { Future?> getUserPoems(String userId); + Future?> getUserCollections(String userId); } From 251e6d56f9618466ad1571b904c44132eff3b165 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:52:11 +0200 Subject: [PATCH 179/475] Extend API service --- .../remote/firebase_api_service.dart | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 2ae5b34..157d874 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -1,8 +1,10 @@ import 'package:firebase_database/firebase_database.dart'; import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/features/saved_poems/data/models/collection.dart'; abstract class FirebaseDatabaseService{ Future?> getUserPoems(String userId); + Future?> getUserCollections(String userId); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -15,7 +17,7 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { final poems = []; - for (var child in snapshot.children) { + for (final child in snapshot.children) { final poemData = Map.from(child.value as Map); poems.add(PoemModel.fromFirebase(poemData)); @@ -26,4 +28,25 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { return null; } } + + @override + Future?> getUserCollections(String userId) async{ + final ref = FirebaseDatabase.instance.ref('$userId/collections'); + final snapshot = await ref.get(); + + if (snapshot.exists) { + final collections = []; + + + for (final child in snapshot.children) { + final collectionData = Map.from(child.value as Map); + + collections.add(CollectionModel.fromFirebase(collectionData)); + } + + return collections; + } else { + return null; + } + } } From 8bb684436467484acf88139dbe2374a2cc50720d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:52:24 +0200 Subject: [PATCH 180/475] Create Collection model and Entity --- .../saved_poems/data/models/collection.dart | 16 ++++++++++++++++ .../saved_poems/domain/entities/collection.dart | 15 +++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 lib/features/saved_poems/data/models/collection.dart create mode 100644 lib/features/saved_poems/domain/entities/collection.dart diff --git a/lib/features/saved_poems/data/models/collection.dart b/lib/features/saved_poems/data/models/collection.dart new file mode 100644 index 0000000..1091eb2 --- /dev/null +++ b/lib/features/saved_poems/data/models/collection.dart @@ -0,0 +1,16 @@ +import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; + +class CollectionModel extends CollectionEntity{ + const CollectionModel({ + super.name = '', + super.poems = const [], + }); + + factory CollectionModel.fromFirebase(Map json) => CollectionModel( + name: json['name'] ?? '', + poems: (json['poems'] as Map).entries.map( + (poem) => PoemModel.fromFirebase(poem.value), + ).toList(), + ); +} diff --git a/lib/features/saved_poems/domain/entities/collection.dart b/lib/features/saved_poems/domain/entities/collection.dart new file mode 100644 index 0000000..9221c7f --- /dev/null +++ b/lib/features/saved_poems/domain/entities/collection.dart @@ -0,0 +1,15 @@ +import 'package:equatable/equatable.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class CollectionEntity extends Equatable{ + const CollectionEntity({this.name, this.poems}); + + final String? name; + final List? poems; + + @override + List get props => [ + name, + poems, + ]; +} From e6dcd6e6264c81883cd42f5022cca2f80ccf4d60 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:52:47 +0200 Subject: [PATCH 181/475] Create user case --- .../usecases/get_user_collections_usecase.dart | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/get_user_collections_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/get_user_collections_usecase.dart b/lib/features/saved_poems/domain/usecases/get_user_collections_usecase.dart new file mode 100644 index 0000000..d966aeb --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/get_user_collections_usecase.dart @@ -0,0 +1,17 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; + +class GetUserCollectionsUseCase implements UseCase?, String>{ + GetUserCollectionsUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future?> call({String? params}) async { + if(params != null){ + return _databaseRepository.getUserCollections(params); + } + return null; + } +} From f35ede6d41fd9f392e4ef284559316116b5673fe Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:52:54 +0200 Subject: [PATCH 182/475] Register in the DI --- lib/core/dependency_injection.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index d81e389..7a5e6a5 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -22,6 +22,7 @@ import 'package:poetlum/features/realtime_database/domain/entities/database_mana import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; import 'package:poetlum/features/saved_poems/data/repository/firebase_db_repository_impl.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -54,6 +55,7 @@ void initializeDependencies() { ..registerSingleton(RegisterUserUseCase(getIt())) ..registerSingleton(LoginUserUseCase(getIt())) ..registerSingleton(GetUserPoemsUseCase(getIt())) + ..registerSingleton(GetUserCollectionsUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -63,7 +65,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => FirebaseDatabaseCubit(getIt())) + ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), From 8fac26694d6079408b13549275c47d8400725a01 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:52:59 +0200 Subject: [PATCH 183/475] Extend cubit --- .../bloc/firebase_database_cubit.dart | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 33f90b7..cab2fa1 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -1,12 +1,15 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; + final GetUserCollectionsUseCase _getUserCollectionsUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -19,4 +22,16 @@ class FirebaseDatabaseCubit extends Cubit { return poems; } + + Future?> getUserCollections(String userId) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + final collections = await _getUserCollectionsUseCase( + params: userId, + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + + return collections; + } } From 8404627dc0c330926836e116d23d5a19de6a20eb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:05:54 +0200 Subject: [PATCH 184/475] Fix fromFirebase constructor --- .../saved_poems/data/models/collection.dart | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/features/saved_poems/data/models/collection.dart b/lib/features/saved_poems/data/models/collection.dart index 1091eb2..2ad1e5b 100644 --- a/lib/features/saved_poems/data/models/collection.dart +++ b/lib/features/saved_poems/data/models/collection.dart @@ -7,10 +7,17 @@ class CollectionModel extends CollectionEntity{ super.poems = const [], }); - factory CollectionModel.fromFirebase(Map json) => CollectionModel( - name: json['name'] ?? '', - poems: (json['poems'] as Map).entries.map( - (poem) => PoemModel.fromFirebase(poem.value), - ).toList(), - ); + factory CollectionModel.fromFirebase(Map json) { + final poemsJson = json['poems'] as List; + final poems = poemsJson.map((poemJson) { + final poemMap = Map.from(poemJson as Map); + + return PoemModel.fromFirebase(poemMap); + }).toList(); + + return CollectionModel( + name: json['name'] ?? '', + poems: poems, + ); + } } From 772114c80751404fcac19a898dedb43e8921daac Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:28:31 +0200 Subject: [PATCH 185/475] Add collections fetching --- .../presentation/screens/saved_poems_screen.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 6e7fc08..4cb093a 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -18,6 +18,7 @@ class _SavedPoemsScreenState extends State { void initState() { super.initState(); initPoems(); + initCollections(); } Future initPoems() async{ @@ -25,6 +26,11 @@ class _SavedPoemsScreenState extends State { print(poems); } + Future initCollections() async{ + final collections = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); + print(collections); + } + @override Widget build(BuildContext context) => BlocBuilder( builder: (context, state) { @@ -34,7 +40,7 @@ class _SavedPoemsScreenState extends State { return Column( children: [ TextButton(onPressed: () async{ - final poems = await context.read().getUserPoems(widget._userRepository.getCurrentUser().userId!); + final poems = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); print(poems); }, child: Text('a')), ], From a6758376218b6f717213009b832ad9ac97a8c865 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:45:36 +0200 Subject: [PATCH 186/475] Remove comments --- .../screens/poems_feed_screen.dart | 72 +------------------ 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index da4811e..544e0ab 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -55,74 +55,4 @@ class PoemsFeedScreen extends StatelessWidget { ], ), ); -} - -/*class ScreensWrapper extends StatelessWidget { - const ScreensWrapper({super.key}); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: const CustomAppBar( - title: 'Poetlum', - ), - drawer: CustomDrawer(UserRepositoryImpl(FirebaseAuth.instance)), - body: _buildBody(context), - bottomNavigationBar: GNav( - onTabChange: (value) { - - }, - gap: 12, - tabs: const [ - GButton(icon: Icons.menu, text: 'Menu'), - GButton(icon: Icons.bookmark_outline_rounded, text: 'Saved poems'), - ], - ), - ); - - BlocBuilder _buildBody(BuildContext context) => BlocBuilder( - builder: (_, state){ - if(state is RemotePoemLoading){ - return const Center(child: CircularProgressIndicator(),); - } - - if(state is RemotePoemError){ - return _buildErrorBody(state.message, context); - } - - if(state is RemotePoemDone){ - return ListView.builder( - itemCount: state.poems!.length, - itemBuilder: (__, index) => PoemCard( - poemEntity: state.poems![index], - ), - ); - } - - return const SizedBox(); - }, - ); - - Widget _buildErrorBody(String error, BuildContext context) => Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text( - error, - style: const TextStyle( - fontSize: 16, - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: IconButton.filledTonal( - onPressed: () => BlocProvider.of(context).add(const GetInitialPoemsEvent()), - icon: const Icon(Icons.refresh), - ), - ), - ], - ), - ); -}*/ +} \ No newline at end of file From 30b360e11018de674c6a1983fb8e2b91f9bb4cb3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:47:21 +0200 Subject: [PATCH 187/475] Create saved poems screen --- .../screens/saved_poems_screen.dart | 37 ++++++++--- .../widgets/collections_card.dart | 66 +++++++++++++++++++ 2 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 lib/features/saved_poems/presentation/widgets/collections_card.dart diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 4cb093a..e47e840 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/saved_poems/data/models/collection.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/collections_card.dart'; class SavedPoemsScreen extends StatefulWidget { const SavedPoemsScreen(this._userRepository, {super.key}); @@ -14,6 +18,9 @@ class SavedPoemsScreen extends StatefulWidget { } class _SavedPoemsScreenState extends State { + late List? poems; + late List? collections; + @override void initState() { super.initState(); @@ -21,31 +28,41 @@ class _SavedPoemsScreenState extends State { initCollections(); } - Future initPoems() async{ - final poems = await context.read().getUserPoems(widget._userRepository.getCurrentUser().userId!); - print(poems); + Future initPoems() async { + poems = await context.read().getUserPoems(widget._userRepository.getCurrentUser().userId!); } - Future initCollections() async{ - final collections = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); - print(collections); + Future initCollections() async { + collections = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); + + collections?.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); } @override Widget build(BuildContext context) => BlocBuilder( builder: (context, state) { - if(state.status == FirebaseDatabaseStatus.submitting){ + if (state.status == FirebaseDatabaseStatus.submitting) { return const CircularProgressIndicator(); - } else{ + } else { return Column( children: [ - TextButton(onPressed: () async{ + TextButton(onPressed: () async { final poems = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); print(poems); - }, child: Text('a')), + }, child: const Text('a')), + + Expanded( + child: ListView.builder( + itemCount: collections!.length, + itemBuilder: (context, index) => CollectionsCard( + collection: collections![index], + ), + ), + ), ], ); } }, ); } + diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart new file mode 100644 index 0000000..502655b --- /dev/null +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; + +class CollectionsCard extends StatelessWidget { + const CollectionsCard({super.key, required this.collection}); + + final CollectionEntity collection; + + @override + Widget build(BuildContext context) => GestureDetector( + //onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), + child: SizedBox( + height: MediaQuery.of(context).size.height / 4, + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + elevation: 3, + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _TitleText(title: collection.name), + + SingleChildScrollView( + child: Column( + children: collection.poems!.map( + (poem) => _InfoText(author: poem.author, title: poem.title), + ).toList(), + ), + ), + ], + ), + ), + ), + ), + ); +} + +class _TitleText extends StatelessWidget { + const _TitleText({required this.title}); + + final String? title; + + @override + Widget build(BuildContext context) => Text( + title ?? 'Untitled', + style: const TextStyle(fontSize: 25, fontWeight: FontWeight.bold), + ); +} + +class _InfoText extends StatelessWidget { + const _InfoText({required this.author, required this.title}); + + final String? author; + final String? title; + + @override + Widget build(BuildContext context) => Text( + '$author $title', + style: const TextStyle(fontSize: 17), + ); +} From 2dc5e5771757375ed8dd2fd0aee221f591d46574 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:55:43 +0200 Subject: [PATCH 188/475] Create buttons placecholder --- .../screens/saved_poems_screen.dart | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index e47e840..f88b237 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -44,25 +44,34 @@ class _SavedPoemsScreenState extends State { if (state.status == FirebaseDatabaseStatus.submitting) { return const CircularProgressIndicator(); } else { - return Column( - children: [ - TextButton(onPressed: () async { - final poems = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); - print(poems); - }, child: const Text('a')), + return SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + FilledButton(onPressed: (){}, child: const Text('Create a collection')), + FilledButton.tonal(onPressed: (){}, child: const Text('Write a poem')), + ], + ), + ), - Expanded( - child: ListView.builder( + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), itemCount: collections!.length, itemBuilder: (context, index) => CollectionsCard( collection: collections![index], ), ), - ), - ], + ], + ), ); } }, ); + } From cef53e41c935755cf86a6c75470030c973e8b394 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:04:45 +0200 Subject: [PATCH 189/475] Rewrite code --- .../presentation/bloc/firebase_database_cubit.dart | 7 +++++++ .../presentation/screens/saved_poems_screen.dart | 14 ++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index cab2fa1..f30b95d 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -1,5 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/data/models/collection.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; @@ -30,6 +31,12 @@ class FirebaseDatabaseCubit extends Cubit { params: userId, ); + final poems = await _getUserPoemsUseCase( + params: userId, + ); + + collections?.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); return collections; diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index f88b237..a33664a 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; -import 'package:poetlum/features/saved_poems/data/models/collection.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; @@ -18,31 +16,24 @@ class SavedPoemsScreen extends StatefulWidget { } class _SavedPoemsScreenState extends State { - late List? poems; - late List? collections; + List? collections = []; @override void initState() { super.initState(); - initPoems(); initCollections(); } - Future initPoems() async { - poems = await context.read().getUserPoems(widget._userRepository.getCurrentUser().userId!); - } Future initCollections() async { collections = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); - - collections?.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); } @override Widget build(BuildContext context) => BlocBuilder( builder: (context, state) { if (state.status == FirebaseDatabaseStatus.submitting) { - return const CircularProgressIndicator(); + return const Center(child: CircularProgressIndicator()); } else { return SingleChildScrollView( child: Column( @@ -72,6 +63,5 @@ class _SavedPoemsScreenState extends State { } }, ); - } From 7817808a2bb9b00af32e2e8dda2b0b53040f0e11 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:28:38 +0200 Subject: [PATCH 190/475] Make card content scrollable --- .../widgets/collections_card.dart | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart index 502655b..244281b 100644 --- a/lib/features/saved_poems/presentation/widgets/collections_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -19,20 +19,20 @@ class CollectionsCard extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Padding( padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - _TitleText(title: collection.name), - - SingleChildScrollView( - child: Column( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _TitleText(title: collection.name), + + Column( children: collection.poems!.map( (poem) => _InfoText(author: poem.author, title: poem.title), ).toList(), ), - ), - ], + ], + ), ), ), ), From 28316a2deccc89d1daf5ea38b73d434cd123d8ef Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:32:28 +0200 Subject: [PATCH 191/475] Remove unused parameter --- .../saved_poems/presentation/widgets/collections_card.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart index 244281b..156e5fc 100644 --- a/lib/features/saved_poems/presentation/widgets/collections_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -22,7 +22,6 @@ class CollectionsCard extends StatelessWidget { child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, children: [ _TitleText(title: collection.name), From 676ff8e327440f8c2963bb9fdbcad8101edb5162 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:35:15 +0200 Subject: [PATCH 192/475] Make title scrollable --- .../presentation/widgets/collections_card.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart index 156e5fc..469c072 100644 --- a/lib/features/saved_poems/presentation/widgets/collections_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -58,8 +58,11 @@ class _InfoText extends StatelessWidget { final String? title; @override - Widget build(BuildContext context) => Text( - '$author $title', - style: const TextStyle(fontSize: 17), - ); + Widget build(BuildContext context) => SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Text( + '$author $title', + style: const TextStyle(fontSize: 17), + ), + ); } From e5db2da0be0403ef6e814af6bd93f1613dd53fac Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:04:30 +0200 Subject: [PATCH 193/475] Create write poem screen --- .../presentation/pages/write_poem_screen.dart | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 lib/features/saved_poems/presentation/pages/write_poem_screen.dart diff --git a/lib/features/saved_poems/presentation/pages/write_poem_screen.dart b/lib/features/saved_poems/presentation/pages/write_poem_screen.dart new file mode 100644 index 0000000..a3c777c --- /dev/null +++ b/lib/features/saved_poems/presentation/pages/write_poem_screen.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; + +class WritePoemPage extends StatelessWidget { + WritePoemPage({super.key}); + + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _contentController = TextEditingController(); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: const CustomAppBar(title: 'Poetlum'), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const _CustomSpacer(heightFactor: 0.03), + _CustomTextField(hintText: 'Poem name', controller: _nameController, isLarge: false), + const _CustomSpacer(heightFactor: 0.03), + _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), + const _CustomSpacer(heightFactor: 0.03), + FilledButton.tonal(onPressed: (){}, child: const Padding(padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Text('Save'))), + const _CustomSpacer(heightFactor: 0.03), + ], + ), + ), + ); +} + +class _CustomTextField extends StatelessWidget { + const _CustomTextField({ + required this.hintText, + required this.controller, required this.isLarge, + }); + + final String hintText; + final TextEditingController controller; + final bool isLarge; + + @override + Widget build(BuildContext context) => LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth / 1.2; + + return Align( + child: SizedBox( + width: width, + child: TextField( + controller: controller, + maxLines: null, + minLines: isLarge ? 20 : 1, + textAlign: TextAlign.center, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: hintText, + hintStyle: const TextStyle(color: Colors.grey), + ), + ), + ), + ); + }, + ); +} + +class _CustomSpacer extends StatelessWidget { + const _CustomSpacer({required this.heightFactor}); + final double heightFactor; + + + @override + Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); +} From 7e5118851d3461ad14e534656d58ee7b5c24ce1c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:04:39 +0200 Subject: [PATCH 194/475] Create constant --- lib/core/constants/navigator_constants.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/constants/navigator_constants.dart b/lib/core/constants/navigator_constants.dart index 90d5ca0..017cdb8 100644 --- a/lib/core/constants/navigator_constants.dart +++ b/lib/core/constants/navigator_constants.dart @@ -3,3 +3,4 @@ const String registerPageConstant = '/register'; const String loginPageConstant = '/login'; const String screensWrapperPageConstant = '/screen_wrapper'; const String poemViewPageConstant = '/poem_view'; +const String writePoemPageConstant = '/write_poem'; From 8ada50b8c5132e365e4979dda748891913be273c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:04:50 +0200 Subject: [PATCH 195/475] Register screen --- lib/features/application/poetlum_app.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 5259918..c84a0b7 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -7,6 +7,7 @@ import 'package:poetlum/features/application/presentation/pages/screens_wrapper. import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; +import 'package:poetlum/features/saved_poems/presentation/pages/write_poem_screen.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @@ -22,6 +23,7 @@ class PoetlumApp extends StatelessWidget { loginPageConstant: (_) => const LoginPage(), screensWrapperPageConstant: (_) => const ScreensWrapper(), poemViewPageConstant:(_) => const PoemViewPage(), + writePoemPageConstant: (_) => WritePoemPage(), }, ); } From b093ed2923b15ee075181439c975a1c3e00beccf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:05:03 +0200 Subject: [PATCH 196/475] Add screen navigation --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index a33664a..bae61a4 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -44,7 +45,7 @@ class _SavedPoemsScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FilledButton(onPressed: (){}, child: const Text('Create a collection')), - FilledButton.tonal(onPressed: (){}, child: const Text('Write a poem')), + FilledButton.tonal(onPressed: () => Navigator.pushNamed(context, writePoemPageConstant), child: const Text('Write a poem')), ], ), ), @@ -64,4 +65,3 @@ class _SavedPoemsScreenState extends State { }, ); } - From 85c9a41ae75f7a4fd8e13f4fecb04dd91db39469 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:48:15 +0200 Subject: [PATCH 197/475] Rename screen to page --- .../pages/{write_poem_screen.dart => write_poem_page.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/features/saved_poems/presentation/pages/{write_poem_screen.dart => write_poem_page.dart} (100%) diff --git a/lib/features/saved_poems/presentation/pages/write_poem_screen.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart similarity index 100% rename from lib/features/saved_poems/presentation/pages/write_poem_screen.dart rename to lib/features/saved_poems/presentation/pages/write_poem_page.dart From d5241469843e7fe7c50d3a56303cefa0e8e69253 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:48:20 +0200 Subject: [PATCH 198/475] Rename pt 2 --- lib/features/application/poetlum_app.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index c84a0b7..2adc13f 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -7,7 +7,7 @@ import 'package:poetlum/features/application/presentation/pages/screens_wrapper. import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; -import 'package:poetlum/features/saved_poems/presentation/pages/write_poem_screen.dart'; +import 'package:poetlum/features/saved_poems/presentation/pages/write_poem_page.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); From 3a2abee825910a5cd8ff42dd648fce8b8b55312f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:13:08 +0200 Subject: [PATCH 199/475] Update firebase api service --- .../data/data_sources/remote/firebase_api_service.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 157d874..b341d94 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -1,10 +1,12 @@ import 'package:firebase_database/firebase_database.dart'; import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/data/models/collection.dart'; abstract class FirebaseDatabaseService{ Future?> getUserPoems(String userId); Future?> getUserCollections(String userId); + Future saveCustomPoem({required String userId, required PoemEntity poemEntity}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -49,4 +51,11 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { return null; } } + + @override + Future saveCustomPoem({required String userId, required PoemEntity poemEntity}) async{ + final poemsRef = FirebaseDatabase.instance.ref().child('$userId/poems'); + + await poemsRef.push().set(poemEntity.toJson()); + } } From 35a8d9328abffe63acd52e4b522ac499c09fd2da Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:13:16 +0200 Subject: [PATCH 200/475] Add toJson method --- lib/features/poems_feed/domain/entities/poem.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/features/poems_feed/domain/entities/poem.dart b/lib/features/poems_feed/domain/entities/poem.dart index c0643c0..0ecca3c 100644 --- a/lib/features/poems_feed/domain/entities/poem.dart +++ b/lib/features/poems_feed/domain/entities/poem.dart @@ -15,4 +15,11 @@ class PoemEntity extends Equatable{ text, linecount, ]; + + Map toJson() => { + 'title': title, + 'author': author, + 'text': text, + 'linecount': linecount, + }; } From 6a49d24897a91b43fa2587824aa8a0b9073ea960 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:13:24 +0200 Subject: [PATCH 201/475] Update repository --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index d4b6827..43f4dc1 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -13,4 +13,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future?> getUserCollections(String userId) async => _databaseService.getUserCollections(userId); + + @override + Future saveCustomPoem({required String userId, required PoemEntity poemEntity}) => _databaseService.saveCustomPoem(userId: userId, poemEntity: poemEntity); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 5abedda..fc4bdc1 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -4,4 +4,5 @@ import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; abstract class FirebaseDatabaseRepository { Future?> getUserPoems(String userId); Future?> getUserCollections(String userId); + Future saveCustomPoem({required String userId, required PoemEntity poemEntity}); } From f6a679de9146af54210a8b9d53abd9a06c126691 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:13:38 +0200 Subject: [PATCH 202/475] Create use case --- .../save_poem/save_custom_poem_params.dart | 8 ++++++++ .../save_poem/save_custom_poem_usecase.dart | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart new file mode 100644 index 0000000..c942bfb --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart @@ -0,0 +1,8 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class SaveCustomPoemParams { + SaveCustomPoemParams({required this.poemEntity, required this.userId}); + + final PoemEntity poemEntity; + final String userId; +} diff --git a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart new file mode 100644 index 0000000..6978bf5 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart @@ -0,0 +1,19 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart'; + +class SaveCustomPoemUseCase implements UseCase{ + SaveCustomPoemUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({SaveCustomPoemParams? params}) async { + if(params != null){ + await _databaseRepository.saveCustomPoem( + poemEntity: params.poemEntity, + userId: params.userId, + ); + } + } +} From 8dd570e5a1ddb2ff0ada76d72623a09820115a34 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:13:48 +0200 Subject: [PATCH 203/475] Update cubit --- .../bloc/firebase_database_cubit.dart | 27 ++++++++++++++++++- .../bloc/firebase_database_state.dart | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index f30b95d..e265628 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -4,13 +4,16 @@ import 'package:poetlum/features/saved_poems/data/models/collection.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._saveCustomPoemUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; + final SaveCustomPoemUseCase _saveCustomPoemUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -41,4 +44,26 @@ class FirebaseDatabaseCubit extends Cubit { return collections; } + + Future saveCustomPoem({required String userId, required String username, required String title, required String text}) async { + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + await _saveCustomPoemUseCase( + params: SaveCustomPoemParams( + userId: userId, + poemEntity: PoemEntity( + author: username, + linecount: text.split('\n').length, + text: text, + title: title, + ), + ), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } + } } diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart index d368c69..b0a23b9 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.dart'; -enum FirebaseDatabaseStatus{initial, submitting, success} +enum FirebaseDatabaseStatus{initial, submitting, success, error} class FirebaseDatabaseState extends Equatable{ const FirebaseDatabaseState({ From 66e0f2b7adb31945a49388f29c9585f230783a43 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:14:01 +0200 Subject: [PATCH 204/475] Init stuff in the DI --- lib/core/dependency_injection.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 7a5e6a5..28e7ffb 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -24,6 +24,7 @@ import 'package:poetlum/features/saved_poems/data/repository/firebase_db_reposit import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; GetIt getIt = GetIt.instance; @@ -56,6 +57,7 @@ void initializeDependencies() { ..registerSingleton(LoginUserUseCase(getIt())) ..registerSingleton(GetUserPoemsUseCase(getIt())) ..registerSingleton(GetUserCollectionsUseCase(getIt())) + ..registerSingleton(SaveCustomPoemUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -65,7 +67,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt())) + ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), From e17b38e8149194ba08cb45e1334f3ba06991f507 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:14:08 +0200 Subject: [PATCH 205/475] Add DI --- lib/features/application/poetlum_app.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 2adc13f..8f15f56 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:poetlum/config/theme/app_theme.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/pages/auth_wrapper.dart'; import 'package:poetlum/features/application/presentation/pages/screens_wrapper.dart'; import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; @@ -23,7 +24,7 @@ class PoetlumApp extends StatelessWidget { loginPageConstant: (_) => const LoginPage(), screensWrapperPageConstant: (_) => const ScreensWrapper(), poemViewPageConstant:(_) => const PoemViewPage(), - writePoemPageConstant: (_) => WritePoemPage(), + writePoemPageConstant: (_) => WritePoemPage(getIt()), }, ); } From 0fa3c21628c4da04d052cb819e417248b5c24943 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:14:24 +0200 Subject: [PATCH 206/475] Add button functionality --- .../presentation/pages/write_poem_page.dart | 85 +++++++++++++++---- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index a3c777c..d2c25ae 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -1,30 +1,83 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class WritePoemPage extends StatelessWidget { - WritePoemPage({super.key}); + WritePoemPage(this._userRepository, {super.key}); + + final UserRepository _userRepository; final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @override - Widget build(BuildContext context) => Scaffold( - appBar: const CustomAppBar(title: 'Poetlum'), - body: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const _CustomSpacer(heightFactor: 0.03), - _CustomTextField(hintText: 'Poem name', controller: _nameController, isLarge: false), - const _CustomSpacer(heightFactor: 0.03), - _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), - const _CustomSpacer(heightFactor: 0.03), - FilledButton.tonal(onPressed: (){}, child: const Padding(padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Text('Save'))), - const _CustomSpacer(heightFactor: 0.03), - ], - ), + Widget build(BuildContext context) => BlocConsumer( + listener: (context, state) { + if (state.status == FirebaseDatabaseStatus.success) { + _showPositiveToast('Your amazing poem has been saved! :D'); + } else if (state.status == FirebaseDatabaseStatus.error) { + _showNegativeToast('An error occurred :('); + } + }, + builder: (context, state) => Scaffold( + appBar: const CustomAppBar(title: 'Poetlum'), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const _CustomSpacer(heightFactor: 0.03), + _CustomTextField(hintText: 'Poem name', controller: _nameController, isLarge: false), + const _CustomSpacer(heightFactor: 0.03), + _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), + const _CustomSpacer(heightFactor: 0.03), + + if (state.status == FirebaseDatabaseStatus.submitting) + const CircularProgressIndicator() + else FilledButton.tonal( + onPressed: () => context.read().saveCustomPoem( + userId: _userRepository.getCurrentUser().userId!, + username: _userRepository.getCurrentUser().username!, + title: _nameController.text, + text: _contentController.text, + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text('Save'), + ), + ), + + const _CustomSpacer(heightFactor: 0.03), + ], + ), + ), ), ); + + Future _showPositiveToast(String text) async{ + await Fluttertoast.showToast( + msg: text, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + ); + } + + Future _showNegativeToast(String error) async{ + await Fluttertoast.showToast( + msg: error, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); + } } class _CustomTextField extends StatelessWidget { From c5fb6f14d5a56c372714765bfe63c9c7c3584662 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 28 Nov 2023 21:09:25 +0200 Subject: [PATCH 207/475] Add form validation --- .../presentation/pages/write_poem_page.dart | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index d2c25ae..f92e01f 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -6,11 +6,17 @@ import 'package:poetlum/features/poems_feed/domain/repository/user_repository.da import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -class WritePoemPage extends StatelessWidget { - WritePoemPage(this._userRepository, {super.key}); +class WritePoemPage extends StatefulWidget { + const WritePoemPage(this._userRepository, {super.key}); final UserRepository _userRepository; + @override + State createState() => _WritePoemPageState(); +} + +class _WritePoemPageState extends State { + final GlobalKey _formKey = GlobalKey(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -24,8 +30,10 @@ class WritePoemPage extends StatelessWidget { } }, builder: (context, state) => Scaffold( - appBar: const CustomAppBar(title: 'Poetlum'), - body: SingleChildScrollView( + appBar: const CustomAppBar(title: 'Poetlum'), + body: SingleChildScrollView( + child: Form( + key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ @@ -34,30 +42,33 @@ class WritePoemPage extends StatelessWidget { const _CustomSpacer(heightFactor: 0.03), _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), const _CustomSpacer(heightFactor: 0.03), - if (state.status == FirebaseDatabaseStatus.submitting) const CircularProgressIndicator() else FilledButton.tonal( - onPressed: () => context.read().saveCustomPoem( - userId: _userRepository.getCurrentUser().userId!, - username: _userRepository.getCurrentUser().username!, - title: _nameController.text, - text: _contentController.text, - ), + onPressed: () { + if (_formKey.currentState!.validate()) { + context.read().saveCustomPoem( + userId: widget._userRepository.getCurrentUser().userId!, + username: widget._userRepository.getCurrentUser().username!, + title: _nameController.text, + text: _contentController.text, + ); + } + }, child: const Padding( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Text('Save'), ), ), - const _CustomSpacer(heightFactor: 0.03), ], ), ), + ), ), ); - Future _showPositiveToast(String text) async{ + Future _showPositiveToast(String text) async{ await Fluttertoast.showToast( msg: text, toastLength: Toast.LENGTH_SHORT, @@ -98,11 +109,17 @@ class _CustomTextField extends StatelessWidget { return Align( child: SizedBox( width: width, - child: TextField( + child: TextFormField( controller: controller, maxLines: null, minLines: isLarge ? 20 : 1, textAlign: TextAlign.center, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter some text'; + } + return null; + }, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: hintText, @@ -119,7 +136,6 @@ class _CustomSpacer extends StatelessWidget { const _CustomSpacer({required this.heightFactor}); final double heightFactor; - @override Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); -} +} \ No newline at end of file From d30a0f3a51be943ffa3a81630409a0482069fe2b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:14:39 +0200 Subject: [PATCH 208/475] Fix nullable collection issue --- .../presentation/bloc/firebase_database_cubit.dart | 7 +++++-- .../presentation/screens/saved_poems_screen.dart | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index e265628..29685b0 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -30,15 +30,18 @@ class FirebaseDatabaseCubit extends Cubit { Future?> getUserCollections(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); - final collections = await _getUserCollectionsUseCase( + var collections = await _getUserCollectionsUseCase( params: userId, ); + collections ??= []; + final poems = await _getUserPoemsUseCase( params: userId, ); - collections?.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); + + collections.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); emit(state.copyWith(status: FirebaseDatabaseStatus.success)); diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index bae61a4..1a68ced 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -50,7 +50,10 @@ class _SavedPoemsScreenState extends State { ), ), - ListView.builder( + + if (collections == null) + const Text("You haven't saved any poems yet. :(") + else ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: collections!.length, From 4edb2347a1cf8162ad7c23e99f4f32648638c5de Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:17:31 +0200 Subject: [PATCH 209/475] Add tooltips --- .../presentation/widgets/app_bar/buttons/refresh_button.dart | 1 + .../presentation/widgets/app_bar/buttons/settings_button.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart index fc39334..c8ac6e4 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -18,6 +18,7 @@ class _RefreshButtonState extends State with TickerProviderStateM builder: (context, state) => RotationTransition( turns: rotationAnimation, child: IconButton( + tooltip: 'Refresh poems list', onPressed: state is RemotePoemLoading ? null : (){ diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 2b6068d..bfc9b9b 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -13,6 +13,7 @@ class _SettingsButtonState extends State with TickerProviderStat Widget build(BuildContext context) => RotationTransition( turns: rotationAnimation, child: IconButton( + tooltip: 'Settings', onPressed: (){ playAnimation(); // ... From 1c68be6990989e2c2b3498daa2f7e6b8301ea82c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:39:43 +0200 Subject: [PATCH 210/475] Fix nullable issue --- .../presentation/bloc/firebase_database_cubit.dart | 7 ++++--- .../presentation/screens/saved_poems_screen.dart | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 29685b0..a782786 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -40,9 +40,10 @@ class FirebaseDatabaseCubit extends Cubit { params: userId, ); - - collections.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); - + if(poems != null){ + collections.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); + } + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); return collections; diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 1a68ced..d5f80ca 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -51,7 +51,7 @@ class _SavedPoemsScreenState extends State { ), - if (collections == null) + if (collections == null || collections!.isEmpty) const Text("You haven't saved any poems yet. :(") else ListView.builder( shrinkWrap: true, From d4adc7620da720df75ac0e217970a2729b3d3cee Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:06:06 +0200 Subject: [PATCH 211/475] Add leading widget support --- .../application/presentation/widgets/app_bar/app_bar.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/features/application/presentation/widgets/app_bar/app_bar.dart b/lib/features/application/presentation/widgets/app_bar/app_bar.dart index e8e7ea8..7992839 100644 --- a/lib/features/application/presentation/widgets/app_bar/app_bar.dart +++ b/lib/features/application/presentation/widgets/app_bar/app_bar.dart @@ -3,9 +3,10 @@ import 'package:poetlum/features/application/presentation/widgets/app_bar/button import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { - const CustomAppBar({super.key, required this.title}); + const CustomAppBar({super.key, required this.title, this.leading}); final String title; + final Widget? leading; @override Widget build(BuildContext context) => AppBar( @@ -13,6 +14,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { RefreshButton(), SettingsButton(), ], + leading: leading, title: Text( title, style: const TextStyle( From 93a596e3918332f5fd0dabcfbb643f39da7130f0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:06:13 +0200 Subject: [PATCH 212/475] Change routing logic --- .../saved_poems/presentation/pages/write_poem_page.dart | 9 ++++++++- .../presentation/screens/saved_poems_screen.dart | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index f92e01f..2cdf356 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -30,7 +31,13 @@ class _WritePoemPageState extends State { } }, builder: (context, state) => Scaffold( - appBar: const CustomAppBar(title: 'Poetlum'), + appBar: CustomAppBar( + title: 'Poetlum', + leading: IconButton( + onPressed: () => Navigator.pushNamedAndRemoveUntil(context, screensWrapperPageConstant, (route) => false), + icon: const Icon(Icons.arrow_back), + ), + ), body: SingleChildScrollView( child: Form( key: _formKey, diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index d5f80ca..7a2cf1d 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -45,7 +45,14 @@ class _SavedPoemsScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FilledButton(onPressed: (){}, child: const Text('Create a collection')), - FilledButton.tonal(onPressed: () => Navigator.pushNamed(context, writePoemPageConstant), child: const Text('Write a poem')), + FilledButton.tonal( + onPressed: () => Navigator.pushNamedAndRemoveUntil( + context, + writePoemPageConstant, + (route) => false, + ), + child: const Text('Write a poem'), + ), ], ), ), From 816914b145bd6aeba37669063b7c0245f47760bd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:12:56 +0200 Subject: [PATCH 213/475] Remove text alignment --- lib/features/saved_poems/presentation/pages/write_poem_page.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 2cdf356..8c100c7 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -120,7 +120,6 @@ class _CustomTextField extends StatelessWidget { controller: controller, maxLines: null, minLines: isLarge ? 20 : 1, - textAlign: TextAlign.center, validator: (value) { if (value == null || value.isEmpty) { return 'Please enter some text'; From a40f4550a15a3956d11d706e1f7a73f5ec248de6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:51:54 +0200 Subject: [PATCH 214/475] Add like button package --- pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index 75d6536..d2e4740 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -560,6 +560,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.1" + like_button: + dependency: "direct main" + description: + name: like_button + sha256: "08e6a45b78888412df5d351786c550205ad3a677e72a0820d5bbc0b063c8a463" + url: "https://pub.dev" + source: hosted + version: "2.0.5" lints: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5b724f1..2cd6417 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,7 @@ dependencies: fluttertoast: ^8.0.9 google_nav_bar: ^5.0.6 animations: ^2.0.8 + like_button: ^2.0.5 dev_dependencies: flutter_test: From 1fd8b1acac9efa296824e726882fdad3edea21da Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:55:10 +0200 Subject: [PATCH 215/475] Create like button --- .../pages/poem_view/poem_view.dart | 3 ++ .../widgets/poem_view/custom_like_button.dart | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 3ab81e4..e8b7163 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; @@ -32,6 +33,8 @@ class PoemViewPage extends StatelessWidget { const CustomSpacer(heightFactor: 0.02), PoemLineCount(lineCount: poemEntity.linecount ?? 0), const CustomSpacer(heightFactor: 0.02), + CustomLikeButton(poemEntity: poemEntity), + const CustomSpacer(heightFactor: 0.02), ], ), ), diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart new file mode 100644 index 0000000..75fe02b --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:like_button/like_button.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; + +class CustomLikeButton extends StatelessWidget { + const CustomLikeButton({super.key, required this.poemEntity}); + + final PoemEntity poemEntity; + + @override + Widget build(BuildContext context) => LikeButton( + size: 42, + circleColor: CircleColor(start: Theme.of(context).colorScheme.primary, end: Theme.of(context).colorScheme.secondary), + bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primary, dotSecondaryColor: Theme.of(context).colorScheme.secondary), + onTap: (isLiked) async { + if(isLiked == false){ + await context.read().saveCustomPoem( + userId: getIt().getCurrentUser().userId!, + username: poemEntity.author ?? '', + title: poemEntity.title ?? '', + text: poemEntity.text ?? '', + ); + } + return !isLiked; + }, + ); +} From 9dd9e8f12d25462ff0e89b0c898b2939aa97a28d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:06:01 +0200 Subject: [PATCH 216/475] Customize button --- .../widgets/poem_view/custom_like_button.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart index 75fe02b..2d567dd 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart @@ -14,8 +14,8 @@ class CustomLikeButton extends StatelessWidget { @override Widget build(BuildContext context) => LikeButton( size: 42, - circleColor: CircleColor(start: Theme.of(context).colorScheme.primary, end: Theme.of(context).colorScheme.secondary), - bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primary, dotSecondaryColor: Theme.of(context).colorScheme.secondary), + circleColor: CircleColor(start: Theme.of(context).colorScheme.primaryContainer, end: Theme.of(context).colorScheme.primary), + bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), onTap: (isLiked) async { if(isLiked == false){ await context.read().saveCustomPoem( @@ -27,5 +27,10 @@ class CustomLikeButton extends StatelessWidget { } return !isLiked; }, + likeBuilder: (isLiked) => Icon( + Icons.bookmark_rounded, + color: isLiked ? Theme.of(context).colorScheme.primary : Colors.grey, + size: 42, + ), ); } From 2ee345b27f6c0ab0c7188d81d869fe8c4ba6adc5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:06:13 +0200 Subject: [PATCH 217/475] Move button on top --- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index e8b7163..f4c8484 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -24,6 +24,8 @@ class PoemViewPage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( children: [ + const CustomSpacer(heightFactor: 0.02), + CustomLikeButton(poemEntity: poemEntity), const CustomSpacer(heightFactor: 0.02), PoemTitle(title: poemEntity.title ?? ''), const CustomSpacer(heightFactor: 0.02), @@ -33,8 +35,6 @@ class PoemViewPage extends StatelessWidget { const CustomSpacer(heightFactor: 0.02), PoemLineCount(lineCount: poemEntity.linecount ?? 0), const CustomSpacer(heightFactor: 0.02), - CustomLikeButton(poemEntity: poemEntity), - const CustomSpacer(heightFactor: 0.02), ], ), ), From 0796d8dc2745669205133e541456f40674f7ddd3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:12:06 +0200 Subject: [PATCH 218/475] Rename flow --- lib/core/dependency_injection.dart | 2 +- .../widgets/poem_view/custom_like_button.dart | 2 +- .../data/data_sources/remote/firebase_api_service.dart | 4 ++-- .../data/repository/firebase_db_repository_impl.dart | 2 +- .../domain/repository/firebase_db_repository.dart | 2 +- .../usecases/save_poem/save_custom_poem_params.dart | 4 ++-- .../usecases/save_poem/save_custom_poem_usecase.dart | 8 ++++---- .../presentation/bloc/firebase_database_cubit.dart | 10 +++++----- .../presentation/pages/write_poem_page.dart | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 28e7ffb..f3fa007 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -57,7 +57,7 @@ void initializeDependencies() { ..registerSingleton(LoginUserUseCase(getIt())) ..registerSingleton(GetUserPoemsUseCase(getIt())) ..registerSingleton(GetUserCollectionsUseCase(getIt())) - ..registerSingleton(SaveCustomPoemUseCase(getIt())) + ..registerSingleton(SavePoemUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart index 2d567dd..155ff9a 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart @@ -18,7 +18,7 @@ class CustomLikeButton extends StatelessWidget { bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), onTap: (isLiked) async { if(isLiked == false){ - await context.read().saveCustomPoem( + await context.read().savePoem( userId: getIt().getCurrentUser().userId!, username: poemEntity.author ?? '', title: poemEntity.title ?? '', diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index b341d94..1240bd4 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -6,7 +6,7 @@ import 'package:poetlum/features/saved_poems/data/models/collection.dart'; abstract class FirebaseDatabaseService{ Future?> getUserPoems(String userId); Future?> getUserCollections(String userId); - Future saveCustomPoem({required String userId, required PoemEntity poemEntity}); + Future savePoem({required String userId, required PoemEntity poemEntity}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -53,7 +53,7 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } @override - Future saveCustomPoem({required String userId, required PoemEntity poemEntity}) async{ + Future savePoem({required String userId, required PoemEntity poemEntity}) async{ final poemsRef = FirebaseDatabase.instance.ref().child('$userId/poems'); await poemsRef.push().set(poemEntity.toJson()); diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 43f4dc1..2888abe 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -15,5 +15,5 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ Future?> getUserCollections(String userId) async => _databaseService.getUserCollections(userId); @override - Future saveCustomPoem({required String userId, required PoemEntity poemEntity}) => _databaseService.saveCustomPoem(userId: userId, poemEntity: poemEntity); + Future savePoem({required String userId, required PoemEntity poemEntity}) => _databaseService.savePoem(userId: userId, poemEntity: poemEntity); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index fc4bdc1..a9563c1 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -4,5 +4,5 @@ import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; abstract class FirebaseDatabaseRepository { Future?> getUserPoems(String userId); Future?> getUserCollections(String userId); - Future saveCustomPoem({required String userId, required PoemEntity poemEntity}); + Future savePoem({required String userId, required PoemEntity poemEntity}); } diff --git a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart index c942bfb..a57b03c 100644 --- a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart +++ b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart @@ -1,7 +1,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -class SaveCustomPoemParams { - SaveCustomPoemParams({required this.poemEntity, required this.userId}); +class SavePoemParams { + SavePoemParams({required this.poemEntity, required this.userId}); final PoemEntity poemEntity; final String userId; diff --git a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart index 6978bf5..084c06d 100644 --- a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart +++ b/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart @@ -2,15 +2,15 @@ import 'package:poetlum/core/usecases/usecase.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart'; -class SaveCustomPoemUseCase implements UseCase{ - SaveCustomPoemUseCase(this._databaseRepository); +class SavePoemUseCase implements UseCase{ + SavePoemUseCase(this._databaseRepository); final FirebaseDatabaseRepository _databaseRepository; @override - Future call({SaveCustomPoemParams? params}) async { + Future call({SavePoemParams? params}) async { if(params != null){ - await _databaseRepository.saveCustomPoem( + await _databaseRepository.savePoem( poemEntity: params.poemEntity, userId: params.userId, ); diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index a782786..3590223 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -9,11 +9,11 @@ import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_cust import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._saveCustomPoemUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; - final SaveCustomPoemUseCase _saveCustomPoemUseCase; + final SavePoemUseCase _savePoemUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -49,12 +49,12 @@ class FirebaseDatabaseCubit extends Cubit { return collections; } - Future saveCustomPoem({required String userId, required String username, required String title, required String text}) async { + Future savePoem({required String userId, required String username, required String title, required String text}) async { emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); try{ - await _saveCustomPoemUseCase( - params: SaveCustomPoemParams( + await _savePoemUseCase( + params: SavePoemParams( userId: userId, poemEntity: PoemEntity( author: username, diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 8c100c7..8a17a3d 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -54,7 +54,7 @@ class _WritePoemPageState extends State { else FilledButton.tonal( onPressed: () { if (_formKey.currentState!.validate()) { - context.read().saveCustomPoem( + context.read().savePoem( userId: widget._userRepository.getCurrentUser().userId!, username: widget._userRepository.getCurrentUser().username!, title: _nameController.text, From 4cf81c020507393be49a577ba4a9de6661323175 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:13:14 +0200 Subject: [PATCH 219/475] Rename files --- lib/core/dependency_injection.dart | 2 +- .../{save_custom_poem_params.dart => save_poem_params.dart} | 0 .../{save_custom_poem_usecase.dart => save_poem_usecase.dart} | 2 +- .../presentation/bloc/firebase_database_cubit.dart | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename lib/features/saved_poems/domain/usecases/save_poem/{save_custom_poem_params.dart => save_poem_params.dart} (100%) rename lib/features/saved_poems/domain/usecases/save_poem/{save_custom_poem_usecase.dart => save_poem_usecase.dart} (94%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index f3fa007..1fa2010 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -24,7 +24,7 @@ import 'package:poetlum/features/saved_poems/data/repository/firebase_db_reposit import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; GetIt getIt = GetIt.instance; diff --git a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart b/lib/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart similarity index 100% rename from lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart rename to lib/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart diff --git a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart b/lib/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart similarity index 94% rename from lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart rename to lib/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart index 084c06d..4c85c87 100644 --- a/lib/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart +++ b/lib/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart @@ -1,6 +1,6 @@ import 'package:poetlum/core/usecases/usecase.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart'; class SavePoemUseCase implements UseCase{ SavePoemUseCase(this._databaseRepository); diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 3590223..711be08 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -4,8 +4,8 @@ import 'package:poetlum/features/saved_poems/data/models/collection.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_params.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_custom_poem_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { From 54bb96fa99906ef49449272a413d7d36e3299b30 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:16:33 +0200 Subject: [PATCH 220/475] Update api service --- .../remote/firebase_api_service.dart | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 1240bd4..e5cd900 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -7,6 +7,7 @@ abstract class FirebaseDatabaseService{ Future?> getUserPoems(String userId); Future?> getUserCollections(String userId); Future savePoem({required String userId, required PoemEntity poemEntity}); + Future deletePoem({required PoemEntity poemEntity, required String userId, required String? collectionName}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -58,4 +59,41 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { await poemsRef.push().set(poemEntity.toJson()); } + + @override + Future deletePoem({required PoemEntity poemEntity, required String userId, required String? collectionName}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + + final poemsRef = userRef.child('poems'); + final poemQuery = poemsRef.orderByChild('title').equalTo(poemEntity.title); + await _deleteIfMatches(poemQuery, poemEntity.author ?? '', poemEntity.text ?? ''); + + if (collectionName != null) { + final collectionsRef = userRef.child('collections'); + final collectionsSnapshot = await collectionsRef.get(); + + if (collectionsSnapshot.exists) { + final collections = collectionsSnapshot.value as Map; + collections.forEach((key, value) { + if (value['name'] == collectionName && value['poems'] != null) { + final collectionPoemsRef = collectionsRef.child('$key/poems'); + final collectionPoemQuery = collectionPoemsRef.orderByChild('title').equalTo(poemEntity.title); + _deleteIfMatches(collectionPoemQuery, poemEntity.author ?? '', poemEntity.text ?? ''); + } + }); + } + } + } + + Future _deleteIfMatches(Query query, String author, String text) async { + final snapshot = await query.get(); + if (snapshot.exists) { + final poems = snapshot.value as Map; + poems.forEach((key, value) { + if (value['author'] == author && value['text'] == text) { + query.ref.child(key).remove(); + } + }); + } + } } From 1dedfb2641b124ed3c6c4beefa87ddd52554f57b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:17:11 +0200 Subject: [PATCH 221/475] Add deletePoem to the repository --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 2888abe..8752f60 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -16,4 +16,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future savePoem({required String userId, required PoemEntity poemEntity}) => _databaseService.savePoem(userId: userId, poemEntity: poemEntity); + + @override + Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}) => _databaseService.deletePoem(poemEntity: poemEntity, userId: userId, collectionName: collectionName); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index a9563c1..b318be8 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -5,4 +5,5 @@ abstract class FirebaseDatabaseRepository { Future?> getUserPoems(String userId); Future?> getUserCollections(String userId); Future savePoem({required String userId, required PoemEntity poemEntity}); + Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}); } From 3c717d54a183c693e5b8e2a2c455d56152f97e8a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:17:21 +0200 Subject: [PATCH 222/475] Create delete poem use case --- .../delete_poem/delete_poem_params.dart | 9 +++++++++ .../delete_poem/delete_poem_usecase.dart | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart b/lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart new file mode 100644 index 0000000..91cc569 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart @@ -0,0 +1,9 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class DeletePoemParams { + DeletePoemParams({required this.poemEntity, required this.userId, this.collectionName}); + + final PoemEntity poemEntity; + final String userId; + final String? collectionName; +} diff --git a/lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart b/lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart new file mode 100644 index 0000000..53f47df --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart'; + +class DeletePoemUseCase implements UseCase{ + DeletePoemUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({DeletePoemParams? params}) async { + if(params != null){ + await _databaseRepository.deletePoem( + poemEntity: params.poemEntity, + userId: params.userId, + collectionName: params.collectionName, + ); + } + } +} From 29a72352d7ba458428f0d074d23251bcbee4c1eb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:17:34 +0200 Subject: [PATCH 223/475] Add delePoem in the cubit --- .../bloc/firebase_database_cubit.dart | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 711be08..f3c877f 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -2,6 +2,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/data/models/collection.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart'; @@ -9,11 +11,12 @@ import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; final SavePoemUseCase _savePoemUseCase; + final DeletePoemUseCase _deletePoemUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -70,4 +73,22 @@ class FirebaseDatabaseCubit extends Cubit { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } } + + Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + await _deletePoemUseCase( + params: DeletePoemParams( + userId: userId, + collectionName: collectionName, + poemEntity: poemEntity, + ), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } + } } From 57ba537f1a019389c4933714d7d19b2a429a2885 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:17:43 +0200 Subject: [PATCH 224/475] Init use case in the DI --- lib/core/dependency_injection.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 1fa2010..c20ea01 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -22,6 +22,7 @@ import 'package:poetlum/features/realtime_database/domain/entities/database_mana import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; import 'package:poetlum/features/saved_poems/data/repository/firebase_db_repository_impl.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; @@ -58,6 +59,7 @@ void initializeDependencies() { ..registerSingleton(GetUserPoemsUseCase(getIt())) ..registerSingleton(GetUserCollectionsUseCase(getIt())) ..registerSingleton(SavePoemUseCase(getIt())) + ..registerSingleton(DeletePoemUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -67,7 +69,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt())) + ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt(), getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), From 0ff7d243dcad73f01528a88794dabd9aad6137b3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:17:52 +0200 Subject: [PATCH 225/475] Delete poem on the second tap --- .../presentation/widgets/poem_view/custom_like_button.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart index 155ff9a..e4f5129 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart @@ -24,6 +24,11 @@ class CustomLikeButton extends StatelessWidget { title: poemEntity.title ?? '', text: poemEntity.text ?? '', ); + } else{ + await context.read().deletePoem( + poemEntity: poemEntity, + userId: getIt().getCurrentUser().userId!, + ); } return !isLiked; }, From 30516a00ed7c47ebcb1af7c121210dd9d9b17089 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:19:46 +0200 Subject: [PATCH 226/475] Code refactor --- .../remote/firebase_api_service.dart | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index e5cd900..7a6a498 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_dynamic_calls + import 'package:firebase_database/firebase_database.dart'; import 'package:poetlum/features/poems_feed/data/models/poem.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; @@ -62,38 +64,38 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @override Future deletePoem({required PoemEntity poemEntity, required String userId, required String? collectionName}) async { - final userRef = FirebaseDatabase.instance.ref(userId); - - final poemsRef = userRef.child('poems'); - final poemQuery = poemsRef.orderByChild('title').equalTo(poemEntity.title); - await _deleteIfMatches(poemQuery, poemEntity.author ?? '', poemEntity.text ?? ''); + final userRef = FirebaseDatabase.instance.ref(userId); + + await _deletePoemFromNode(userRef.child('poems'), poemEntity); - if (collectionName != null) { - final collectionsRef = userRef.child('collections'); - final collectionsSnapshot = await collectionsRef.get(); + if (collectionName != null) { + final collectionsRef = userRef.child('collections'); + final collectionsSnapshot = await collectionsRef.get(); - if (collectionsSnapshot.exists) { - final collections = collectionsSnapshot.value as Map; - collections.forEach((key, value) { - if (value['name'] == collectionName && value['poems'] != null) { - final collectionPoemsRef = collectionsRef.child('$key/poems'); - final collectionPoemQuery = collectionPoemsRef.orderByChild('title').equalTo(poemEntity.title); - _deleteIfMatches(collectionPoemQuery, poemEntity.author ?? '', poemEntity.text ?? ''); - } - }); + if (collectionsSnapshot.exists) { + final collections = collectionsSnapshot.value as Map; + for (final key in collections.keys) { + final value = collections[key]; + if (value['name'] == collectionName && value['poems'] != null) { + await _deletePoemFromNode(collectionsRef.child('$key/poems'), poemEntity); + } } } } +} - Future _deleteIfMatches(Query query, String author, String text) async { - final snapshot = await query.get(); - if (snapshot.exists) { - final poems = snapshot.value as Map; - poems.forEach((key, value) { - if (value['author'] == author && value['text'] == text) { - query.ref.child(key).remove(); - } - }); +Future _deletePoemFromNode(DatabaseReference nodeRef, PoemEntity poemEntity) async { + final poemQuery = nodeRef.orderByChild('title').equalTo(poemEntity.title); + final snapshot = await poemQuery.get(); + if (snapshot.exists) { + final poems = snapshot.value as Map; + for (var key in poems.keys) { + final value = poems[key]; + if (value['author'] == poemEntity.author && value['text'] == poemEntity.text) { + await nodeRef.child(key).remove(); + } } } } + +} From 6e038f97ea4f3ba96f958f93ecadba1b40836bc5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:50:09 +0200 Subject: [PATCH 227/475] Update api service --- .../remote/firebase_api_service.dart | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 7a6a498..676cae2 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -10,6 +10,7 @@ abstract class FirebaseDatabaseService{ Future?> getUserCollections(String userId); Future savePoem({required String userId, required PoemEntity poemEntity}); Future deletePoem({required PoemEntity poemEntity, required String userId, required String? collectionName}); + Future isPoemExists({required PoemEntity poemEntity, required String userId}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -84,18 +85,35 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } } -Future _deletePoemFromNode(DatabaseReference nodeRef, PoemEntity poemEntity) async { - final poemQuery = nodeRef.orderByChild('title').equalTo(poemEntity.title); - final snapshot = await poemQuery.get(); - if (snapshot.exists) { - final poems = snapshot.value as Map; - for (var key in poems.keys) { - final value = poems[key]; - if (value['author'] == poemEntity.author && value['text'] == poemEntity.text) { - await nodeRef.child(key).remove(); + Future _deletePoemFromNode(DatabaseReference nodeRef, PoemEntity poemEntity) async { + final poemQuery = nodeRef.orderByChild('title').equalTo(poemEntity.title); + final snapshot = await poemQuery.get(); + if (snapshot.exists) { + final poems = snapshot.value as Map; + for (var key in poems.keys) { + final value = poems[key]; + if (value['author'] == poemEntity.author && value['text'] == poemEntity.text) { + await nodeRef.child(key).remove(); + } } } } -} + @override + Future isPoemExists({required PoemEntity poemEntity, required String userId}) async { + final poemsRef = FirebaseDatabase.instance.ref('$userId/poems'); + + final query = poemsRef.orderByChild('title').equalTo(poemEntity.title); + final snapshot = await query.get(); + + if (snapshot.exists) { + final poems = snapshot.value as Map; + for (final poem in poems.values) { + if (poem['author'] == poemEntity.author && poem['text'] == poemEntity.text) { + return true; + } + } + } + return false; + } } From 619854a9f67716d2ffcc7d1d67dfd86c1be16cbc Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:50:25 +0200 Subject: [PATCH 228/475] Add isPoemExists in the repository --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 8752f60..1146a1d 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -19,4 +19,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}) => _databaseService.deletePoem(poemEntity: poemEntity, userId: userId, collectionName: collectionName); + + @override + Future isPoemExists({required PoemEntity poemEntity, required String userId}) => _databaseService.isPoemExists(poemEntity: poemEntity, userId: userId); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index b318be8..bb656a7 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -6,4 +6,5 @@ abstract class FirebaseDatabaseRepository { Future?> getUserCollections(String userId); Future savePoem({required String userId, required PoemEntity poemEntity}); Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}); + Future isPoemExists({required PoemEntity poemEntity, required String userId}); } From 79083a8e8c818785a524a62e807819fe9210d0b0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:50:37 +0200 Subject: [PATCH 229/475] Create isPoemExists use case --- .../is_poem_exists/is_poem_exists_params.dart | 8 ++++++++ .../is_poem_exists_usecase.dart | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart b/lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart new file mode 100644 index 0000000..de38584 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart @@ -0,0 +1,8 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class IsPoemExistsParams { + IsPoemExistsParams({required this.poemEntity, required this.userId}); + + final PoemEntity poemEntity; + final String userId; +} diff --git a/lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart b/lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart new file mode 100644 index 0000000..40fd8f6 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart'; + +class IsPoemExistsUseCase implements UseCase{ + IsPoemExistsUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({IsPoemExistsParams? params}) async { + if(params != null){ + final result = await _databaseRepository.isPoemExists(poemEntity: params.poemEntity, userId: params.userId); + + return result; + } + + return false; + } +} From 95ad1a693e6447faee04f6c7fd4f78714688a1a0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:50:53 +0200 Subject: [PATCH 230/475] Add isPoemExists in the cubit --- .../bloc/firebase_database_cubit.dart | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index f3c877f..fe13c15 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -6,17 +6,20 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; final SavePoemUseCase _savePoemUseCase; final DeletePoemUseCase _deletePoemUseCase; + final IsPoemExistsUseCase _isPoemExistsUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -91,4 +94,22 @@ class FirebaseDatabaseCubit extends Cubit { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } } + + Future isPoemExists({required PoemEntity poemEntity, required String userId, String? collectionName}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + final isExists = await _isPoemExistsUseCase( + params: IsPoemExistsParams(poemEntity: poemEntity, userId: userId), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + + return isExists; + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + + return false; + } + } } From f409b834cb684a94311182a0271acb5b3d2c619b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:51:01 +0200 Subject: [PATCH 231/475] Init stuff in the DI --- lib/core/dependency_injection.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index c20ea01..de42381 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -25,6 +25,7 @@ import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repos import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -60,6 +61,7 @@ void initializeDependencies() { ..registerSingleton(GetUserCollectionsUseCase(getIt())) ..registerSingleton(SavePoemUseCase(getIt())) ..registerSingleton(DeletePoemUseCase(getIt())) + ..registerSingleton(IsPoemExistsUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -69,7 +71,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt(), getIt())) + ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt(), getIt(), getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), From 83d24e4311cbaf58e931a742da2a9ddfcfbb413a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:51:22 +0200 Subject: [PATCH 232/475] Add isExists functionality to the button --- .../widgets/poem_view/custom_like_button.dart | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart index e4f5129..12bcd07 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart @@ -12,30 +12,40 @@ class CustomLikeButton extends StatelessWidget { final PoemEntity poemEntity; @override - Widget build(BuildContext context) => LikeButton( - size: 42, - circleColor: CircleColor(start: Theme.of(context).colorScheme.primaryContainer, end: Theme.of(context).colorScheme.primary), - bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), - onTap: (isLiked) async { - if(isLiked == false){ - await context.read().savePoem( - userId: getIt().getCurrentUser().userId!, - username: poemEntity.author ?? '', - title: poemEntity.title ?? '', - text: poemEntity.text ?? '', - ); + Widget build(BuildContext context) => FutureBuilder( + future: context.read().isPoemExists(poemEntity: poemEntity, userId: getIt().getCurrentUser().userId!), + builder: (context, snapshot) { + if(snapshot.connectionState == ConnectionState.waiting){ + return const CircularProgressIndicator(); } else{ - await context.read().deletePoem( - poemEntity: poemEntity, - userId: getIt().getCurrentUser().userId!, + return LikeButton( + isLiked: snapshot.data, + size: 42, + circleColor: CircleColor(start: Theme.of(context).colorScheme.primaryContainer, end: Theme.of(context).colorScheme.primary), + bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), + onTap: (isLiked) async { + if(isLiked == false){ + await context.read().savePoem( + userId: getIt().getCurrentUser().userId!, + username: poemEntity.author ?? '', + title: poemEntity.title ?? '', + text: poemEntity.text ?? '', + ); + } else{ + await context.read().deletePoem( + poemEntity: poemEntity, + userId: getIt().getCurrentUser().userId!, + ); + } + return !isLiked; + }, + likeBuilder: (isLiked) => Icon( + Icons.bookmark_rounded, + color: isLiked ? Theme.of(context).colorScheme.primary : Colors.grey, + size: 42, + ), ); } - return !isLiked; }, - likeBuilder: (isLiked) => Icon( - Icons.bookmark_rounded, - color: isLiked ? Theme.of(context).colorScheme.primary : Colors.grey, - size: 42, - ), ); } From b2304dcc343ca5ad176dd422ddd374abbdb7d12d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:52:58 +0200 Subject: [PATCH 233/475] Text improvement --- .../saved_poems/presentation/widgets/collections_card.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart index 469c072..192b339 100644 --- a/lib/features/saved_poems/presentation/widgets/collections_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -61,7 +61,7 @@ class _InfoText extends StatelessWidget { Widget build(BuildContext context) => SingleChildScrollView( scrollDirection: Axis.horizontal, child: Text( - '$author $title', + '$author: $title', style: const TextStyle(fontSize: 17), ), ); From d93ebdce7d4ac7f3c202014c181744bae8f159d2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:50:32 +0200 Subject: [PATCH 234/475] Install multi_dropdown package --- pubspec.lock | 16 ++++++++++++++++ pubspec.yaml | 1 + 2 files changed, 17 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index d2e4740..75c5bc4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -504,6 +504,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + http: + dependency: transitive + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -616,6 +624,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + multi_dropdown: + dependency: "direct main" + description: + name: multi_dropdown + sha256: "43f580a3a641462d6962c1dd4bccede40884d9210519b6f8809337e39b62bc2f" + url: "https://pub.dev" + source: hosted + version: "2.1.1" nested: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2cd6417..f045b1b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,7 @@ dependencies: google_nav_bar: ^5.0.6 animations: ^2.0.8 like_button: ^2.0.5 + multi_dropdown: ^2.1.1 dev_dependencies: flutter_test: From f160847f8b48caf4a4ca5a5a23c8b39e4b4d93a6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:51:02 +0200 Subject: [PATCH 235/475] Create bottom sheet --- .../presentation/screens/saved_poems_screen.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 7a2cf1d..aeb64a2 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -5,6 +5,7 @@ import 'package:poetlum/features/poems_feed/domain/repository/user_repository.da import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/collections_card.dart'; class SavedPoemsScreen extends StatefulWidget { @@ -44,7 +45,18 @@ class _SavedPoemsScreenState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - FilledButton(onPressed: (){}, child: const Text('Create a collection')), + FilledButton( + child: const Text('Create a collection'), + onPressed: (){ + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder:(context) => ColectionBottomSheetContent( + poems: collections?[0].poems, + ), + ); + }, + ), FilledButton.tonal( onPressed: () => Navigator.pushNamedAndRemoveUntil( context, From e90487f36a6427c6967c2cd7ed427800db05c537 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:51:18 +0200 Subject: [PATCH 236/475] Create bottom sheet template --- .../widgets/collection_bottom_sheet.dart | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart new file mode 100644 index 0000000..4016aa1 --- /dev/null +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; + +class ColectionBottomSheetContent extends StatelessWidget { + ColectionBottomSheetContent({super.key, required this.poems}); + + final List? poems; + final TextEditingController _collectionNameController = TextEditingController(); + final MultiSelectController _controller = MultiSelectController(); + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/1.15, + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.05), + const Text( + 'Create a collection', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + ), + const CustomSpacer(heightFactor: 0.05), + CustomTextField(hintText: 'Collection name', controller: _collectionNameController), + const CustomSpacer(heightFactor: 0.05), + SizedBox( + width: MediaQuery.of(context).size.width / 1.35, + child: MultiSelectDropDown( + borderRadius: 4, + borderWidth: 1.5, + backgroundColor: Colors.transparent, + hint: 'Select a poem to add', + controller: _controller, + onOptionSelected: (selectedOptions) {}, + options: poems == null + ? >[] + : poems!.map( + (poem) => ValueItem(label: '${poem.author ?? ''}: ${poem.title}', value: poem), + ).toList(), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + ), + const CustomSpacer(heightFactor: 0.05), + FilledButton.tonal(onPressed: (){}, child: const Padding(padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),child: Text("Create"))) + ], + ), + ); +} From 3f2a17d46b8133c5c768f8c7b69d3d4ae801be1e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:59:06 +0200 Subject: [PATCH 237/475] FIx typo --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index aeb64a2..8a0a052 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -51,7 +51,7 @@ class _SavedPoemsScreenState extends State { showModalBottomSheet( context: context, isScrollControlled: true, - builder:(context) => ColectionBottomSheetContent( + builder:(context) => CollectionBottomSheetContent( poems: collections?[0].poems, ), ); From f77c1ac986296df4227e0a5d0e5e0f5dfef2b184 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:59:14 +0200 Subject: [PATCH 238/475] Decompoze large widget --- .../widgets/collection_bottom_sheet.dart | 98 +++++++++++++------ 1 file changed, 70 insertions(+), 28 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index 4016aa1..47fb1c8 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -4,8 +4,8 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; -class ColectionBottomSheetContent extends StatelessWidget { - ColectionBottomSheetContent({super.key, required this.poems}); +class CollectionBottomSheetContent extends StatelessWidget { + CollectionBottomSheetContent({super.key, required this.poems}); final List? poems; final TextEditingController _collectionNameController = TextEditingController(); @@ -13,40 +13,82 @@ class ColectionBottomSheetContent extends StatelessWidget { @override Widget build(BuildContext context) => SizedBox( - height: MediaQuery.of(context).size.height/1.15, + height: MediaQuery.of(context).size.height / 1.15, width: MediaQuery.of(context).size.width, child: Column( children: [ const CustomSpacer(heightFactor: 0.05), - const Text( - 'Create a collection', - style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), - ), + const _TitleTextWidget(), const CustomSpacer(heightFactor: 0.05), - CustomTextField(hintText: 'Collection name', controller: _collectionNameController), + _CollectionNameInputWidget(controller: _collectionNameController), const CustomSpacer(heightFactor: 0.05), - SizedBox( - width: MediaQuery.of(context).size.width / 1.35, - child: MultiSelectDropDown( - borderRadius: 4, - borderWidth: 1.5, - backgroundColor: Colors.transparent, - hint: 'Select a poem to add', - controller: _controller, - onOptionSelected: (selectedOptions) {}, - options: poems == null - ? >[] - : poems!.map( - (poem) => ValueItem(label: '${poem.author ?? ''}: ${poem.title}', value: poem), - ).toList(), - dropdownHeight: 300, - optionTextStyle: const TextStyle(fontSize: 16), - selectedOptionIcon: const Icon(Icons.check_circle), - ), - ), + _PoemSelectionWidget(controller: _controller, poems: poems), const CustomSpacer(heightFactor: 0.05), - FilledButton.tonal(onPressed: (){}, child: const Padding(padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),child: Text("Create"))) + const _CreateButtonWidget(), ], ), ); } + +class _TitleTextWidget extends StatelessWidget { + const _TitleTextWidget(); + + @override + Widget build(BuildContext context) => const Text( + 'Create a collection', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + ); +} + +class _CollectionNameInputWidget extends StatelessWidget { + const _CollectionNameInputWidget({required this.controller}); + + final TextEditingController controller; + + @override + Widget build(BuildContext context) => CustomTextField(hintText: 'Collection name', controller: controller); +} + +class _PoemSelectionWidget extends StatelessWidget { + const _PoemSelectionWidget({required this.controller, this.poems}); + + final MultiSelectController controller; + final List? poems; + + @override + Widget build(BuildContext context) => SizedBox( + width: MediaQuery.of(context).size.width / 1.35, + child: SizedBox( + width: MediaQuery.of(context).size.width / 1.35, + child: MultiSelectDropDown( + borderRadius: 4, + borderWidth: 1.5, + backgroundColor: Colors.transparent, + hint: 'Select a poem to add', + controller: controller, + onOptionSelected: (selectedOptions) {}, + options: poems == null + ? >[] + : poems!.map( + (poem) => ValueItem(label: '${poem.author ?? ''}: ${poem.title}', value: poem), + ).toList(), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + ), + ); +} + +class _CreateButtonWidget extends StatelessWidget { + const _CreateButtonWidget(); + + @override + Widget build(BuildContext context) => FilledButton.tonal( + onPressed: () {}, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text('Create') + ), + ); +} From 4387eb067e0e3f3026175a0aff353c320ed55588 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:06:22 +0200 Subject: [PATCH 239/475] Update api service --- .../data_sources/remote/firebase_api_service.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 676cae2..cd6f0ff 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -11,6 +11,7 @@ abstract class FirebaseDatabaseService{ Future savePoem({required String userId, required PoemEntity poemEntity}); Future deletePoem({required PoemEntity poemEntity, required String userId, required String? collectionName}); Future isPoemExists({required PoemEntity poemEntity, required String userId}); + Future createNewCollection({required String userId, required String collectionName, required List poems}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -116,4 +117,18 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } return false; } + + @override + Future createNewCollection({required String userId, required String collectionName, required List poems}) async{ + final collectionsRef = FirebaseDatabase.instance.ref('$userId/collections'); + + final poemsJson = poems.map((poem) => poem.toJson()).toList(); + + final newCollectionData = { + 'name': collectionName, + 'poems': poemsJson, + }; + + await collectionsRef.push().set(newCollectionData); + } } From 7281d5c2561be934e629faf7fb864b846d3bd793 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:06:30 +0200 Subject: [PATCH 240/475] Update repository --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 1146a1d..f701a23 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -22,4 +22,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future isPoemExists({required PoemEntity poemEntity, required String userId}) => _databaseService.isPoemExists(poemEntity: poemEntity, userId: userId); + + @override + Future createNewCollection({required String userId, required String collectionName, required List poems}) => _databaseService.createNewCollection(userId: userId, collectionName: collectionName, poems: poems); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index bb656a7..29cb8a6 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -7,4 +7,5 @@ abstract class FirebaseDatabaseRepository { Future savePoem({required String userId, required PoemEntity poemEntity}); Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}); Future isPoemExists({required PoemEntity poemEntity, required String userId}); + Future createNewCollection({required String userId, required String collectionName, required List poems}); } From bda63e1d07e92b79938056d5b4c72ac3e225fa3b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:06:37 +0200 Subject: [PATCH 241/475] Create use case --- .../create_new_collection_params.dart | 10 ++++++++++ .../create_new_collection_usecase.dart | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_params.dart b/lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_params.dart new file mode 100644 index 0000000..7017be2 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_params.dart @@ -0,0 +1,10 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class CreateNewCollectionParams { + CreateNewCollectionParams({required this.userId, required this.collectionName, required this.poems}); + + + final String userId; + final String collectionName; + final List poems; +} diff --git a/lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart b/lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart new file mode 100644 index 0000000..910762a --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_params.dart'; + +class CreateNewCollectionUseCase implements UseCase{ + CreateNewCollectionUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({CreateNewCollectionParams? params}) async { + if(params != null){ + await _databaseRepository.createNewCollection( + userId: params.userId, + collectionName: params.collectionName, + poems: params.poems, + ); + } + } +} From f14ece6bdb911be837d3a78f20b7b46c19b0adc6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:06:42 +0200 Subject: [PATCH 242/475] Update cubit --- .../bloc/firebase_database_cubit.dart | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index fe13c15..1872082 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -2,6 +2,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/data/models/collection.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; @@ -13,13 +15,14 @@ import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; final SavePoemUseCase _savePoemUseCase; final DeletePoemUseCase _deletePoemUseCase; final IsPoemExistsUseCase _isPoemExistsUseCase; + final CreateNewCollectionUseCase _createNewCollectionUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -112,4 +115,22 @@ class FirebaseDatabaseCubit extends Cubit { return false; } } + + Future createNewCollection({required String userId, required String collectionName, required List poems}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + await _createNewCollectionUseCase( + params: CreateNewCollectionParams( + userId: userId, + collectionName: collectionName, + poems: poems, + ), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } + } } From 63fcaebdb87e8b95d1fa4c3722ace27a990e2dbe Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:06:46 +0200 Subject: [PATCH 243/475] Init DI --- lib/core/dependency_injection.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index de42381..4496b72 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -22,6 +22,7 @@ import 'package:poetlum/features/realtime_database/domain/entities/database_mana import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; import 'package:poetlum/features/saved_poems/data/repository/firebase_db_repository_impl.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; @@ -62,6 +63,7 @@ void initializeDependencies() { ..registerSingleton(SavePoemUseCase(getIt())) ..registerSingleton(DeletePoemUseCase(getIt())) ..registerSingleton(IsPoemExistsUseCase(getIt())) + ..registerSingleton(CreateNewCollectionUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -71,7 +73,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt(), getIt(), getIt())) + ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt(), getIt(), getIt(), getIt())) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), From d03ac49da85afdaf521896b6306634eee1dd88e1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:06:53 +0200 Subject: [PATCH 244/475] Implement stuff --- .../widgets/collection_bottom_sheet.dart | 58 ++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index 47fb1c8..aa3fcea 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -1,15 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class CollectionBottomSheetContent extends StatelessWidget { CollectionBottomSheetContent({super.key, required this.poems}); final List? poems; final TextEditingController _collectionNameController = TextEditingController(); - final MultiSelectController _controller = MultiSelectController(); + final MultiSelectController _selectController = MultiSelectController(); @override Widget build(BuildContext context) => SizedBox( @@ -22,9 +27,16 @@ class CollectionBottomSheetContent extends StatelessWidget { const CustomSpacer(heightFactor: 0.05), _CollectionNameInputWidget(controller: _collectionNameController), const CustomSpacer(heightFactor: 0.05), - _PoemSelectionWidget(controller: _controller, poems: poems), + _PoemSelectionWidget(controller: _selectController, poems: poems), const CustomSpacer(heightFactor: 0.05), - const _CreateButtonWidget(), + _CreateButtonWidget( + collectionName: _collectionNameController.text, + selectController: _selectController, + ), + TextButton(onPressed: (){ + print(_selectController.selectedOptions.length); + print(_selectController.selectedOptions.map((e) => e.value!).toList()); + }, child: Text('check')) ], ), ); @@ -42,7 +54,7 @@ class _TitleTextWidget extends StatelessWidget { class _CollectionNameInputWidget extends StatelessWidget { const _CollectionNameInputWidget({required this.controller}); - + final TextEditingController controller; @override @@ -81,14 +93,46 @@ class _PoemSelectionWidget extends StatelessWidget { } class _CreateButtonWidget extends StatelessWidget { - const _CreateButtonWidget(); + const _CreateButtonWidget({required this.collectionName, required this.selectController}); + + final String collectionName; + final MultiSelectController selectController; @override Widget build(BuildContext context) => FilledButton.tonal( - onPressed: () {}, + onPressed: () async { + print(selectController.selectedOptions.length); + print(selectController.selectedOptions.map((e) => e.value!).toList()); + + if(selectController.selectedOptions.isEmpty){ + await _showNegativeToast('Please select at least one poem to add to the collection'); + } else if(collectionName.isEmpty){ + await _showNegativeToast('Please provide the name for the collection'); + } + else{ + await context.read().createNewCollection( + userId: getIt().getCurrentUser().userId!, + collectionName: collectionName, + poems: selectController.selectedOptions.map( + (selectedOption) => selectedOption.value!, + ).toList(), + ); + } + }, child: const Padding( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Text('Create') + child: Text('Create'), ), ); + + Future _showNegativeToast(String error) async{ + await Fluttertoast.showToast( + msg: error, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); + } } From 83cef96d50ff702801f30e77234d3d5f2710f44a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:28:54 +0200 Subject: [PATCH 245/475] Fix collection save --- .../widgets/collection_bottom_sheet.dart | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index aa3fcea..8c86b16 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -9,12 +9,32 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.d import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -class CollectionBottomSheetContent extends StatelessWidget { - CollectionBottomSheetContent({super.key, required this.poems}); +class CollectionBottomSheetContent extends StatefulWidget { + const CollectionBottomSheetContent({super.key, required this.poems}); final List? poems; - final TextEditingController _collectionNameController = TextEditingController(); - final MultiSelectController _selectController = MultiSelectController(); + + @override + State createState() => _CollectionBottomSheetContentState(); +} + +class _CollectionBottomSheetContentState extends State { + late TextEditingController _collectionNameController; + late MultiSelectController _selectController; + + @override + void initState() { + super.initState(); + _collectionNameController = TextEditingController(); + _selectController = MultiSelectController(); + } + + @override + void dispose() { + _collectionNameController.dispose(); + _selectController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) => SizedBox( @@ -27,7 +47,7 @@ class CollectionBottomSheetContent extends StatelessWidget { const CustomSpacer(heightFactor: 0.05), _CollectionNameInputWidget(controller: _collectionNameController), const CustomSpacer(heightFactor: 0.05), - _PoemSelectionWidget(controller: _selectController, poems: poems), + _PoemSelectionWidget(controller: _selectController, poems: widget.poems), const CustomSpacer(heightFactor: 0.05), _CreateButtonWidget( collectionName: _collectionNameController.text, @@ -102,7 +122,6 @@ class _CreateButtonWidget extends StatelessWidget { Widget build(BuildContext context) => FilledButton.tonal( onPressed: () async { print(selectController.selectedOptions.length); - print(selectController.selectedOptions.map((e) => e.value!).toList()); if(selectController.selectedOptions.isEmpty){ await _showNegativeToast('Please select at least one poem to add to the collection'); @@ -117,6 +136,8 @@ class _CreateButtonWidget extends StatelessWidget { (selectedOption) => selectedOption.value!, ).toList(), ); + + await _showPositiveToast('The collection has been successfully saved'); } }, child: const Padding( @@ -125,6 +146,17 @@ class _CreateButtonWidget extends StatelessWidget { ), ); + Future _showPositiveToast(String text) async{ + await Fluttertoast.showToast( + msg: text, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + ); + } + Future _showNegativeToast(String error) async{ await Fluttertoast.showToast( msg: error, From 4fb5beb42c5ed6191196525517895ca8e2886268 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:36:39 +0200 Subject: [PATCH 246/475] Add loading indicator --- .../widgets/collection_bottom_sheet.dart | 62 +++++++++++-------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index 8c86b16..b12400f 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -8,6 +8,7 @@ import 'package:poetlum/features/poems_feed/domain/repository/user_repository.da import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class CollectionBottomSheetContent extends StatefulWidget { const CollectionBottomSheetContent({super.key, required this.poems}); @@ -119,32 +120,41 @@ class _CreateButtonWidget extends StatelessWidget { final MultiSelectController selectController; @override - Widget build(BuildContext context) => FilledButton.tonal( - onPressed: () async { - print(selectController.selectedOptions.length); - - if(selectController.selectedOptions.isEmpty){ - await _showNegativeToast('Please select at least one poem to add to the collection'); - } else if(collectionName.isEmpty){ - await _showNegativeToast('Please provide the name for the collection'); - } - else{ - await context.read().createNewCollection( - userId: getIt().getCurrentUser().userId!, - collectionName: collectionName, - poems: selectController.selectedOptions.map( - (selectedOption) => selectedOption.value!, - ).toList(), - ); - - await _showPositiveToast('The collection has been successfully saved'); - } - }, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Text('Create'), - ), - ); + Widget build(BuildContext context) => BlocConsumer( + listener: (context, state) { + if (state.status == FirebaseDatabaseStatus.success) { + _showPositiveToast('Your amazing poem has been saved! :D'); + } else if (state.status == FirebaseDatabaseStatus.error) { + _showNegativeToast('An error occurred :('); + } + }, + builder: (context, state) => state.status == FirebaseDatabaseStatus.submitting + ? const CircularProgressIndicator() + : FilledButton.tonal( + onPressed: () async { + if(selectController.selectedOptions.isEmpty){ + await _showNegativeToast('Please select at least one poem to add to the collection'); + } else if(collectionName.isEmpty){ + await _showNegativeToast('Please provide the name for the collection'); + } + else{ + await context.read().createNewCollection( + userId: getIt().getCurrentUser().userId!, + collectionName: collectionName, + poems: selectController.selectedOptions.map( + (selectedOption) => selectedOption.value!, + ).toList(), + ); + + await _showPositiveToast('The collection has been successfully saved'); + } + }, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text('Create'), + ), + ), + ); Future _showPositiveToast(String text) async{ await Fluttertoast.showToast( From 43076bc5b1687f27be45ee2807d6d3ed9e24c9cf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:38:27 +0200 Subject: [PATCH 247/475] Remove unnecessary listener --- .../presentation/widgets/collection_bottom_sheet.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index b12400f..f7245a3 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -122,9 +122,7 @@ class _CreateButtonWidget extends StatelessWidget { @override Widget build(BuildContext context) => BlocConsumer( listener: (context, state) { - if (state.status == FirebaseDatabaseStatus.success) { - _showPositiveToast('Your amazing poem has been saved! :D'); - } else if (state.status == FirebaseDatabaseStatus.error) { + if (state.status == FirebaseDatabaseStatus.error) { _showNegativeToast('An error occurred :('); } }, From 4ca909dea64f381044330186175e71d23ccf7e61 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:38:44 +0200 Subject: [PATCH 248/475] Remove debug widget --- .../presentation/widgets/collection_bottom_sheet.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index f7245a3..b31f0ee 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -54,10 +54,7 @@ class _CollectionBottomSheetContentState extends State e.value!).toList()); - }, child: Text('check')) + const CustomSpacer(heightFactor: 0.05), ], ), ); From 01cdc958889fec814474efd81ac2fc36667e4166 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:50:08 +0200 Subject: [PATCH 249/475] Refresh page when bottom sheet is disabled --- .../presentation/screens/saved_poems_screen.dart | 9 +++++++-- .../presentation/widgets/collection_bottom_sheet.dart | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 8a0a052..3c1df6f 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,6 +1,9 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -47,14 +50,16 @@ class _SavedPoemsScreenState extends State { children: [ FilledButton( child: const Text('Create a collection'), - onPressed: (){ - showModalBottomSheet( + onPressed: () async{ + await showModalBottomSheet( context: context, isScrollControlled: true, builder:(context) => CollectionBottomSheetContent( poems: collections?[0].poems, ), ); + + await context.read().getUserCollections(getIt().getCurrentUser().userId!); }, ), FilledButton.tonal( diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index b31f0ee..e1fc34b 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -34,6 +34,7 @@ class _CollectionBottomSheetContentState extends State Date: Fri, 1 Dec 2023 15:56:38 +0200 Subject: [PATCH 250/475] Fix refresh collections issue --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 3c1df6f..7c9374d 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -59,7 +59,7 @@ class _SavedPoemsScreenState extends State { ), ); - await context.read().getUserCollections(getIt().getCurrentUser().userId!); + collections = await context.read().getUserCollections(getIt().getCurrentUser().userId!); }, ), FilledButton.tonal( From fdbff75cf994e6eda1c0804929805551128cf9da Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:09:53 +0200 Subject: [PATCH 251/475] Fix empty text editing controller issue --- .../presentation/widgets/collection_bottom_sheet.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index e1fc34b..4f10417 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -10,6 +10,7 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_t import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +// TODO: Пофиксить проблему, что текстовое поле пустое пока не спрячешь клавиатуру class CollectionBottomSheetContent extends StatefulWidget { const CollectionBottomSheetContent({super.key, required this.poems}); @@ -52,7 +53,7 @@ class _CollectionBottomSheetContentState extends State selectController; @override @@ -130,13 +131,13 @@ class _CreateButtonWidget extends StatelessWidget { onPressed: () async { if(selectController.selectedOptions.isEmpty){ await _showNegativeToast('Please select at least one poem to add to the collection'); - } else if(collectionName.isEmpty){ + } else if(textController.text.isEmpty){ await _showNegativeToast('Please provide the name for the collection'); } else{ await context.read().createNewCollection( userId: getIt().getCurrentUser().userId!, - collectionName: collectionName, + collectionName: textController.text, poems: selectController.selectedOptions.map( (selectedOption) => selectedOption.value!, ).toList(), From 07681a7f0cdb08cc83fa02bb86a763eb62ad4912 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:12:22 +0200 Subject: [PATCH 252/475] Remove TODO --- .../presentation/widgets/collection_bottom_sheet.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart index 4f10417..58d18bb 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart @@ -10,7 +10,6 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_t import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -// TODO: Пофиксить проблему, что текстовое поле пустое пока не спрячешь клавиатуру class CollectionBottomSheetContent extends StatefulWidget { const CollectionBottomSheetContent({super.key, required this.poems}); From 8ea47fbd597912d3f21db606ed7af949b7244a80 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:16:26 +0200 Subject: [PATCH 253/475] Rename widget --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- .../saved_poems/presentation/widgets/collections_card.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 7c9374d..e1b051e 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -81,7 +81,7 @@ class _SavedPoemsScreenState extends State { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: collections!.length, - itemBuilder: (context, index) => CollectionsCard( + itemBuilder: (context, index) => CollectionCard( collection: collections![index], ), ), diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart index 192b339..9ec9a29 100644 --- a/lib/features/saved_poems/presentation/widgets/collections_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; -class CollectionsCard extends StatelessWidget { - const CollectionsCard({super.key, required this.collection}); +class CollectionCard extends StatelessWidget { + const CollectionCard({super.key, required this.collection}); final CollectionEntity collection; From 4ef3f95a6386373ca0481a66981fd6c4f2d42c30 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:26:06 +0200 Subject: [PATCH 254/475] Add dismissible widget --- .../widgets/collections_card.dart | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart index 9ec9a29..d15adbd 100644 --- a/lib/features/saved_poems/presentation/widgets/collections_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -7,30 +7,39 @@ class CollectionCard extends StatelessWidget { final CollectionEntity collection; @override - Widget build(BuildContext context) => GestureDetector( - //onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), - child: SizedBox( - height: MediaQuery.of(context).size.height / 4, - child: Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8)), - ), - elevation: 3, - margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Padding( - padding: const EdgeInsets.all(16), - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _TitleText(title: collection.name), - - Column( - children: collection.poems!.map( - (poem) => _InfoText(author: poem.author, title: poem.title), - ).toList(), - ), - ], + Widget build(BuildContext context) => Dismissible( + key: UniqueKey(), + direction: collection.isAllSavedPoems + ? DismissDirection.none + : DismissDirection.horizontal, + onDismissed: (direction) { + + }, + child: GestureDetector( + //onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), + child: SizedBox( + height: MediaQuery.of(context).size.height / 4, + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + elevation: 3, + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Padding( + padding: const EdgeInsets.all(16), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _TitleText(title: collection.name), + + Column( + children: collection.poems!.map( + (poem) => _InfoText(author: poem.author, title: poem.title), + ).toList(), + ), + ], + ), ), ), ), From e546caba291c75a3461bde3db984b448105885b6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:26:53 +0200 Subject: [PATCH 255/475] Update models to make all saved poems undismissible --- lib/features/saved_poems/data/models/collection.dart | 1 + lib/features/saved_poems/domain/entities/collection.dart | 3 ++- .../saved_poems/presentation/bloc/firebase_database_cubit.dart | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/data/models/collection.dart b/lib/features/saved_poems/data/models/collection.dart index 2ad1e5b..7890137 100644 --- a/lib/features/saved_poems/data/models/collection.dart +++ b/lib/features/saved_poems/data/models/collection.dart @@ -5,6 +5,7 @@ class CollectionModel extends CollectionEntity{ const CollectionModel({ super.name = '', super.poems = const [], + super.isAllSavedPoems = false, }); factory CollectionModel.fromFirebase(Map json) { diff --git a/lib/features/saved_poems/domain/entities/collection.dart b/lib/features/saved_poems/domain/entities/collection.dart index 9221c7f..419dbcd 100644 --- a/lib/features/saved_poems/domain/entities/collection.dart +++ b/lib/features/saved_poems/domain/entities/collection.dart @@ -2,10 +2,11 @@ import 'package:equatable/equatable.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class CollectionEntity extends Equatable{ - const CollectionEntity({this.name, this.poems}); + const CollectionEntity({this.name, this.poems, required this.isAllSavedPoems}); final String? name; final List? poems; + final bool isAllSavedPoems; @override List get props => [ diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 1872082..8dc00c7 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -50,7 +50,7 @@ class FirebaseDatabaseCubit extends Cubit { ); if(poems != null){ - collections.insert(0, CollectionModel(name: 'All saved poems', poems: poems)); + collections.insert(0, CollectionModel(name: 'All saved poems', poems: poems, isAllSavedPoems: true)); } emit(state.copyWith(status: FirebaseDatabaseStatus.success)); From 9e38b1fbada54e20f6a07a36a2946d78d6e0574b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:41:34 +0200 Subject: [PATCH 256/475] Add deleteCollection function --- .../remote/firebase_api_service.dart | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index cd6f0ff..683eeef 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid_dynamic_calls +// ignore_for_file: avoid_dynamic_calls, cascade_invocations import 'package:firebase_database/firebase_database.dart'; import 'package:poetlum/features/poems_feed/data/models/poem.dart'; @@ -12,6 +12,7 @@ abstract class FirebaseDatabaseService{ Future deletePoem({required PoemEntity poemEntity, required String userId, required String? collectionName}); Future isPoemExists({required PoemEntity poemEntity, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); + Future deleteCollection({required String userId, required String collectionName, required List poems}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -131,4 +132,50 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { await collectionsRef.push().set(newCollectionData); } + + @override + Future deleteCollection({required String userId, required String collectionName, required List poems}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + final collectionsRef = userRef.child('collections'); + + final collectionsSnapshot = await collectionsRef.get(); + + if (!collectionsSnapshot.exists) { + return; + } + + final collections = collectionsSnapshot.value as Map; + collections.forEach((key, value) { + if (value['name'] == collectionName) { + final collectionPoems = (value['poems'] as List) + .map((poem) { + final poemData = Map.from(poem as Map); + final poemModel = PoemModel.fromFirebase(poemData); + + return poemModel; + }) + .toList(); + + if (_arePoemListsEqual(collectionPoems, poems)) { + collectionsRef.child(key).remove(); + } + } + }); + } + + bool _arePoemListsEqual(List list1, List list2) { + if (list1.length != list2.length) return false; + + for (final poem in list1) { + if (!list2.any((p) => _isPoemEqual(p, poem))) { + return false; + } + } + return true; + } + + bool _isPoemEqual(PoemEntity poem1, PoemEntity poem2) => poem1.title == poem2.title && + poem1.author == poem2.author && + poem1.text == poem2.text && + poem1.linecount == poem2.linecount; } From c878459abafdb810e5c75c29f5c9cc922f60cd79 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:41:45 +0200 Subject: [PATCH 257/475] Add deleteCollection in the repository --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index f701a23..3112e35 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -25,4 +25,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future createNewCollection({required String userId, required String collectionName, required List poems}) => _databaseService.createNewCollection(userId: userId, collectionName: collectionName, poems: poems); + + @override + Future deleteCollection({required String userId, required String collectionName, required List poems}) => _databaseService.deleteCollection(userId: userId, collectionName: collectionName, poems: poems); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 29cb8a6..abe82a7 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -8,4 +8,5 @@ abstract class FirebaseDatabaseRepository { Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}); Future isPoemExists({required PoemEntity poemEntity, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); + Future deleteCollection({required String userId, required String collectionName, required List poems}); } From d981a205b71baf69c71c380d7521ffab483af526 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:42:01 +0200 Subject: [PATCH 258/475] Add deleteCollection user case --- .../delete_collection_params.dart | 10 ++++++++++ .../delete_collection_usecase.dart | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_params.dart b/lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_params.dart new file mode 100644 index 0000000..0536df9 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_params.dart @@ -0,0 +1,10 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class DeleteCollectionParams { + DeleteCollectionParams({required this.userId, required this.collectionName, required this.poems}); + + + final String userId; + final String collectionName; + final List poems; +} diff --git a/lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart b/lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart new file mode 100644 index 0000000..57b05e2 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_params.dart'; + +class DeleteCollectionUseCase implements UseCase{ + DeleteCollectionUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({DeleteCollectionParams? params}) async { + if(params != null){ + await _databaseRepository.deleteCollection( + userId: params.userId, + collectionName: params.collectionName, + poems: params.poems, + ); + } + } +} From 6524d7461397ed855ac5fe12e2f2f91da92ad5b9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:42:13 +0200 Subject: [PATCH 259/475] Add deleteCollection in the cubit --- .../bloc/firebase_database_cubit.dart | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 8dc00c7..847f013 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -4,6 +4,8 @@ import 'package:poetlum/features/saved_poems/data/models/collection.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; @@ -15,7 +17,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -23,6 +25,7 @@ class FirebaseDatabaseCubit extends Cubit { final DeletePoemUseCase _deletePoemUseCase; final IsPoemExistsUseCase _isPoemExistsUseCase; final CreateNewCollectionUseCase _createNewCollectionUseCase; + final DeleteCollectionUseCase _deleteCollectionUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -133,4 +136,22 @@ class FirebaseDatabaseCubit extends Cubit { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } } + + Future deleteCollection({required String userId, required String collectionName, required List poems}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + await _deleteCollectionUseCase( + params: DeleteCollectionParams( + userId: userId, + collectionName: collectionName, + poems: poems, + ), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } + } } From 7fd8678e6a822e62999fc07e49011eff40c233d1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:42:23 +0200 Subject: [PATCH 260/475] Init stuff in the DI --- lib/core/dependency_injection.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 4496b72..c38fab2 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -23,6 +23,7 @@ import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_a import 'package:poetlum/features/saved_poems/data/repository/firebase_db_repository_impl.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; @@ -64,6 +65,7 @@ void initializeDependencies() { ..registerSingleton(DeletePoemUseCase(getIt())) ..registerSingleton(IsPoemExistsUseCase(getIt())) ..registerSingleton(CreateNewCollectionUseCase(getIt())) + ..registerSingleton(DeleteCollectionUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -73,7 +75,17 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => FirebaseDatabaseCubit(getIt(), getIt(), getIt(), getIt(), getIt(), getIt())) + ..registerFactory( + () => FirebaseDatabaseCubit( + getIt(), + getIt(), + getIt(), + getIt(), + getIt(), + getIt(), + getIt(), + ), + ) ..registerFactory(() => RegisterFormValidationCubit( usernameValidator: getIt(), emailValidator: getIt(), From 5b7f98af34e8c669f9ba6e8a63f0f3dc4f15a233 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:42:43 +0200 Subject: [PATCH 261/475] Call cubit on dissmiss --- .../presentation/widgets/collections_card.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collections_card.dart index d15adbd..0632a51 100644 --- a/lib/features/saved_poems/presentation/widgets/collections_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collections_card.dart @@ -1,5 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class CollectionCard extends StatelessWidget { const CollectionCard({super.key, required this.collection}); @@ -13,7 +18,11 @@ class CollectionCard extends StatelessWidget { ? DismissDirection.none : DismissDirection.horizontal, onDismissed: (direction) { - + context.read().deleteCollection( + userId: getIt().getCurrentUser().userId!, + collectionName: collection.name ?? '', + poems: collection.poems ?? [], + ); }, child: GestureDetector( //onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), From 59508d2e4a26e36b395723090f7a14f20aa512b9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:57:16 +0200 Subject: [PATCH 262/475] Remove emits --- .../saved_poems/presentation/bloc/firebase_database_cubit.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 847f013..c595e67 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -138,8 +138,6 @@ class FirebaseDatabaseCubit extends Cubit { } Future deleteCollection({required String userId, required String collectionName, required List poems}) async{ - emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); - try{ await _deleteCollectionUseCase( params: DeleteCollectionParams( @@ -149,7 +147,6 @@ class FirebaseDatabaseCubit extends Cubit { ), ); - emit(state.copyWith(status: FirebaseDatabaseStatus.success)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } From 692973dd2c7988d6cc274d5a0cd76ca7e72f2dfb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:57:47 +0200 Subject: [PATCH 263/475] Rewrite function to delete only one similar collection --- .../data_sources/remote/firebase_api_service.dart | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 683eeef..25d5add 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -145,19 +145,23 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } final collections = collectionsSnapshot.value as Map; + var collectionDeleted = false; + collections.forEach((key, value) { + if (collectionDeleted) return; + if (value['name'] == collectionName) { final collectionPoems = (value['poems'] as List) .map((poem) { final poemData = Map.from(poem as Map); final poemModel = PoemModel.fromFirebase(poemData); - return poemModel; }) .toList(); if (_arePoemListsEqual(collectionPoems, poems)) { collectionsRef.child(key).remove(); + collectionDeleted = true; } } }); @@ -174,8 +178,9 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { return true; } - bool _isPoemEqual(PoemEntity poem1, PoemEntity poem2) => poem1.title == poem2.title && - poem1.author == poem2.author && - poem1.text == poem2.text && - poem1.linecount == poem2.linecount; + bool _isPoemEqual(PoemEntity poem1, PoemEntity poem2) => + poem1.title == poem2.title && + poem1.author == poem2.author && + poem1.text == poem2.text && + poem1.linecount == poem2.linecount; } From a1f74806419dee321fc23aa9f9c5bc7cb392fe74 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:12:50 +0200 Subject: [PATCH 264/475] Create SavedPoemViewPage template --- .../pages/saved_poem_view_page.dart | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart new file mode 100644 index 0000000..9fff24b --- /dev/null +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; + +class SavedPoemViewPage extends StatelessWidget { + const SavedPoemViewPage({super.key}); + + @override + Widget build(BuildContext context) { + final poemEntity = (ModalRoute.of(context)?.settings.arguments ?? const PoemEntity()) as PoemEntity; + + return Scaffold( + appBar: const CustomAppBar(title: 'Poetlum'), + body: SizedBox( + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.02), + CustomLikeButton(poemEntity: poemEntity), + const CustomSpacer(heightFactor: 0.02), + PoemTitle(title: poemEntity.title ?? ''), + const CustomSpacer(heightFactor: 0.02), + PoemAuthor(author: poemEntity.author ?? ''), + const CustomSpacer(heightFactor: 0.02), + PoemContent(text: poemEntity.text ?? ''), + const CustomSpacer(heightFactor: 0.02), + PoemLineCount(lineCount: poemEntity.linecount ?? 0), + const CustomSpacer(heightFactor: 0.02), + ], + ), + ), + ), + ), + ); + } +} From 2d80f2b6ae7dc009b5cd649e4dce7ecfedd4d922 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:13:13 +0200 Subject: [PATCH 265/475] Rename button --- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 +- .../presentation/widgets/poem_view/custom_like_button.dart | 4 ++-- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index f4c8484..1f704b6 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -25,7 +25,7 @@ class PoemViewPage extends StatelessWidget { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - CustomLikeButton(poemEntity: poemEntity), + CustomSaveButton(poemEntity: poemEntity), const CustomSpacer(heightFactor: 0.02), PoemTitle(title: poemEntity.title ?? ''), const CustomSpacer(heightFactor: 0.02), diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart index 12bcd07..7510525 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart @@ -6,8 +6,8 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -class CustomLikeButton extends StatelessWidget { - const CustomLikeButton({super.key, required this.poemEntity}); +class CustomSaveButton extends StatelessWidget { + const CustomSaveButton({super.key, required this.poemEntity}); final PoemEntity poemEntity; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 9fff24b..bcfe07f 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -25,7 +25,7 @@ class SavedPoemViewPage extends StatelessWidget { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - CustomLikeButton(poemEntity: poemEntity), + CustomSaveButton(poemEntity: poemEntity), const CustomSpacer(heightFactor: 0.02), PoemTitle(title: poemEntity.title ?? ''), const CustomSpacer(heightFactor: 0.02), From c98146950668677799b48b725494e90f5f7d4baf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:22:01 +0200 Subject: [PATCH 266/475] Create constant --- lib/core/constants/navigator_constants.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/constants/navigator_constants.dart b/lib/core/constants/navigator_constants.dart index 017cdb8..9f1c6a9 100644 --- a/lib/core/constants/navigator_constants.dart +++ b/lib/core/constants/navigator_constants.dart @@ -4,3 +4,4 @@ const String loginPageConstant = '/login'; const String screensWrapperPageConstant = '/screen_wrapper'; const String poemViewPageConstant = '/poem_view'; const String writePoemPageConstant = '/write_poem'; +const String savedPoemViewConstant = '/saved_poem_view'; From 8cbb467e22d43d8a2168d700caff8dfc0d375653 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:22:10 +0200 Subject: [PATCH 267/475] Register page --- lib/features/application/poetlum_app.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 8f15f56..715442b 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -8,6 +8,7 @@ import 'package:poetlum/features/application/presentation/pages/screens_wrapper. import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; +import 'package:poetlum/features/saved_poems/presentation/pages/saved_poem_view_page.dart'; import 'package:poetlum/features/saved_poems/presentation/pages/write_poem_page.dart'; class PoetlumApp extends StatelessWidget { @@ -25,6 +26,7 @@ class PoetlumApp extends StatelessWidget { screensWrapperPageConstant: (_) => const ScreensWrapper(), poemViewPageConstant:(_) => const PoemViewPage(), writePoemPageConstant: (_) => WritePoemPage(getIt()), + savedPoemViewConstant: (_) => const SavedPoemViewPage(), }, ); } From 02de8631cf85257f4341402afe0f861322f961dc Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:22:24 +0200 Subject: [PATCH 268/475] Add custom delete button --- .../pages/saved_poem_view_page.dart | 4 +- .../widgets/custom_delete_button.dart | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 lib/features/saved_poems/presentation/widgets/custom_delete_button.dart diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index bcfe07f..4cb30eb 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/custom_delete_button.dart'; class SavedPoemViewPage extends StatelessWidget { const SavedPoemViewPage({super.key}); @@ -25,7 +25,7 @@ class SavedPoemViewPage extends StatelessWidget { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - CustomSaveButton(poemEntity: poemEntity), + CustomDeleteButton(poemEntity: poemEntity), const CustomSpacer(heightFactor: 0.02), PoemTitle(title: poemEntity.title ?? ''), const CustomSpacer(heightFactor: 0.02), diff --git a/lib/features/saved_poems/presentation/widgets/custom_delete_button.dart b/lib/features/saved_poems/presentation/widgets/custom_delete_button.dart new file mode 100644 index 0000000..96cf892 --- /dev/null +++ b/lib/features/saved_poems/presentation/widgets/custom_delete_button.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:like_button/like_button.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; + +class CustomDeleteButton extends StatelessWidget { + const CustomDeleteButton({super.key, required this.poemEntity}); + + final PoemEntity poemEntity; + + @override + Widget build(BuildContext context) => FutureBuilder( + future: context.read().isPoemExists(poemEntity: poemEntity, userId: getIt().getCurrentUser().userId!), + builder: (context, snapshot) { + if(snapshot.connectionState == ConnectionState.waiting){ + return const CircularProgressIndicator(); + } else{ + return LikeButton( + isLiked: snapshot.data, + size: 42, + circleColor: CircleColor(start: Theme.of(context).colorScheme.primaryContainer, end: Theme.of(context).colorScheme.primary), + bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), + onTap: (isLiked) async { + if(isLiked == false){ + await context.read().savePoem( + userId: getIt().getCurrentUser().userId!, + username: poemEntity.author ?? '', + title: poemEntity.title ?? '', + text: poemEntity.text ?? '', + ); + } else{ + await context.read().deletePoem( + poemEntity: poemEntity, + userId: getIt().getCurrentUser().userId!, + ); + } + return !isLiked; + }, + likeBuilder: (isLiked) => Icon( + Icons.delete, + color: isLiked ? Theme.of(context).colorScheme.primary : Colors.grey, + size: 42, + ), + ); + } + }, + ); +} From 64b188dd6e283dd919ba90b67f059d4328418b05 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:23:21 +0200 Subject: [PATCH 269/475] Rename file --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- .../widgets/{collections_card.dart => collection_card.dart} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/features/saved_poems/presentation/widgets/{collections_card.dart => collection_card.dart} (100%) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index e1b051e..9d44902 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -9,7 +9,7 @@ import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/collections_card.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/collection_card.dart'; class SavedPoemsScreen extends StatefulWidget { const SavedPoemsScreen(this._userRepository, {super.key}); diff --git a/lib/features/saved_poems/presentation/widgets/collections_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart similarity index 100% rename from lib/features/saved_poems/presentation/widgets/collections_card.dart rename to lib/features/saved_poems/presentation/widgets/collection_card.dart From c66517bda72bd99479405d1ff7a21340e73ae17b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:46:38 +0200 Subject: [PATCH 270/475] Create savedCollectionView Constant --- lib/core/constants/navigator_constants.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/constants/navigator_constants.dart b/lib/core/constants/navigator_constants.dart index 9f1c6a9..9fa6daf 100644 --- a/lib/core/constants/navigator_constants.dart +++ b/lib/core/constants/navigator_constants.dart @@ -4,4 +4,5 @@ const String loginPageConstant = '/login'; const String screensWrapperPageConstant = '/screen_wrapper'; const String poemViewPageConstant = '/poem_view'; const String writePoemPageConstant = '/write_poem'; +const String savedCollectionViewConstant = '/saved_collection_view'; const String savedPoemViewConstant = '/saved_poem_view'; From e9929652f20d60b3a5f02b67b1fa45d0ce0868b4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:46:55 +0200 Subject: [PATCH 271/475] Register SavedCollectionView Page --- lib/features/application/poetlum_app.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 715442b..3d48ed3 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -8,6 +8,7 @@ import 'package:poetlum/features/application/presentation/pages/screens_wrapper. import 'package:poetlum/features/authorization/presentation/pages/login/login_page.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_view.dart'; +import 'package:poetlum/features/saved_poems/presentation/pages/saved_collection_view_page.dart'; import 'package:poetlum/features/saved_poems/presentation/pages/saved_poem_view_page.dart'; import 'package:poetlum/features/saved_poems/presentation/pages/write_poem_page.dart'; @@ -26,6 +27,7 @@ class PoetlumApp extends StatelessWidget { screensWrapperPageConstant: (_) => const ScreensWrapper(), poemViewPageConstant:(_) => const PoemViewPage(), writePoemPageConstant: (_) => WritePoemPage(getIt()), + savedCollectionViewConstant: (_) => const SavedCollectionViewPage(), savedPoemViewConstant: (_) => const SavedPoemViewPage(), }, ); From 152e90a3c60917837e965367ad3926853f697619 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:47:14 +0200 Subject: [PATCH 272/475] Create SavedCollectionViewPage --- .../pages/saved_collection_view_page.dart | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart new file mode 100644 index 0000000..0940b8a --- /dev/null +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; + +class SavedCollectionViewPage extends StatelessWidget { + const SavedCollectionViewPage({super.key}); + + @override + Widget build(BuildContext context){ + final collectionEntity = (ModalRoute.of(context)?.settings.arguments ?? const CollectionEntity(isAllSavedPoems: false)) as CollectionEntity; + + return Scaffold( + appBar: const CustomAppBar(title: 'Poetlum'), + body: ListView.builder( + itemCount: collectionEntity.poems?.length ?? 0, + itemBuilder: (__, index) => PoemCard( + poemEntity: collectionEntity.poems?[index] ?? const PoemEntity(), + ), + ), + ); + } +} From d74185cb519f93c07b13858c097a7f03fcbf4b91 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:49:23 +0200 Subject: [PATCH 273/475] Add redirrect to the savedCollectionViewPage --- .../saved_poems/presentation/widgets/collection_card.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 0632a51..796dbc5 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; @@ -25,7 +26,7 @@ class CollectionCard extends StatelessWidget { ); }, child: GestureDetector( - //onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), + onTap: () => Navigator.pushNamed(context, savedCollectionViewConstant, arguments: collection), child: SizedBox( height: MediaQuery.of(context).size.height / 4, child: Card( From 90b62bf45916b23c53c7e63cc5dc66eb47b151a9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 19:05:44 +0200 Subject: [PATCH 274/475] Add route parameter --- .../poems_feed/presentation/screens/poems_feed_screen.dart | 2 ++ .../presentation/widgets/poems_feed/poem_card.dart | 6 +++--- .../presentation/pages/saved_collection_view_page.dart | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index 544e0ab..1ce2602 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; @@ -23,6 +24,7 @@ class PoemsFeedScreen extends StatelessWidget { return ListView.builder( itemCount: state.poems!.length, itemBuilder: (__, index) => PoemCard( + route: poemViewPageConstant, poemEntity: state.poems![index], ), ); diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart index e243dff..dad394b 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class PoemCard extends StatelessWidget { - const PoemCard({super.key, required this.poemEntity}); + const PoemCard({super.key, required this.poemEntity, required this.route}); final PoemEntity poemEntity; + final String route; @override Widget build(BuildContext context) => GestureDetector( - onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), + onTap: () => Navigator.pushNamed(context, route, arguments: poemEntity), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 0940b8a..c4828e6 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart'; @@ -16,6 +17,7 @@ class SavedCollectionViewPage extends StatelessWidget { body: ListView.builder( itemCount: collectionEntity.poems?.length ?? 0, itemBuilder: (__, index) => PoemCard( + route: savedPoemViewConstant, poemEntity: collectionEntity.poems?[index] ?? const PoemEntity(), ), ), From bd4446f0c24e41008747d8247a73842eeea799b4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 19:05:55 +0200 Subject: [PATCH 275/475] Delete button --- .../widgets/custom_delete_button.dart | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 lib/features/saved_poems/presentation/widgets/custom_delete_button.dart diff --git a/lib/features/saved_poems/presentation/widgets/custom_delete_button.dart b/lib/features/saved_poems/presentation/widgets/custom_delete_button.dart deleted file mode 100644 index 96cf892..0000000 --- a/lib/features/saved_poems/presentation/widgets/custom_delete_button.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:like_button/like_button.dart'; -import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; - -class CustomDeleteButton extends StatelessWidget { - const CustomDeleteButton({super.key, required this.poemEntity}); - - final PoemEntity poemEntity; - - @override - Widget build(BuildContext context) => FutureBuilder( - future: context.read().isPoemExists(poemEntity: poemEntity, userId: getIt().getCurrentUser().userId!), - builder: (context, snapshot) { - if(snapshot.connectionState == ConnectionState.waiting){ - return const CircularProgressIndicator(); - } else{ - return LikeButton( - isLiked: snapshot.data, - size: 42, - circleColor: CircleColor(start: Theme.of(context).colorScheme.primaryContainer, end: Theme.of(context).colorScheme.primary), - bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), - onTap: (isLiked) async { - if(isLiked == false){ - await context.read().savePoem( - userId: getIt().getCurrentUser().userId!, - username: poemEntity.author ?? '', - title: poemEntity.title ?? '', - text: poemEntity.text ?? '', - ); - } else{ - await context.read().deletePoem( - poemEntity: poemEntity, - userId: getIt().getCurrentUser().userId!, - ); - } - return !isLiked; - }, - likeBuilder: (isLiked) => Icon( - Icons.delete, - color: isLiked ? Theme.of(context).colorScheme.primary : Colors.grey, - size: 42, - ), - ); - } - }, - ); -} From 2bc56d11a4d8efb04525a8f58cb7ffd4992ee542 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 19:06:05 +0200 Subject: [PATCH 276/475] Add delete button placeholder --- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 4cb30eb..9bd2a8b 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -6,7 +6,6 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_ import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/custom_delete_button.dart'; class SavedPoemViewPage extends StatelessWidget { const SavedPoemViewPage({super.key}); @@ -25,7 +24,7 @@ class SavedPoemViewPage extends StatelessWidget { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - CustomDeleteButton(poemEntity: poemEntity), + SizedBox(height: 60, width: 60, child: IconButton.filledTonal(onPressed: (){}, icon: const Icon(Icons.delete, size: 30))), const CustomSpacer(heightFactor: 0.02), PoemTitle(title: poemEntity.title ?? ''), const CustomSpacer(heightFactor: 0.02), From 9d8acd988e43928140e059ce17245f5af8f7c316 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 3 Dec 2023 19:11:02 +0200 Subject: [PATCH 277/475] Create deletePoemFromCollection function --- .../remote/firebase_api_service.dart | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 25d5add..c7f8d6e 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -13,6 +13,7 @@ abstract class FirebaseDatabaseService{ Future isPoemExists({required PoemEntity poemEntity, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); + Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -183,4 +184,40 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { poem1.author == poem2.author && poem1.text == poem2.text && poem1.linecount == poem2.linecount; + + @override + Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + final collectionsRef = userRef.child('collections'); + + final collectionsSnapshot = await collectionsRef.get(); + + if (!collectionsSnapshot.exists) { + return; + } + + final collections = collectionsSnapshot.value as Map; + + for (final key in collections.keys) { + final value = collections[key]; + if (value['name'] == collectionName && value['poems'] != null) { + final collectionPoemsRef = collectionsRef.child('$key/poems'); + final collectionPoemsSnapshot = await collectionPoemsRef.get(); + + if (collectionPoemsSnapshot.exists) { + final collectionPoems = collectionPoemsSnapshot.value as Map; + for (final poemKey in collectionPoems.keys) { + final poemValue = collectionPoems[poemKey]; + final poemData = Map.from(poemValue as Map); + final poemModel = PoemModel.fromFirebase(poemData); + + if (_isPoemEqual(poemModel, poemToDelete)) { + await collectionPoemsRef.child(poemKey).remove(); + break; + } + } + } + } + } + } } From b67478cef24b622ef326595207ee2e5c78dc846d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:59:59 +0200 Subject: [PATCH 278/475] Create deletePoemFromCollection function --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 3112e35..80181a8 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -28,4 +28,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future deleteCollection({required String userId, required String collectionName, required List poems}) => _databaseService.deleteCollection(userId: userId, collectionName: collectionName, poems: poems); + + @override + Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}) => _databaseService.deletePoemFromCollection(userId: userId, collectionName: collectionName, poemToDelete: poemToDelete); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index abe82a7..a6a549b 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -9,4 +9,5 @@ abstract class FirebaseDatabaseRepository { Future isPoemExists({required PoemEntity poemEntity, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); + Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}); } From fc42a723c5a650e1dbed483f102345dd2eb30147 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 4 Dec 2023 23:00:28 +0200 Subject: [PATCH 279/475] Create use case --- .../delete_poem_from_collection_params.dart | 9 +++++++++ .../delete_poem_from_collection_usecase.dart | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart b/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart new file mode 100644 index 0000000..7d53d51 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart @@ -0,0 +1,9 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class DeletePoemFromCollectionParams { + DeletePoemFromCollectionParams({required this.poemEntity, required this.userId, required this.collectionName}); + + final PoemEntity poemEntity; + final String userId; + final String collectionName; +} diff --git a/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart b/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart new file mode 100644 index 0000000..800feba --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart'; + +class DeletePoemFromCollectionUseCase implements UseCase{ + DeletePoemFromCollectionUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({DeletePoemFromCollectionParams? params}) async { + if(params != null){ + await _databaseRepository.deletePoemFromCollection( + poemToDelete: params.poemEntity, + userId: params.userId, + collectionName: params.collectionName, + ); + } + } +} From fbd15b877a5869f88232324eab7f40fa8ad60dbe Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 4 Dec 2023 23:00:33 +0200 Subject: [PATCH 280/475] Init DI --- lib/core/dependency_injection.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index c38fab2..8b1b145 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -25,6 +25,7 @@ import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repos import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collection/create_new_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; @@ -66,6 +67,7 @@ void initializeDependencies() { ..registerSingleton(IsPoemExistsUseCase(getIt())) ..registerSingleton(CreateNewCollectionUseCase(getIt())) ..registerSingleton(DeleteCollectionUseCase(getIt())) + ..registerSingleton(DeletePoemFromCollectionUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -84,6 +86,7 @@ void initializeDependencies() { getIt(), getIt(), getIt(), + getIt(), ), ) ..registerFactory(() => RegisterFormValidationCubit( From c6af4e4e95ccf9cc34f8b177a741eb468147ac90 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 4 Dec 2023 23:00:39 +0200 Subject: [PATCH 281/475] Add to the cubit --- .../bloc/firebase_database_cubit.dart | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index c595e67..2976ecb 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -8,6 +8,8 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/d import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart'; @@ -17,7 +19,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -26,6 +28,7 @@ class FirebaseDatabaseCubit extends Cubit { final IsPoemExistsUseCase _isPoemExistsUseCase; final CreateNewCollectionUseCase _createNewCollectionUseCase; final DeleteCollectionUseCase _deleteCollectionUseCase; + final DeletePoemFromCollectionUseCase _deletePoemFromCollectionUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -151,4 +154,22 @@ class FirebaseDatabaseCubit extends Cubit { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } } + + Future deletePoemFromCollection({required PoemEntity poemEntity, required String userId, required String collectionName}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + await _deletePoemFromCollectionUseCase( + params: DeletePoemFromCollectionParams( + userId: userId, + collectionName: collectionName, + poemEntity: poemEntity, + ), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } + } } From 7ded8d477738876cea2dd0dbbd2e3566ba834592 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 4 Dec 2023 23:05:24 +0200 Subject: [PATCH 282/475] Remove route property --- .../poems_feed/presentation/screens/poems_feed_screen.dart | 2 -- .../presentation/widgets/poems_feed/poem_card.dart | 6 +++--- .../presentation/pages/saved_collection_view_page.dart | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index 1ce2602..544e0ab 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; @@ -24,7 +23,6 @@ class PoemsFeedScreen extends StatelessWidget { return ListView.builder( itemCount: state.poems!.length, itemBuilder: (__, index) => PoemCard( - route: poemViewPageConstant, poemEntity: state.poems![index], ), ); diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart index dad394b..e243dff 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class PoemCard extends StatelessWidget { - const PoemCard({super.key, required this.poemEntity, required this.route}); + const PoemCard({super.key, required this.poemEntity}); final PoemEntity poemEntity; - final String route; @override Widget build(BuildContext context) => GestureDetector( - onTap: () => Navigator.pushNamed(context, route, arguments: poemEntity), + onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index c4828e6..0940b8a 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart'; @@ -17,7 +16,6 @@ class SavedCollectionViewPage extends StatelessWidget { body: ListView.builder( itemCount: collectionEntity.poems?.length ?? 0, itemBuilder: (__, index) => PoemCard( - route: savedPoemViewConstant, poemEntity: collectionEntity.poems?[index] ?? const PoemEntity(), ), ), From 9879286d924b5047a9a31fafe2e2169a7b6fe6cd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 5 Dec 2023 01:45:50 +0200 Subject: [PATCH 283/475] Create separate SavedPoemCard widget --- .../pages/saved_collection_view_page.dart | 5 +- .../pages/saved_poem_view_page.dart | 16 +++- .../presentation/widgets/saved_poem_card.dart | 81 +++++++++++++++++++ 3 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 lib/features/saved_poems/presentation/widgets/saved_poem_card.dart diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 0940b8a..077cabe 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; class SavedCollectionViewPage extends StatelessWidget { const SavedCollectionViewPage({super.key}); @@ -15,8 +15,9 @@ class SavedCollectionViewPage extends StatelessWidget { appBar: const CustomAppBar(title: 'Poetlum'), body: ListView.builder( itemCount: collectionEntity.poems?.length ?? 0, - itemBuilder: (__, index) => PoemCard( + itemBuilder: (__, index) => SavedPoemCard( poemEntity: collectionEntity.poems?[index] ?? const PoemEntity(), + collectionEntity: collectionEntity, ), ), ); diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 9bd2a8b..98c606a 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,18 +1,24 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class SavedPoemViewPage extends StatelessWidget { const SavedPoemViewPage({super.key}); @override Widget build(BuildContext context) { - final poemEntity = (ModalRoute.of(context)?.settings.arguments ?? const PoemEntity()) as PoemEntity; + final poemEntity = (ModalRoute.of(context)?.settings.arguments as Map)['poem'] as PoemEntity; + final collectionEntity = (ModalRoute.of(context)?.settings.arguments as Map)['collection'] as CollectionEntity; return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), @@ -24,7 +30,13 @@ class SavedPoemViewPage extends StatelessWidget { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - SizedBox(height: 60, width: 60, child: IconButton.filledTonal(onPressed: (){}, icon: const Icon(Icons.delete, size: 30))), + SizedBox(height: 60, width: 60, child: IconButton.filledTonal(onPressed: () async{ + await context.read().deletePoemFromCollection( + poemEntity: poemEntity, + userId: getIt().getCurrentUser().userId!, + collectionName: collectionEntity.name ?? '', + ); + }, icon: const Icon(Icons.delete, size: 30))), const CustomSpacer(heightFactor: 0.02), PoemTitle(title: poemEntity.title ?? ''), const CustomSpacer(heightFactor: 0.02), diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart new file mode 100644 index 0000000..def4106 --- /dev/null +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; + +class SavedPoemCard extends StatelessWidget { + const SavedPoemCard({super.key, required this.poemEntity, required this.collectionEntity}); + + final PoemEntity poemEntity; + final CollectionEntity collectionEntity; + + @override + Widget build(BuildContext context) => GestureDetector( + onTap: () => Navigator.pushNamed(context, savedPoemViewConstant, arguments: {'poem': poemEntity, 'collection': collectionEntity}), + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + elevation: 3, + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TitleText(title: poemEntity.title), + const SizedBox(height: 8), + _AuthorText(author: poemEntity.author), + const SizedBox(height: 16), + _PoemText(text: poemEntity.text, maxLength: 250), + ], + ), + ), + ), + ); +} + +class _TitleText extends StatelessWidget { + const _TitleText({required this.title}); + + final String? title; + + @override + Widget build(BuildContext context) => Text( + title ?? 'Untitled', + style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + ); +} + +class _AuthorText extends StatelessWidget { + const _AuthorText({required this.author}); + + final String? author; + + @override + Widget build(BuildContext context) => Text( + author ?? 'Unknown Author', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ); +} + +class _PoemText extends StatelessWidget { + const _PoemText({required this.text, required this.maxLength}); + + final String? text; + final int maxLength; + + @override + Widget build(BuildContext context) { + if (text == null || text!.isEmpty) { + return const Text('No content available.', + style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic), + ); + } + return Text( + text!.length > maxLength ? '${text!.substring(0, maxLength)}...' : text!, + style: const TextStyle(fontSize: 14), + ); + } +} From d3e0b7dafb2f1b8bb27d529b3eb077087f6018d9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 5 Dec 2023 01:46:13 +0200 Subject: [PATCH 284/475] Update deletePoemFromCollection --- .../data_sources/remote/firebase_api_service.dart | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index c7f8d6e..fdedd15 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -205,14 +205,12 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { final collectionPoemsSnapshot = await collectionPoemsRef.get(); if (collectionPoemsSnapshot.exists) { - final collectionPoems = collectionPoemsSnapshot.value as Map; - for (final poemKey in collectionPoems.keys) { - final poemValue = collectionPoems[poemKey]; - final poemData = Map.from(poemValue as Map); - final poemModel = PoemModel.fromFirebase(poemData); - + final collectionPoems = collectionPoemsSnapshot.value as List; + for (final poemData in collectionPoems) { + final poemModel = PoemModel.fromFirebase(Map.from(poemData as Map)); if (_isPoemEqual(poemModel, poemToDelete)) { - await collectionPoemsRef.child(poemKey).remove(); + final poemIndex = collectionPoems.indexOf(poemData); + await collectionPoemsRef.child('$poemIndex').remove(); break; } } From 17f6e3b8d413bef1cd723e1b812febb25d062cec Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:58:27 +0200 Subject: [PATCH 285/475] Update deletePoemFromCollection to delete null entries --- .../data_sources/remote/firebase_api_service.dart | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index fdedd15..e3d4878 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -206,14 +206,15 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { if (collectionPoemsSnapshot.exists) { final collectionPoems = collectionPoemsSnapshot.value as List; - for (final poemData in collectionPoems) { + final updatedPoems = collectionPoems.where((poemData) { + if (poemData == null) return false; + final poemModel = PoemModel.fromFirebase(Map.from(poemData as Map)); - if (_isPoemEqual(poemModel, poemToDelete)) { - final poemIndex = collectionPoems.indexOf(poemData); - await collectionPoemsRef.child('$poemIndex').remove(); - break; - } - } + + return !_isPoemEqual(poemModel, poemToDelete); + }).toList(); + + await collectionPoemsRef.set(updatedPoems); } } } From 298855ca20c6a207246f87e86ba7054d99dd12be Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:02:30 +0200 Subject: [PATCH 286/475] Refresh collections when user deletes a poem --- .../presentation/bloc/firebase_database_cubit.dart | 1 + .../presentation/bloc/firebase_database_state.dart | 2 +- .../presentation/screens/saved_poems_screen.dart | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 2976ecb..fc29ebf 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -168,6 +168,7 @@ class FirebaseDatabaseCubit extends Cubit { ); emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + emit(state.copyWith(status: FirebaseDatabaseStatus.needsRefresh)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart index b0a23b9..06e3c14 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.dart'; -enum FirebaseDatabaseStatus{initial, submitting, success, error} +enum FirebaseDatabaseStatus{initial, submitting, success, error, needsRefresh} class FirebaseDatabaseState extends Equatable{ const FirebaseDatabaseState({ diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 9d44902..0f2785d 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -35,7 +35,12 @@ class _SavedPoemsScreenState extends State { } @override - Widget build(BuildContext context) => BlocBuilder( + Widget build(BuildContext context) => BlocConsumer( + listener: (context, state) { + if (state.status == FirebaseDatabaseStatus.needsRefresh) { + initCollections(); + } + }, builder: (context, state) { if (state.status == FirebaseDatabaseStatus.submitting) { return const Center(child: CircularProgressIndicator()); From 55d2263a7f1b8b821079a49d52771b8807cc9d48 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:33:40 +0200 Subject: [PATCH 287/475] Make SavedPoemCard dismissible --- .../pages/saved_poem_view_page.dart | 15 +----- .../presentation/widgets/saved_poem_card.dart | 54 ++++++++++++------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 98c606a..12d2827 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,24 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; -import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class SavedPoemViewPage extends StatelessWidget { const SavedPoemViewPage({super.key}); @override Widget build(BuildContext context) { - final poemEntity = (ModalRoute.of(context)?.settings.arguments as Map)['poem'] as PoemEntity; - final collectionEntity = (ModalRoute.of(context)?.settings.arguments as Map)['collection'] as CollectionEntity; + final poemEntity = ModalRoute.of(context)?.settings.arguments as PoemEntity; return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), @@ -30,13 +24,6 @@ class SavedPoemViewPage extends StatelessWidget { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - SizedBox(height: 60, width: 60, child: IconButton.filledTonal(onPressed: () async{ - await context.read().deletePoemFromCollection( - poemEntity: poemEntity, - userId: getIt().getCurrentUser().userId!, - collectionName: collectionEntity.name ?? '', - ); - }, icon: const Icon(Icons.delete, size: 30))), const CustomSpacer(heightFactor: 0.02), PoemTitle(title: poemEntity.title ?? ''), const CustomSpacer(heightFactor: 0.02), diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index def4106..3bc57b9 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -1,7 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class SavedPoemCard extends StatelessWidget { const SavedPoemCard({super.key, required this.poemEntity, required this.collectionEntity}); @@ -10,26 +14,36 @@ class SavedPoemCard extends StatelessWidget { final CollectionEntity collectionEntity; @override - Widget build(BuildContext context) => GestureDetector( - onTap: () => Navigator.pushNamed(context, savedPoemViewConstant, arguments: {'poem': poemEntity, 'collection': collectionEntity}), - child: Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8)), - ), - elevation: 3, - margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _TitleText(title: poemEntity.title), - const SizedBox(height: 8), - _AuthorText(author: poemEntity.author), - const SizedBox(height: 16), - _PoemText(text: poemEntity.text, maxLength: 250), - ], + Widget build(BuildContext context) => Dismissible( + key: UniqueKey(), + onDismissed: (direction) async{ + await context.read().deletePoemFromCollection( + poemEntity: poemEntity, + userId: getIt().getCurrentUser().userId!, + collectionName: collectionEntity.name ?? '', + ); + }, + child: GestureDetector( + onTap: () => Navigator.pushNamed(context, savedPoemViewConstant, arguments: poemEntity), + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + elevation: 3, + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TitleText(title: poemEntity.title), + const SizedBox(height: 8), + _AuthorText(author: poemEntity.author), + const SizedBox(height: 16), + _PoemText(text: poemEntity.text, maxLength: 250), + ], + ), ), ), ), From 8c8d59ac27486cd87659f5f1878996d187137c01 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:58:08 +0200 Subject: [PATCH 288/475] Add poems nullable support --- .../saved_poems/data/models/collection.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/features/saved_poems/data/models/collection.dart b/lib/features/saved_poems/data/models/collection.dart index 7890137..4674480 100644 --- a/lib/features/saved_poems/data/models/collection.dart +++ b/lib/features/saved_poems/data/models/collection.dart @@ -9,12 +9,15 @@ class CollectionModel extends CollectionEntity{ }); factory CollectionModel.fromFirebase(Map json) { - final poemsJson = json['poems'] as List; - final poems = poemsJson.map((poemJson) { - final poemMap = Map.from(poemJson as Map); - - return PoemModel.fromFirebase(poemMap); - }).toList(); + final poemsJson = json['poems'] as List?; + List? poems; + + if (poemsJson != null) { + poems = poemsJson.map((poemJson) { + final poemMap = Map.from(poemJson as Map); + return PoemModel.fromFirebase(poemMap); + }).toList(); + } return CollectionModel( name: json['name'] ?? '', From 5125abfd09728cfea05d87b632dbae9d1d54f028 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 15:00:38 +0200 Subject: [PATCH 289/475] Add placeholder text if poems list is null --- .../presentation/widgets/collection_card.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 796dbc5..8c0dcf2 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -43,11 +43,11 @@ class CollectionCard extends StatelessWidget { children: [ _TitleText(title: collection.name), - Column( + if (collection.poems != null) Column( children: collection.poems!.map( (poem) => _InfoText(author: poem.author, title: poem.title), ).toList(), - ), + ) else const _EmptyCollectionText(), ], ), ), @@ -85,3 +85,16 @@ class _InfoText extends StatelessWidget { ), ); } + +class _EmptyCollectionText extends StatelessWidget { + const _EmptyCollectionText(); + + @override + Widget build(BuildContext context) => const SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Text( + 'The collection is currently empty, but not for long ✍️', + style: TextStyle(fontSize: 17), + ), + ); +} \ No newline at end of file From 31adc1f4719d71ff491cf68248a21708cd400a95 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:40:29 +0200 Subject: [PATCH 290/475] Add an ability to delete from "All saved poems" collection --- .../remote/firebase_api_service.dart | 26 ++++++++++++++++--- .../firebase_db_repository_impl.dart | 2 +- .../repository/firebase_db_repository.dart | 2 +- .../delete_poem_from_collection_params.dart | 4 +-- .../bloc/firebase_database_cubit.dart | 2 +- .../presentation/widgets/saved_poem_card.dart | 17 ++++++++---- 6 files changed, 39 insertions(+), 14 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index e3d4878..67ca9f5 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -13,7 +13,7 @@ abstract class FirebaseDatabaseService{ Future isPoemExists({required PoemEntity poemEntity, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); - Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}); + Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -186,10 +186,27 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { poem1.linecount == poem2.linecount; @override - Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}) async { - final userRef = FirebaseDatabase.instance.ref(userId); - final collectionsRef = userRef.child('collections'); + Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + + if (collectionName == null) { + final poemsRef = userRef.child('poems'); + final poemQuery = poemsRef.orderByChild('title').equalTo(poemToDelete.title); + + final snapshot = await poemQuery.get(); + if (snapshot.exists) { + final poems = snapshot.value as Map; + poems.forEach((key, value) { + final poemData = Map.from(value as Map); + final poemModel = PoemModel.fromFirebase(poemData); + if (_isPoemEqual(poemModel, poemToDelete)) { + poemsRef.child(key).remove(); + } + }); + } + } else { + final collectionsRef = userRef.child('collections'); final collectionsSnapshot = await collectionsRef.get(); if (!collectionsSnapshot.exists) { @@ -220,3 +237,4 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } } } +} diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 80181a8..90b0aa8 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -30,5 +30,5 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ Future deleteCollection({required String userId, required String collectionName, required List poems}) => _databaseService.deleteCollection(userId: userId, collectionName: collectionName, poems: poems); @override - Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}) => _databaseService.deletePoemFromCollection(userId: userId, collectionName: collectionName, poemToDelete: poemToDelete); + Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}) => _databaseService.deletePoemFromCollection(userId: userId, collectionName: collectionName, poemToDelete: poemToDelete); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index a6a549b..e7fd506 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -9,5 +9,5 @@ abstract class FirebaseDatabaseRepository { Future isPoemExists({required PoemEntity poemEntity, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); - Future deletePoemFromCollection({required String userId, required String collectionName, required PoemEntity poemToDelete}); + Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); } diff --git a/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart b/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart index 7d53d51..51c7f9f 100644 --- a/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart +++ b/lib/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart @@ -1,9 +1,9 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class DeletePoemFromCollectionParams { - DeletePoemFromCollectionParams({required this.poemEntity, required this.userId, required this.collectionName}); + DeletePoemFromCollectionParams({required this.poemEntity, required this.userId, this.collectionName}); final PoemEntity poemEntity; final String userId; - final String collectionName; + final String? collectionName; } diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index fc29ebf..f138e1e 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -155,7 +155,7 @@ class FirebaseDatabaseCubit extends Cubit { } } - Future deletePoemFromCollection({required PoemEntity poemEntity, required String userId, required String collectionName}) async{ + Future deletePoemFromCollection({required PoemEntity poemEntity, required String userId, String? collectionName}) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); try{ diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 3bc57b9..69b65b4 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -17,11 +17,18 @@ class SavedPoemCard extends StatelessWidget { Widget build(BuildContext context) => Dismissible( key: UniqueKey(), onDismissed: (direction) async{ - await context.read().deletePoemFromCollection( - poemEntity: poemEntity, - userId: getIt().getCurrentUser().userId!, - collectionName: collectionEntity.name ?? '', - ); + if(collectionEntity.isAllSavedPoems){ + await context.read().deletePoemFromCollection( + poemEntity: poemEntity, + userId: getIt().getCurrentUser().userId!, + ); + } else{ + await context.read().deletePoemFromCollection( + poemEntity: poemEntity, + userId: getIt().getCurrentUser().userId!, + collectionName: collectionEntity.name ?? '', + ); + } }, child: GestureDetector( onTap: () => Navigator.pushNamed(context, savedPoemViewConstant, arguments: poemEntity), From 1f42de3488f6de21a15c7c068019b0f80b940bb7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:47:04 +0200 Subject: [PATCH 291/475] Add indentation --- .../remote/firebase_api_service.dart | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 67ca9f5..1572879 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -187,54 +187,54 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @override Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}) async { - final userRef = FirebaseDatabase.instance.ref(userId); + final userRef = FirebaseDatabase.instance.ref(userId); - if (collectionName == null) { - final poemsRef = userRef.child('poems'); - final poemQuery = poemsRef.orderByChild('title').equalTo(poemToDelete.title); + if (collectionName == null) { + final poemsRef = userRef.child('poems'); + final poemQuery = poemsRef.orderByChild('title').equalTo(poemToDelete.title); + + final snapshot = await poemQuery.get(); + if (snapshot.exists) { + final poems = snapshot.value as Map; + poems.forEach((key, value) { + final poemData = Map.from(value as Map); + final poemModel = PoemModel.fromFirebase(poemData); + + if (_isPoemEqual(poemModel, poemToDelete)) { + poemsRef.child(key).remove(); + } + }); + } + } else { + final collectionsRef = userRef.child('collections'); + final collectionsSnapshot = await collectionsRef.get(); - final snapshot = await poemQuery.get(); - if (snapshot.exists) { - final poems = snapshot.value as Map; - poems.forEach((key, value) { - final poemData = Map.from(value as Map); - final poemModel = PoemModel.fromFirebase(poemData); + if (!collectionsSnapshot.exists) { + return; + } - if (_isPoemEqual(poemModel, poemToDelete)) { - poemsRef.child(key).remove(); - } - }); - } - } else { - final collectionsRef = userRef.child('collections'); - final collectionsSnapshot = await collectionsRef.get(); + final collections = collectionsSnapshot.value as Map; + + for (final key in collections.keys) { + final value = collections[key]; + if (value['name'] == collectionName && value['poems'] != null) { + final collectionPoemsRef = collectionsRef.child('$key/poems'); + final collectionPoemsSnapshot = await collectionPoemsRef.get(); - if (!collectionsSnapshot.exists) { - return; - } + if (collectionPoemsSnapshot.exists) { + final collectionPoems = collectionPoemsSnapshot.value as List; + final updatedPoems = collectionPoems.where((poemData) { + if (poemData == null) return false; - final collections = collectionsSnapshot.value as Map; - - for (final key in collections.keys) { - final value = collections[key]; - if (value['name'] == collectionName && value['poems'] != null) { - final collectionPoemsRef = collectionsRef.child('$key/poems'); - final collectionPoemsSnapshot = await collectionPoemsRef.get(); - - if (collectionPoemsSnapshot.exists) { - final collectionPoems = collectionPoemsSnapshot.value as List; - final updatedPoems = collectionPoems.where((poemData) { - if (poemData == null) return false; - - final poemModel = PoemModel.fromFirebase(Map.from(poemData as Map)); - - return !_isPoemEqual(poemModel, poemToDelete); - }).toList(); - - await collectionPoemsRef.set(updatedPoems); + final poemModel = PoemModel.fromFirebase(Map.from(poemData as Map)); + + return !_isPoemEqual(poemModel, poemToDelete); + }).toList(); + + await collectionPoemsRef.set(updatedPoems); + } } } } } } -} From 3ae3d5b6d841ccc34161a8a33d24e9637a0248d0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:15:13 +0200 Subject: [PATCH 292/475] Create FloatingActionButton template --- .../pages/saved_collection_view_page.dart | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 077cabe..3ba526d 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -1,7 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; class SavedCollectionViewPage extends StatelessWidget { @@ -13,6 +18,22 @@ class SavedCollectionViewPage extends StatelessWidget { return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), + floatingActionButton: FloatingActionButton( + onPressed: () async { + await showModalBottomSheet( + context: context, + isScrollControlled: true, + builder:(context) => CollectionBottomSheetContent( + poems: collectionEntity.poems, + ), + ); + + final savedPoems = await context.read().getUserPoems( + getIt().getCurrentUser().userId!, + ); + }, + child: const Icon(Icons.add), + ), body: ListView.builder( itemCount: collectionEntity.poems?.length ?? 0, itemBuilder: (__, index) => SavedPoemCard( From fceb7b9b015d28699cc5ee39f3c8006257bddece Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:16:54 +0200 Subject: [PATCH 293/475] Rename CreateCollectionBottomSheetContent --- .../presentation/pages/saved_collection_view_page.dart | 4 ++-- .../presentation/screens/saved_poems_screen.dart | 4 ++-- ...tom_sheet.dart => create_collection_bottom_sheet.dart} | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) rename lib/features/saved_poems/presentation/widgets/{collection_bottom_sheet.dart => create_collection_bottom_sheet.dart} (94%) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 3ba526d..2fa2b55 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -6,7 +6,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; class SavedCollectionViewPage extends StatelessWidget { @@ -23,7 +23,7 @@ class SavedCollectionViewPage extends StatelessWidget { await showModalBottomSheet( context: context, isScrollControlled: true, - builder:(context) => CollectionBottomSheetContent( + builder:(context) => CreateCollectionBottomSheetContent( poems: collectionEntity.poems, ), ); diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 0f2785d..2ffca87 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -8,7 +8,7 @@ import 'package:poetlum/features/poems_feed/domain/repository/user_repository.da import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/collection_card.dart'; class SavedPoemsScreen extends StatefulWidget { @@ -59,7 +59,7 @@ class _SavedPoemsScreenState extends State { await showModalBottomSheet( context: context, isScrollControlled: true, - builder:(context) => CollectionBottomSheetContent( + builder:(context) => CreateCollectionBottomSheetContent( poems: collections?[0].poems, ), ); diff --git a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart similarity index 94% rename from lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart rename to lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 58d18bb..3ab6ddf 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -10,16 +10,16 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_t import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -class CollectionBottomSheetContent extends StatefulWidget { - const CollectionBottomSheetContent({super.key, required this.poems}); +class CreateCollectionBottomSheetContent extends StatefulWidget { + const CreateCollectionBottomSheetContent({super.key, required this.poems}); final List? poems; @override - State createState() => _CollectionBottomSheetContentState(); + State createState() => _CreateCollectionBottomSheetContentState(); } -class _CollectionBottomSheetContentState extends State { +class _CreateCollectionBottomSheetContentState extends State { late TextEditingController _collectionNameController; late MultiSelectController _selectController; From a200e0fa5843af1ecf789f91785fa2cecb3f52f3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:05:52 +0200 Subject: [PATCH 294/475] Add an ability to delete empty collections --- .../remote/firebase_api_service.dart | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 1572879..855ef99 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -152,17 +152,24 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { if (collectionDeleted) return; if (value['name'] == collectionName) { - final collectionPoems = (value['poems'] as List) - .map((poem) { - final poemData = Map.from(poem as Map); - final poemModel = PoemModel.fromFirebase(poemData); - return poemModel; - }) - .toList(); - - if (_arePoemListsEqual(collectionPoems, poems)) { + final collectionPoemsJson = value['poems'] as List?; + + if (collectionPoemsJson == null || collectionPoemsJson.isEmpty) { collectionsRef.child(key).remove(); collectionDeleted = true; + } else { + final collectionPoems = collectionPoemsJson + .map((poem) { + final poemData = Map.from(poem as Map); + final poemModel = PoemModel.fromFirebase(poemData); + return poemModel; + }) + .toList(); + + if (_arePoemListsEqual(collectionPoems, poems)) { + collectionsRef.child(key).remove(); + collectionDeleted = true; + } } } }); From 8ed352a8e09ed0cce9e6d6ccce718509db80016f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:50:40 +0200 Subject: [PATCH 295/475] Add UpdatePoemsInCollection function --- .../remote/firebase_api_service.dart | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 855ef99..417507c 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -14,6 +14,7 @@ abstract class FirebaseDatabaseService{ Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); + Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -244,4 +245,38 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } } } + + @override + Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + final collectionsRef = userRef.child('collections'); + + final collectionsSnapshot = await collectionsRef.get(); + + if (!collectionsSnapshot.exists) { + return; + } + + final collections = collectionsSnapshot.value as Map; + + String? targetCollectionKey; + for (var key in collections.keys) { + final collectionData = collections[key]; + if (collectionData['name'] == collectionName) { + targetCollectionKey = key; + break; + } + } + + if (targetCollectionKey != null) { + final updatedPoemsData = updatedPoems.map((poem) => { + 'author': poem.author, + 'linecount': poem.linecount, + 'text': poem.text, + 'title': poem.title + }).toList(); + + await collectionsRef.child('$targetCollectionKey/poems').set(updatedPoemsData); + } + } } From 9ce53c15de8fad4c6479abf9a2018bb5569a075c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:50:50 +0200 Subject: [PATCH 296/475] Add UpdatePoemsInCollection in the repo --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 90b0aa8..df0a6b5 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -31,4 +31,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}) => _databaseService.deletePoemFromCollection(userId: userId, collectionName: collectionName, poemToDelete: poemToDelete); + + @override + Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}) => _databaseService.updatePoemsInCollection(userId: userId, collectionName: collectionName, updatedPoems: updatedPoems); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index e7fd506..29fac62 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -10,4 +10,5 @@ abstract class FirebaseDatabaseRepository { Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); + Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); } From 6f91cdd308474ec5eaf8122f9101585c76cf90f7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:50:58 +0200 Subject: [PATCH 297/475] Create use case --- .../update_poems_in_collection_params.dart | 9 +++++++++ .../update_poems_in_collection_usecase.dart | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart b/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart new file mode 100644 index 0000000..f576c54 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart @@ -0,0 +1,9 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class UpdatePoemsInCollectionParams { + UpdatePoemsInCollectionParams(this.updatedPoems, {required this.userId, required this.collectionName}); + + final String userId; + final String collectionName; + final List updatedPoems; +} diff --git a/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart b/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart new file mode 100644 index 0000000..82482df --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart'; + +class UpdatePoemsInCollectionUseCase implements UseCase{ + UpdatePoemsInCollectionUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({UpdatePoemsInCollectionParams? params}) async { + if(params != null){ + await _databaseRepository.updatePoemsInCollection( + userId: params.userId, + collectionName: params.collectionName, + updatedPoems: params.updatedPoems, + ); + } + } +} From 21e77510ba1c5591079417998372bde4b7841bdd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:51:05 +0200 Subject: [PATCH 298/475] Init UpdatePoemsInCollectionUseCase in the DI --- lib/core/dependency_injection.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 8b1b145..b34a215 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -30,6 +30,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collection import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; GetIt getIt = GetIt.instance; @@ -68,6 +69,7 @@ void initializeDependencies() { ..registerSingleton(CreateNewCollectionUseCase(getIt())) ..registerSingleton(DeleteCollectionUseCase(getIt())) ..registerSingleton(DeletePoemFromCollectionUseCase(getIt())) + ..registerSingleton(UpdatePoemsInCollectionUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) From 00fcd50f45f964b2229fc51fb3ceaf7e4091014f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:56:11 +0200 Subject: [PATCH 299/475] Update cubit --- lib/core/dependency_injection.dart | 1 + .../update_poems_in_collection_params.dart | 2 +- .../bloc/firebase_database_cubit.dart | 20 ++++++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index b34a215..3875574 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -89,6 +89,7 @@ void initializeDependencies() { getIt(), getIt(), getIt(), + getIt(), ), ) ..registerFactory(() => RegisterFormValidationCubit( diff --git a/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart b/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart index f576c54..7106a36 100644 --- a/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart +++ b/lib/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart @@ -1,7 +1,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class UpdatePoemsInCollectionParams { - UpdatePoemsInCollectionParams(this.updatedPoems, {required this.userId, required this.collectionName}); + UpdatePoemsInCollectionParams({required this.userId, required this.collectionName, required this.updatedPoems}); final String userId; final String collectionName; diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index f138e1e..8c71e08 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -16,10 +16,12 @@ import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_p import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -29,6 +31,7 @@ class FirebaseDatabaseCubit extends Cubit { final CreateNewCollectionUseCase _createNewCollectionUseCase; final DeleteCollectionUseCase _deleteCollectionUseCase; final DeletePoemFromCollectionUseCase _deletePoemFromCollectionUseCase; + final UpdatePoemsInCollectionUseCase _updatePoemsInCollectionUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -173,4 +176,19 @@ class FirebaseDatabaseCubit extends Cubit { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } } + + Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}) async{ + try{ + await _updatePoemsInCollectionUseCase( + params: UpdatePoemsInCollectionParams( + userId: userId, + collectionName: collectionName, + updatedPoems: updatedPoems, + ), + ); + + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } + } } From 6217b64acab53a7957b45c427395601708445b82 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:30:26 +0200 Subject: [PATCH 300/475] Add missed emits --- .../presentation/bloc/firebase_database_cubit.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 8c71e08..f1f7d5b 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -144,6 +144,8 @@ class FirebaseDatabaseCubit extends Cubit { } Future deleteCollection({required String userId, required String collectionName, required List poems}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + try{ await _deleteCollectionUseCase( params: DeleteCollectionParams( @@ -152,7 +154,7 @@ class FirebaseDatabaseCubit extends Cubit { poems: poems, ), ); - + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } @@ -178,6 +180,8 @@ class FirebaseDatabaseCubit extends Cubit { } Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + try{ await _updatePoemsInCollectionUseCase( params: UpdatePoemsInCollectionParams( @@ -186,7 +190,7 @@ class FirebaseDatabaseCubit extends Cubit { updatedPoems: updatedPoems, ), ); - + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } From 474138004ba78e66909c3e237cca651c5f650360 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:30:56 +0200 Subject: [PATCH 301/475] Create bottom sheet --- .../pages/saved_collection_view_page.dart | 16 +- ...dd_to_collection_bottom_sheet_content.dart | 173 ++++++++++++++++++ 2 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 lib/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 2fa2b55..991aa25 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -6,7 +6,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; class SavedCollectionViewPage extends StatelessWidget { @@ -20,17 +20,19 @@ class SavedCollectionViewPage extends StatelessWidget { appBar: const CustomAppBar(title: 'Poetlum'), floatingActionButton: FloatingActionButton( onPressed: () async { + final savedPoems = await context.read().getUserPoems( + getIt().getCurrentUser().userId!, + ); + await showModalBottomSheet( context: context, isScrollControlled: true, - builder:(context) => CreateCollectionBottomSheetContent( - poems: collectionEntity.poems, + builder:(context) => AddToCollectionBottomSheetContent( + collectionName: collectionEntity.name ?? '', + poemsInTheCollection: collectionEntity.poems, + allSavedPoems: savedPoems, ), ); - - final savedPoems = await context.read().getUserPoems( - getIt().getCurrentUser().userId!, - ); }, child: const Icon(Icons.add), ), diff --git a/lib/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart new file mode 100644 index 0000000..42dcbc5 --- /dev/null +++ b/lib/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart @@ -0,0 +1,173 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; + +class AddToCollectionBottomSheetContent extends StatefulWidget { + const AddToCollectionBottomSheetContent({super.key, this.allSavedPoems, this.poemsInTheCollection, required this.collectionName}); + + final List? allSavedPoems; + final List? poemsInTheCollection; + final String collectionName; + + @override + State createState() => _AddToCollectionBottomSheetContentState(); +} + +class _AddToCollectionBottomSheetContentState extends State { + late MultiSelectController _selectController; + late List> selectedValues = >[]; + late List> allValues = >[]; + + @override + void initState() { + super.initState(); + _selectController = MultiSelectController(); + + _initPoemsInTheCollectionValues(); + _initAllSavedPoemsValues(); + } + + void _initPoemsInTheCollectionValues(){ + if(widget.poemsInTheCollection != null){ + selectedValues = widget.poemsInTheCollection!.map( + (poem) => ValueItem(label: '${poem.author}: ${poem.title}', value: poem), + ).toList(); + } + } + + void _initAllSavedPoemsValues(){ + if(widget.allSavedPoems != null){ + allValues = widget.allSavedPoems!.map( + (poem) => ValueItem(label: '${poem.author}: ${poem.title}', value: poem), + ).toList(); + } + } + + @override + void dispose() { + _selectController.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height / 1.15, + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.05), + const _TitleTextWidget(), + const CustomSpacer(heightFactor: 0.05), + _PoemSelectionWidget(controller: _selectController, allValues: allValues, selectedValues: selectedValues), + const CustomSpacer(heightFactor: 0.05), + _EditButtonWidget(selectController: _selectController, collectionName: widget.collectionName), + const CustomSpacer(heightFactor: 0.05), + ], + ), + ); +} + +class _TitleTextWidget extends StatelessWidget { + const _TitleTextWidget(); + + @override + Widget build(BuildContext context) => const Text( + 'Edit the collection', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + ); +} + +class _PoemSelectionWidget extends StatelessWidget { + const _PoemSelectionWidget({required this.controller, required this.allValues, required this.selectedValues}); + + final MultiSelectController controller; + final List> allValues; + final List> selectedValues; + + @override + Widget build(BuildContext context) => SizedBox( + width: MediaQuery.of(context).size.width / 1.35, + child: MultiSelectDropDown( + borderRadius: 4, + borderWidth: 1.5, + backgroundColor: Colors.transparent, + hint: 'Select a poem to add', + controller: controller, + onOptionSelected: (selectedOptions) {}, + options: allValues, + dropdownHeight: 300, + selectedOptions: selectedValues, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + ); +} + +class _EditButtonWidget extends StatelessWidget { + const _EditButtonWidget({required this.selectController, required this.collectionName}); + + final MultiSelectController selectController; + final String collectionName; + + @override + Widget build(BuildContext context) => BlocConsumer( + listener: (context, state) { + if (state.status == FirebaseDatabaseStatus.error) { + _showNegativeToast('An error occurred :('); + } + }, + builder: (context, state) => state.status == FirebaseDatabaseStatus.submitting + ? const CircularProgressIndicator() + : FilledButton.tonal( + onPressed: () async { + if(selectController.selectedOptions.isEmpty){ + await _showNegativeToast('Please select at least one poem to add to the collection'); + } else{ + await context.read().updatePoemsInCollection( + userId: getIt().getCurrentUser().userId!, + collectionName: collectionName, + updatedPoems: selectController.selectedOptions.map( + (selectedOption) => selectedOption.value!, + ).toList(), + ); + + await _showPositiveToast('The collection has been successfully saved'); + } + }, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text('Edit'), + ), + ), + ); + + Future _showPositiveToast(String text) async{ + await Fluttertoast.showToast( + msg: text, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + ); + } + + Future _showNegativeToast(String error) async{ + await Fluttertoast.showToast( + msg: error, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); + } +} From 2e1aec0992d6dd26119b4ed0736a5977cd1f350c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:34:11 +0200 Subject: [PATCH 302/475] Rename reference --- .../presentation/pages/saved_collection_view_page.dart | 4 ++-- ...t.dart => update_collection_bottom_sheet_content.dart} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename lib/features/saved_poems/presentation/widgets/{add_to_collection_bottom_sheet_content.dart => update_collection_bottom_sheet_content.dart} (93%) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 991aa25..c67ee08 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -6,7 +6,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; class SavedCollectionViewPage extends StatelessWidget { @@ -27,7 +27,7 @@ class SavedCollectionViewPage extends StatelessWidget { await showModalBottomSheet( context: context, isScrollControlled: true, - builder:(context) => AddToCollectionBottomSheetContent( + builder:(context) => UpdateCollectionBottomSheetContent( collectionName: collectionEntity.name ?? '', poemsInTheCollection: collectionEntity.poems, allSavedPoems: savedPoems, diff --git a/lib/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart similarity index 93% rename from lib/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart rename to lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 42dcbc5..537e282 100644 --- a/lib/features/saved_poems/presentation/widgets/add_to_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -9,18 +9,18 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.d import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -class AddToCollectionBottomSheetContent extends StatefulWidget { - const AddToCollectionBottomSheetContent({super.key, this.allSavedPoems, this.poemsInTheCollection, required this.collectionName}); +class UpdateCollectionBottomSheetContent extends StatefulWidget { + const UpdateCollectionBottomSheetContent({super.key, this.allSavedPoems, this.poemsInTheCollection, required this.collectionName}); final List? allSavedPoems; final List? poemsInTheCollection; final String collectionName; @override - State createState() => _AddToCollectionBottomSheetContentState(); + State createState() => _UpdateCollectionBottomSheetContentState(); } -class _AddToCollectionBottomSheetContentState extends State { +class _UpdateCollectionBottomSheetContentState extends State { late MultiSelectController _selectController; late List> selectedValues = >[]; late List> allValues = >[]; From 8801078d46f16efe3acbfbceb43019861f89079a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:35:28 +0200 Subject: [PATCH 303/475] Update saved poems screen on edit --- .../saved_poems/presentation/bloc/firebase_database_cubit.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index f1f7d5b..d979d6c 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -191,6 +191,7 @@ class FirebaseDatabaseCubit extends Cubit { ), ); emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + emit(state.copyWith(status: FirebaseDatabaseStatus.needsRefresh)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } From b3d0d4786f9ee025f7f873334fb772b356333051 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:46:25 +0200 Subject: [PATCH 304/475] Organize imports --- .../presentation/pages/saved_collection_view_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index c67ee08..033ecc2 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -6,8 +6,8 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart'; class SavedCollectionViewPage extends StatelessWidget { const SavedCollectionViewPage({super.key}); From 40b557a32c962493d8239a5e4ae33dff6079994a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:43:51 +0200 Subject: [PATCH 305/475] Create getPoemsInCollection function --- .../remote/firebase_api_service.dart | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 417507c..6427b5b 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -15,6 +15,7 @@ abstract class FirebaseDatabaseService{ Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); + Future> getPoemsInCollection({required String userId, required String collectionName}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -279,4 +280,34 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { await collectionsRef.child('$targetCollectionKey/poems').set(updatedPoemsData); } } + + @override + Future> getPoemsInCollection({required String userId, required String collectionName}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + final collectionsRef = userRef.child('collections'); + + final collectionsSnapshot = await collectionsRef.get(); + + if (!collectionsSnapshot.exists) { + return []; + } + + final collections = collectionsSnapshot.value as Map; + var poems = []; + + + for (final key in collections.keys) { + final collectionData = collections[key]; + if (collectionData['name'] == collectionName && collectionData['poems'] != null) { + final poemsData = collectionData['poems'] as List; + poems = poemsData.map((poemJson) { + final poemMap = Map.from(poemJson as Map); + return PoemModel.fromFirebase(poemMap); + }).toList(); + break; + } + } + + return poems; + } } From 79e9cecc8658985870d470ea78e273258b0b3dbe Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:44:03 +0200 Subject: [PATCH 306/475] Create getPoemsInCollection in the repo --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index df0a6b5..11f22f3 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -34,4 +34,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}) => _databaseService.updatePoemsInCollection(userId: userId, collectionName: collectionName, updatedPoems: updatedPoems); + + @override + Future> getPoemsInCollection({required String userId, required String collectionName}) => _databaseService.getPoemsInCollection(userId: userId, collectionName: collectionName); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 29fac62..7f55e9e 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -11,4 +11,5 @@ abstract class FirebaseDatabaseRepository { Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); + Future> getPoemsInCollection({required String userId, required String collectionName}); } From 3ab3bcf4cc8be0f8c8799830a3e88d7a43a363c6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:44:13 +0200 Subject: [PATCH 307/475] Create use case for getPoemsInCollection --- .../get_poems_in_collection_params.dart | 6 +++++ .../get_poems_in_collection_usecase.dart | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart b/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart new file mode 100644 index 0000000..50be4b8 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart @@ -0,0 +1,6 @@ +class GetPoemsInCollectionParams { + GetPoemsInCollectionParams({required this.collectionName, required this.userId}); + + final String collectionName; + final String userId; +} diff --git a/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart b/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart new file mode 100644 index 0000000..80b009b --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart @@ -0,0 +1,24 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart'; + +class GetPoemsInCollectionUseCase implements UseCase{ + GetPoemsInCollectionUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future> call({GetPoemsInCollectionParams? params}) async { + if(params != null){ + final result = await _databaseRepository.getPoemsInCollection( + userId: params.userId, + collectionName: params.collectionName, + ); + + return result; + } + + return []; + } +} From e97ece8e6b7650fde171fa3351f7434fb071d0df Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:44:23 +0200 Subject: [PATCH 308/475] Init use case in the DI --- lib/core/dependency_injection.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 3875574..2362fe6 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -26,6 +26,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collecti import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; @@ -70,6 +71,7 @@ void initializeDependencies() { ..registerSingleton(DeleteCollectionUseCase(getIt())) ..registerSingleton(DeletePoemFromCollectionUseCase(getIt())) ..registerSingleton(UpdatePoemsInCollectionUseCase(getIt())) + ..registerSingleton(GetPoemsInCollectionUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -90,6 +92,7 @@ void initializeDependencies() { getIt(), getIt(), getIt(), + getIt(), ), ) ..registerFactory(() => RegisterFormValidationCubit( From 7dc918c6b796121ca00d4bc8dc81c024ae88444e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:45:10 +0200 Subject: [PATCH 309/475] Create getPoemsInCollection in the cubit --- .../bloc/firebase_database_cubit.dart | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index d979d6c..b32e771 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -10,6 +10,8 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart'; @@ -21,7 +23,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_col import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -32,6 +34,7 @@ class FirebaseDatabaseCubit extends Cubit { final DeleteCollectionUseCase _deleteCollectionUseCase; final DeletePoemFromCollectionUseCase _deletePoemFromCollectionUseCase; final UpdatePoemsInCollectionUseCase _updatePoemsInCollectionUseCase; + final GetPoemsInCollectionUseCase _getPoemsInCollectionUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -196,4 +199,25 @@ class FirebaseDatabaseCubit extends Cubit { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } } + + Future> getPoemsInCollection({required String userId, required String collectionName}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + final result = await _getPoemsInCollectionUseCase( + params: GetPoemsInCollectionParams( + userId: userId, + collectionName: collectionName, + ), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + + return result; + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + + return []; + } + } } From bfffa206c7b09ead23cd37a873701d5a66100339 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:45:25 +0200 Subject: [PATCH 310/475] Fetch poems on page loading --- .../pages/saved_collection_view_page.dart | 79 ++++++++++++++----- 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 033ecc2..a510e43 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -6,42 +6,85 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart'; -class SavedCollectionViewPage extends StatelessWidget { +class SavedCollectionViewPage extends StatefulWidget { const SavedCollectionViewPage({super.key}); + @override + State createState() => _SavedCollectionViewPageState(); +} + +class _SavedCollectionViewPageState extends State { + List poems = []; + late CollectionEntity collectionEntity; + bool isInit = true; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (isInit) { + collectionEntity = (ModalRoute.of(context)?.settings.arguments ?? const CollectionEntity(isAllSavedPoems: false)) as CollectionEntity; + initPoems(); + isInit = false; + } + } + + Future initPoems() async { + poems = await context.read().getPoemsInCollection( + userId: getIt().getCurrentUser().userId!, + collectionName: collectionEntity.name ?? '', + ); + } + @override Widget build(BuildContext context){ - final collectionEntity = (ModalRoute.of(context)?.settings.arguments ?? const CollectionEntity(isAllSavedPoems: false)) as CollectionEntity; + final localContext = context; return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), floatingActionButton: FloatingActionButton( + tooltip: 'Edit a collection', onPressed: () async { final savedPoems = await context.read().getUserPoems( getIt().getCurrentUser().userId!, ); - await showModalBottomSheet( - context: context, - isScrollControlled: true, - builder:(context) => UpdateCollectionBottomSheetContent( - collectionName: collectionEntity.name ?? '', - poemsInTheCollection: collectionEntity.poems, - allSavedPoems: savedPoems, - ), - ); + if (mounted){ + await showModalBottomSheet( + context: localContext, + isScrollControlled: true, + builder:(context) => UpdateCollectionBottomSheetContent( + collectionName: collectionEntity.name ?? '', + poemsInTheCollection: collectionEntity.poems, + allSavedPoems: savedPoems, + ), + ); + } }, - child: const Icon(Icons.add), + child: const Icon(Icons.edit), ), - body: ListView.builder( - itemCount: collectionEntity.poems?.length ?? 0, - itemBuilder: (__, index) => SavedPoemCard( - poemEntity: collectionEntity.poems?[index] ?? const PoemEntity(), - collectionEntity: collectionEntity, - ), + body: BlocConsumer( + listener: (context, state) { + if (state.status == FirebaseDatabaseStatus.needsRefresh) { + initPoems(); + } + }, + builder: (context, state) { + if (state.status == FirebaseDatabaseStatus.submitting) { + return const Center(child: CircularProgressIndicator()); + } else { + return ListView.builder( + itemCount: poems.length, + itemBuilder: (__, index) => SavedPoemCard( + poemEntity: poems[index], + collectionEntity: collectionEntity, + ), + ); + } + }, ), ); } From 3e8527510927d8b56bca2b786cd20b9d260ef89e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:58:21 +0200 Subject: [PATCH 311/475] Add support to "All saved poems" --- .../remote/firebase_api_service.dart | 56 ++++++++++++------- .../firebase_db_repository_impl.dart | 2 +- .../repository/firebase_db_repository.dart | 2 +- .../get_poems_in_collection_params.dart | 4 +- .../bloc/firebase_database_cubit.dart | 2 +- .../pages/saved_collection_view_page.dart | 4 +- 6 files changed, 43 insertions(+), 27 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 6427b5b..e793943 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -15,7 +15,7 @@ abstract class FirebaseDatabaseService{ Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); - Future> getPoemsInCollection({required String userId, required String collectionName}); + Future> getPoemsInCollection({required String userId, String? collectionName}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -282,32 +282,46 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } @override - Future> getPoemsInCollection({required String userId, required String collectionName}) async { + Future> getPoemsInCollection({required String userId, String? collectionName}) async { final userRef = FirebaseDatabase.instance.ref(userId); - final collectionsRef = userRef.child('collections'); - final collectionsSnapshot = await collectionsRef.get(); + if (collectionName == null) { + final poemsRef = userRef.child('poems'); + final poemsSnapshot = await poemsRef.get(); - if (!collectionsSnapshot.exists) { - return []; - } + if (!poemsSnapshot.exists) { + return []; + } - final collections = collectionsSnapshot.value as Map; - var poems = []; - + final poemsData = poemsSnapshot.value as Map; + return poemsData.values.map((poemJson) { + final poemMap = Map.from(poemJson as Map); + return PoemModel.fromFirebase(poemMap); + }).toList(); + } else { + final collectionsRef = userRef.child('collections'); + final collectionsSnapshot = await collectionsRef.get(); - for (final key in collections.keys) { - final collectionData = collections[key]; - if (collectionData['name'] == collectionName && collectionData['poems'] != null) { - final poemsData = collectionData['poems'] as List; - poems = poemsData.map((poemJson) { - final poemMap = Map.from(poemJson as Map); - return PoemModel.fromFirebase(poemMap); - }).toList(); - break; + if (!collectionsSnapshot.exists) { + return []; + } + + final collections = collectionsSnapshot.value as Map; + var poems = []; + + for (final key in collections.keys) { + final collectionData = collections[key]; + if (collectionData['name'] == collectionName && collectionData['poems'] != null) { + final poemsData = collectionData['poems'] as List; + poems = poemsData.map((poemJson) { + final poemMap = Map.from(poemJson as Map); + return PoemModel.fromFirebase(poemMap); + }).toList(); + break; + } } - } - return poems; + return poems; + } } } diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 11f22f3..114d86a 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -36,5 +36,5 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}) => _databaseService.updatePoemsInCollection(userId: userId, collectionName: collectionName, updatedPoems: updatedPoems); @override - Future> getPoemsInCollection({required String userId, required String collectionName}) => _databaseService.getPoemsInCollection(userId: userId, collectionName: collectionName); + Future> getPoemsInCollection({required String userId, String? collectionName}) => _databaseService.getPoemsInCollection(userId: userId, collectionName: collectionName); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 7f55e9e..4048423 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -11,5 +11,5 @@ abstract class FirebaseDatabaseRepository { Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); - Future> getPoemsInCollection({required String userId, required String collectionName}); + Future> getPoemsInCollection({required String userId, String? collectionName}); } diff --git a/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart b/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart index 50be4b8..4d9704b 100644 --- a/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart +++ b/lib/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart @@ -1,6 +1,6 @@ class GetPoemsInCollectionParams { - GetPoemsInCollectionParams({required this.collectionName, required this.userId}); + GetPoemsInCollectionParams({this.collectionName, required this.userId}); - final String collectionName; + final String? collectionName; final String userId; } diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index b32e771..5eb50d2 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -200,7 +200,7 @@ class FirebaseDatabaseCubit extends Cubit { } } - Future> getPoemsInCollection({required String userId, required String collectionName}) async{ + Future> getPoemsInCollection({required String userId, String? collectionName}) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); try{ diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index a510e43..26cfc5f 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -35,7 +35,9 @@ class _SavedCollectionViewPageState extends State { Future initPoems() async { poems = await context.read().getPoemsInCollection( userId: getIt().getCurrentUser().userId!, - collectionName: collectionEntity.name ?? '', + collectionName: collectionEntity.isAllSavedPoems + ? null + : collectionEntity.name, ); } From 883e547f0d9e9a3dde955b0e0c1500df0c14d2c1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:14:23 +0200 Subject: [PATCH 312/475] Add an collection name check --- .../remote/firebase_api_service.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index e793943..0ef382e 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -16,6 +16,7 @@ abstract class FirebaseDatabaseService{ Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); Future> getPoemsInCollection({required String userId, String? collectionName}); + Future isCollectionExists({required String userId, required String collectionName}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -324,4 +325,26 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { return poems; } } + + @override + Future isCollectionExists({required String userId, required String collectionName}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + final collectionsRef = userRef.child('collections'); + + final collectionsSnapshot = await collectionsRef.get(); + + if (!collectionsSnapshot.exists || collectionsSnapshot.value == null) { + return false; + } + + final collections = Map.from(collectionsSnapshot.value as Map); + + for (final key in collections.keys) { + if (collections[key]['name'] == collectionName) { + return true; + } + } + + return false; + } } From a43c3388750ae0524bc60d44f1981247b7d4c249 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:14:31 +0200 Subject: [PATCH 313/475] Add an collection name check in the repo --- .../data/repository/firebase_db_repository_impl.dart | 3 +++ .../saved_poems/domain/repository/firebase_db_repository.dart | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 114d86a..18e2b0b 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -37,4 +37,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future> getPoemsInCollection({required String userId, String? collectionName}) => _databaseService.getPoemsInCollection(userId: userId, collectionName: collectionName); + + @override + Future isCollectionExists({required String userId, required String collectionName}) => _databaseService.isCollectionExists(userId: userId, collectionName: collectionName); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 4048423..e41bcba 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -12,4 +12,5 @@ abstract class FirebaseDatabaseRepository { Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); Future> getPoemsInCollection({required String userId, String? collectionName}); + Future isCollectionExists({required String userId, required String collectionName}); } From 5f4cc54cc934a73996fcae52f47087341f8ad64f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:14:39 +0200 Subject: [PATCH 314/475] Add an collection name check use case --- .../is_collection_exists_params.dart | 6 +++++ .../is_collection_exists_usecase.dart | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_params.dart b/lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_params.dart new file mode 100644 index 0000000..bbabfbf --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_params.dart @@ -0,0 +1,6 @@ +class IsCollectionExistsParams { + IsCollectionExistsParams({required this.collectionName, required this.userId}); + + final String collectionName; + final String userId; +} diff --git a/lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart b/lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart new file mode 100644 index 0000000..578a08c --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart @@ -0,0 +1,23 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_params.dart'; + +class IsCollectionExistsUseCase implements UseCase{ + IsCollectionExistsUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({IsCollectionExistsParams? params}) async { + if(params != null){ + final result = await _databaseRepository.isCollectionExists( + collectionName: params.collectionName, + userId: params.userId, + ); + + return result; + } + + return false; + } +} From 7fcdd49cfa357de082d52d567953bd0f754a39f5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:14:46 +0200 Subject: [PATCH 315/475] Init use case in the DI --- lib/core/dependency_injection.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 2362fe6..eb83cb3 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -29,6 +29,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_co import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; @@ -72,6 +73,7 @@ void initializeDependencies() { ..registerSingleton(DeletePoemFromCollectionUseCase(getIt())) ..registerSingleton(UpdatePoemsInCollectionUseCase(getIt())) ..registerSingleton(GetPoemsInCollectionUseCase(getIt())) + ..registerSingleton(IsCollectionExistsUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -93,6 +95,7 @@ void initializeDependencies() { getIt(), getIt(), getIt(), + getIt(), ), ) ..registerFactory(() => RegisterFormValidationCubit( From 1c13dc53ce7bec54ab600c942c3880192577f796 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:14:55 +0200 Subject: [PATCH 316/475] Add an collection name check in the cubit --- .../bloc/firebase_database_cubit.dart | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 5eb50d2..3a5ec2a 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -14,6 +14,8 @@ import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collec import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart'; @@ -23,7 +25,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_col import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase, this._isCollectionExistsUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -35,6 +37,7 @@ class FirebaseDatabaseCubit extends Cubit { final DeletePoemFromCollectionUseCase _deletePoemFromCollectionUseCase; final UpdatePoemsInCollectionUseCase _updatePoemsInCollectionUseCase; final GetPoemsInCollectionUseCase _getPoemsInCollectionUseCase; + final IsCollectionExistsUseCase _isCollectionExistsUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -220,4 +223,22 @@ class FirebaseDatabaseCubit extends Cubit { return []; } } + + Future isCollectionExists({required String collectionName, required String userId}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + final isExists = await _isCollectionExistsUseCase( + params: IsCollectionExistsParams(collectionName: collectionName, userId: userId), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + + return isExists; + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + + return false; + } + } } From a5b9a0326300fa4aacf96fc0b520d4f29ceb0fb4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Thu, 7 Dec 2023 00:15:03 +0200 Subject: [PATCH 317/475] Implement collection name check --- .../create_collection_bottom_sheet.dart | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 3ab6ddf..f9d958a 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -134,15 +136,25 @@ class _CreateButtonWidget extends StatelessWidget { await _showNegativeToast('Please provide the name for the collection'); } else{ - await context.read().createNewCollection( - userId: getIt().getCurrentUser().userId!, + final isCollectionExist = await context.read().isCollectionExists( collectionName: textController.text, - poems: selectController.selectedOptions.map( - (selectedOption) => selectedOption.value!, - ).toList(), + userId: getIt().getCurrentUser().userId!, ); - - await _showPositiveToast('The collection has been successfully saved'); + + if(!isCollectionExist){ + await context.read().createNewCollection( + userId: getIt().getCurrentUser().userId!, + collectionName: textController.text, + poems: selectController.selectedOptions.map( + (selectedOption) => selectedOption.value!, + ).toList(), + ); + + await _showPositiveToast('The collection has been successfully saved'); + } else{ + await _showNegativeToast('The collection with this name already exists'); + } + } }, child: const Padding( From 9cfaca5118a721406a58a68226fc4e65bee399aa Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:52:48 +0200 Subject: [PATCH 318/475] Change fetched poems variable --- .../presentation/pages/saved_collection_view_page.dart | 2 +- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 26cfc5f..49b978c 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -60,7 +60,7 @@ class _SavedCollectionViewPageState extends State { isScrollControlled: true, builder:(context) => UpdateCollectionBottomSheetContent( collectionName: collectionEntity.name ?? '', - poemsInTheCollection: collectionEntity.poems, + poemsInTheCollection: poems, allSavedPoems: savedPoems, ), ); diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 2ffca87..f46e7de 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -8,8 +8,8 @@ import 'package:poetlum/features/poems_feed/domain/repository/user_repository.da import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/collection_card.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart'; class SavedPoemsScreen extends StatefulWidget { const SavedPoemsScreen(this._userRepository, {super.key}); From 39f54d16dafbc7f3bbc78c9da4d99e112f96a10e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:15:56 +0200 Subject: [PATCH 319/475] Rename variable --- .../presentation/pages/saved_collection_view_page.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 49b978c..a4ef98c 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -18,7 +18,7 @@ class SavedCollectionViewPage extends StatefulWidget { } class _SavedCollectionViewPageState extends State { - List poems = []; + List poemsInTheCollection = []; late CollectionEntity collectionEntity; bool isInit = true; @@ -33,7 +33,7 @@ class _SavedCollectionViewPageState extends State { } Future initPoems() async { - poems = await context.read().getPoemsInCollection( + poemsInTheCollection = await context.read().getPoemsInCollection( userId: getIt().getCurrentUser().userId!, collectionName: collectionEntity.isAllSavedPoems ? null @@ -60,7 +60,7 @@ class _SavedCollectionViewPageState extends State { isScrollControlled: true, builder:(context) => UpdateCollectionBottomSheetContent( collectionName: collectionEntity.name ?? '', - poemsInTheCollection: poems, + poemsInTheCollection: poemsInTheCollection, allSavedPoems: savedPoems, ), ); @@ -79,9 +79,9 @@ class _SavedCollectionViewPageState extends State { return const Center(child: CircularProgressIndicator()); } else { return ListView.builder( - itemCount: poems.length, + itemCount: poemsInTheCollection.length, itemBuilder: (__, index) => SavedPoemCard( - poemEntity: poems[index], + poemEntity: poemsInTheCollection[index], collectionEntity: collectionEntity, ), ); From 0f89930e48c4f18511d5738726efd9ac65e2d9cf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:55:54 +0200 Subject: [PATCH 320/475] Add FutureBuilder --- .../screens/saved_poems_screen.dart | 117 ++++++++++-------- 1 file changed, 67 insertions(+), 50 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index f46e7de..ce674f5 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -21,77 +21,94 @@ class SavedPoemsScreen extends StatefulWidget { } class _SavedPoemsScreenState extends State { - List? collections = []; + Future?>? collectionsFuture; @override void initState() { super.initState(); - initCollections(); + collectionsFuture = initCollections(); } - - Future initCollections() async { - collections = await context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); - } + Future?> initCollections() async => context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); @override Widget build(BuildContext context) => BlocConsumer( listener: (context, state) { if (state.status == FirebaseDatabaseStatus.needsRefresh) { - initCollections(); + setState(() { + collectionsFuture = initCollections(); + }); } }, builder: (context, state) { if (state.status == FirebaseDatabaseStatus.submitting) { return const Center(child: CircularProgressIndicator()); } else { - return SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - FilledButton( - child: const Text('Create a collection'), - onPressed: () async{ - await showModalBottomSheet( - context: context, - isScrollControlled: true, - builder:(context) => CreateCollectionBottomSheetContent( - poems: collections?[0].poems, - ), - ); + return FutureBuilder?>( + future: collectionsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return const Center(child: Text('Error fetching data')); + } + + var collections = snapshot.data; + + if (collections == null || collections.isEmpty) { + return const Center(child: Text("You haven't saved any poems yet. :(")); + } - collections = await context.read().getUserCollections(getIt().getCurrentUser().userId!); - }, + return SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + FilledButton( + child: const Text('Create a collection'), + onPressed: () async{ + await showModalBottomSheet( + context: context, + isScrollControlled: true, + builder:(context) => CreateCollectionBottomSheetContent( + poems: collections?[0].poems, + ), + ); + + + collections = await context.read().getUserCollections(getIt().getCurrentUser().userId!); + }, + ), + FilledButton.tonal( + onPressed: () => Navigator.pushNamedAndRemoveUntil( + context, + writePoemPageConstant, + (route) => false, + ), + child: const Text('Write a poem'), + ), + ], ), - FilledButton.tonal( - onPressed: () => Navigator.pushNamedAndRemoveUntil( - context, - writePoemPageConstant, - (route) => false, - ), - child: const Text('Write a poem'), + ), + if (collections == null || collections!.isEmpty) + const Text("You haven't saved any poems yet. :(") + else ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: collections!.length, + itemBuilder: (context, index) => CollectionCard( + collection: collections![index], ), - ], - ), - ), - - - if (collections == null || collections!.isEmpty) - const Text("You haven't saved any poems yet. :(") - else ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: collections!.length, - itemBuilder: (context, index) => CollectionCard( - collection: collections![index], - ), + ), + ], ), - ], - ), + ); + }, ); } }, From 5c91aa723f47204c081385421d6481f53e50e319 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 14:03:22 +0200 Subject: [PATCH 321/475] Add message if poemsInTheCollection are empty --- .../pages/saved_collection_view_page.dart | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index a4ef98c..802a3ea 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -78,13 +78,24 @@ class _SavedCollectionViewPageState extends State { if (state.status == FirebaseDatabaseStatus.submitting) { return const Center(child: CircularProgressIndicator()); } else { - return ListView.builder( - itemCount: poemsInTheCollection.length, - itemBuilder: (__, index) => SavedPoemCard( - poemEntity: poemsInTheCollection[index], - collectionEntity: collectionEntity, - ), - ); + return poemsInTheCollection.isEmpty + ? const Padding( + padding: EdgeInsets.symmetric(horizontal: 15), + child: Center( + child: Text( + 'Nothing to show here 😔\nTap on the "Edit" button to add amazing poems to the collection', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ), + ) + : ListView.builder( + itemCount: poemsInTheCollection.length, + itemBuilder: (__, index) => SavedPoemCard( + poemEntity: poemsInTheCollection[index], + collectionEntity: collectionEntity, + ), + ); } }, ), From 18408a7bab3e5f31604c9bc103ffe3fc0f3d7ae5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 14:31:27 +0200 Subject: [PATCH 322/475] Refresh screen on creating new collecton --- .../saved_poems/presentation/bloc/firebase_database_cubit.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 3a5ec2a..99ed001 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -107,7 +107,7 @@ class FirebaseDatabaseCubit extends Cubit { ), ); - emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } @@ -144,6 +144,7 @@ class FirebaseDatabaseCubit extends Cubit { ); emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + emit(state.copyWith(status: FirebaseDatabaseStatus.needsRefresh)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } From 76df9f01163eb917965e1fc5f39aed9425f6fa21 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 14:44:36 +0200 Subject: [PATCH 323/475] Remove emits from deleteCollection --- .../saved_poems/presentation/bloc/firebase_database_cubit.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index 99ed001..bfd4bd8 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -151,8 +151,6 @@ class FirebaseDatabaseCubit extends Cubit { } Future deleteCollection({required String userId, required String collectionName, required List poems}) async{ - emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); - try{ await _deleteCollectionUseCase( params: DeleteCollectionParams( @@ -161,7 +159,6 @@ class FirebaseDatabaseCubit extends Cubit { poems: poems, ), ); - emit(state.copyWith(status: FirebaseDatabaseStatus.success)); } catch(e) { emit(state.copyWith(status: FirebaseDatabaseStatus.error)); } From b47cd20a383cd2b554f107d1bf85ff2bafbab736 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:20:02 +0200 Subject: [PATCH 324/475] Change theme function --- lib/config/theme/app_theme.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/config/theme/app_theme.dart b/lib/config/theme/app_theme.dart index b055c1d..d66768a 100644 --- a/lib/config/theme/app_theme.dart +++ b/lib/config/theme/app_theme.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -ThemeData theme()=> ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), +ThemeData theme(Color color) => ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: color), useMaterial3: true, scaffoldBackgroundColor: Colors.white, ); From cc2a13db7b42db9876e9550257c730e0b67062b0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:20:24 +0200 Subject: [PATCH 325/475] Create theme state cubit --- .../presentation/bloc/change_theme_cubit.dart | 16 ++++++++++++++++ .../presentation/bloc/change_theme_state.dart | 12 ++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 lib/features/theme_change/presentation/bloc/change_theme_cubit.dart create mode 100644 lib/features/theme_change/presentation/bloc/change_theme_state.dart diff --git a/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart b/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart new file mode 100644 index 0000000..21c8008 --- /dev/null +++ b/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/config/theme/app_theme.dart'; +import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_state.dart'; + +class ThemeCubit extends Cubit { + ThemeCubit() : super(ThemeState.initial()); + + void setThemeColor(Color themeColor) { + emit( + ThemeState( + themeData: theme(themeColor), + ), + ); + } +} diff --git a/lib/features/theme_change/presentation/bloc/change_theme_state.dart b/lib/features/theme_change/presentation/bloc/change_theme_state.dart new file mode 100644 index 0000000..8fcaf58 --- /dev/null +++ b/lib/features/theme_change/presentation/bloc/change_theme_state.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/config/theme/app_theme.dart'; + +class ThemeState { + ThemeState({required this.themeData}); + + factory ThemeState.initial() => ThemeState( + themeData: theme(Colors.deepPurple), + ); + + final ThemeData themeData; +} From e2d45717a23ef43e42d6e22e44137286a08413bf Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:21:30 +0200 Subject: [PATCH 326/475] Create color option button --- .../widgets/color_option_button.dart | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 lib/features/theme_change/presentation/widgets/color_option_button.dart diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart new file mode 100644 index 0000000..82af5bf --- /dev/null +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; + +class ColorOptionButton extends StatefulWidget { + const ColorOptionButton({ + required this.themeColor, + super.key + }); + + final Color themeColor; + + @override + State createState() => _ColorOptionButtonState(); +} + +class _ColorOptionButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { + @override + void dispose() { + rotationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.all(30), + child: RotationTransition( + turns: rotationAnimation, + child: GestureDetector( + onTap: (){ + playAnimation(); + + context.read().setThemeColor (widget.themeColor); + }, + child: Container( + decoration: BoxDecoration( + color: widget.themeColor, + borderRadius: const BorderRadius.all(Radius.circular(20)), + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.2), + offset: Offset(4, 4), + blurRadius: 5, + ) + ] + ), + ), + ), + ) + ); +} \ No newline at end of file From 12d8c9c503804d649c7763de636bdbe72bed884c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:25:30 +0200 Subject: [PATCH 327/475] Add bottom sheet on button tap --- .../app_bar/buttons/settings_button.dart | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index bfc9b9b..1a29abc 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/features/theme_change/presentation/widgets/color_options.dart'; class SettingsButton extends StatefulWidget { const SettingsButton({super.key}); @@ -14,9 +15,13 @@ class _SettingsButtonState extends State with TickerProviderStat turns: rotationAnimation, child: IconButton( tooltip: 'Settings', - onPressed: (){ + onPressed: () async{ playAnimation(); - // ... + + await showModalBottomSheet( + context: context, + builder: (context) => _buildBottomSheetContent(), + ); }, icon: const Icon(Icons.settings), ), @@ -27,4 +32,18 @@ class _SettingsButtonState extends State with TickerProviderStat rotationController.dispose(); super.dispose(); } + + Widget _buildBottomSheetContent() => SizedBox( + child: Column( + children: [ + const Text('Choose your theme'), + Expanded( + child: GridView.count( + crossAxisCount: 3, + children: ColorOptions.colors, + ), + ), + ], + ), + ); } From 1e9ff5dc7d2319fb0c1368a76a053ce1fc073430 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:25:42 +0200 Subject: [PATCH 328/475] Add cubit to the DI --- lib/core/dependency_injection.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index eb83cb3..dff91f3 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -34,6 +34,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_p import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; GetIt getIt = GetIt.instance; @@ -80,9 +81,10 @@ void initializeDependencies() { ..registerLazySingleton(() => LocalEmailValidator()) ..registerLazySingleton(() => PasswordValidator()) - // Bloc + // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) + ..registerFactory(() => ThemeCubit()) ..registerFactory( () => FirebaseDatabaseCubit( getIt(), From a525689eff550a3057f8d47c349d5ea1646b7481 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:25:58 +0200 Subject: [PATCH 329/475] Implement cubit --- lib/features/application/poetlum_app.dart | 34 +++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index 3d48ed3..c115b16 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; -import 'package:poetlum/config/theme/app_theme.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/pages/auth_wrapper.dart'; @@ -11,24 +11,28 @@ import 'package:poetlum/features/poems_feed/presentation/pages/poem_view/poem_vi import 'package:poetlum/features/saved_poems/presentation/pages/saved_collection_view_page.dart'; import 'package:poetlum/features/saved_poems/presentation/pages/saved_poem_view_page.dart'; import 'package:poetlum/features/saved_poems/presentation/pages/write_poem_page.dart'; +import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; +import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_state.dart'; class PoetlumApp extends StatelessWidget { const PoetlumApp({super.key}); @override - Widget build(BuildContext context) => GetMaterialApp( - title: 'Poetlum', - theme: theme(), - initialRoute: authWrapperPageConstant, - routes: { - authWrapperPageConstant:(_) => const AuthWrapper(), - registerPageConstant: (_) => const RegistrationPage(), - loginPageConstant: (_) => const LoginPage(), - screensWrapperPageConstant: (_) => const ScreensWrapper(), - poemViewPageConstant:(_) => const PoemViewPage(), - writePoemPageConstant: (_) => WritePoemPage(getIt()), - savedCollectionViewConstant: (_) => const SavedCollectionViewPage(), - savedPoemViewConstant: (_) => const SavedPoemViewPage(), - }, + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) => GetMaterialApp( + title: 'Poetlum', + theme: state.themeData, + initialRoute: authWrapperPageConstant, + routes: { + authWrapperPageConstant:(_) => const AuthWrapper(), + registerPageConstant: (_) => const RegistrationPage(), + loginPageConstant: (_) => const LoginPage(), + screensWrapperPageConstant: (_) => const ScreensWrapper(), + poemViewPageConstant:(_) => const PoemViewPage(), + writePoemPageConstant: (_) => WritePoemPage(getIt()), + savedCollectionViewConstant: (_) => const SavedCollectionViewPage(), + savedPoemViewConstant: (_) => const SavedPoemViewPage(), + }, + ), ); } From a35edc5ba2115a458cd48b24e06d70dd7e312539 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:26:08 +0200 Subject: [PATCH 330/475] Register cubit --- lib/features/multi_bloc_provider/presentation/init_blocs.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/multi_bloc_provider/presentation/init_blocs.dart b/lib/features/multi_bloc_provider/presentation/init_blocs.dart index 893ab13..433814d 100644 --- a/lib/features/multi_bloc_provider/presentation/init_blocs.dart +++ b/lib/features/multi_bloc_provider/presentation/init_blocs.dart @@ -6,6 +6,7 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; class InitBlocs extends StatelessWidget { const InitBlocs({super.key, required this.child}); @@ -20,6 +21,7 @@ class InitBlocs extends StatelessWidget { BlocProvider(create:(context) => getIt()), BlocProvider(create:(context) => getIt()), BlocProvider(create: (context) => getIt()), + BlocProvider(create: (context) => getIt()), ], child: child, ); From 9de11d94356865b60230f4958eabb67c78cc57c7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:26:19 +0200 Subject: [PATCH 331/475] Add color options --- .../presentation/widgets/color_options.dart | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 lib/features/theme_change/presentation/widgets/color_options.dart diff --git a/lib/features/theme_change/presentation/widgets/color_options.dart b/lib/features/theme_change/presentation/widgets/color_options.dart new file mode 100644 index 0000000..49b4692 --- /dev/null +++ b/lib/features/theme_change/presentation/widgets/color_options.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/theme_change/presentation/widgets/color_option_button.dart'; + +class ColorOptions{ + static const List colors = [ + ColorOptionButton(themeColor: Color(0xFF817DBA)), + ColorOptionButton(themeColor: Color(0xFFA0C49D)), + ColorOptionButton(themeColor: Color(0xFFC4DFDF)), + + ColorOptionButton(themeColor: Color(0xFFA7727D)), + ColorOptionButton(themeColor: Color(0xFFD0B8A8)), + ColorOptionButton(themeColor: Color(0xFF9F8772)), + + ColorOptionButton(themeColor: Colors.redAccent), + ColorOptionButton(themeColor: Colors.blueAccent), + ColorOptionButton(themeColor: Colors.cyanAccent), + + ColorOptionButton(themeColor: Colors.limeAccent), + ColorOptionButton(themeColor: Colors.pinkAccent), + ColorOptionButton(themeColor: Colors.tealAccent), + + ColorOptionButton(themeColor: Colors.greenAccent), + ColorOptionButton(themeColor: Colors.amberAccent), + ColorOptionButton(themeColor: Colors.indigoAccent), + + ColorOptionButton(themeColor: Colors.orangeAccent), + ColorOptionButton(themeColor: Colors.purpleAccent), + ColorOptionButton(themeColor: Colors.yellowAccent), + + ColorOptionButton(themeColor: Colors.lightBlueAccent), + ColorOptionButton(themeColor: Colors.deepOrangeAccent), + ColorOptionButton(themeColor: Colors.deepPurpleAccent), + + ColorOptionButton(themeColor: Colors.lightGreenAccent), + ColorOptionButton(themeColor: Colors.grey), + ColorOptionButton(themeColor: Colors.white) + ]; +} From d59724043ed14d9d060c4a7acf5c981856ff5d36 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:46:03 +0200 Subject: [PATCH 332/475] Remove unused field --- lib/config/theme/app_theme.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/config/theme/app_theme.dart b/lib/config/theme/app_theme.dart index d66768a..6abf93d 100644 --- a/lib/config/theme/app_theme.dart +++ b/lib/config/theme/app_theme.dart @@ -3,5 +3,4 @@ import 'package:flutter/material.dart'; ThemeData theme(Color color) => ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: color), useMaterial3: true, - scaffoldBackgroundColor: Colors.white, ); From 4f0b915d9c111a0d99ee80b437963d2f03791442 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:46:15 +0200 Subject: [PATCH 333/475] Install shared_preferences package --- .../widgets/color_option_button.dart | 2 +- pubspec.lock | 104 ++++++++++++++++++ pubspec.yaml | 1 + 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index 82af5bf..3ac1eb2 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -31,7 +31,7 @@ class _ColorOptionButtonState extends State with TickerProvi onTap: (){ playAnimation(); - context.read().setThemeColor (widget.themeColor); + context.read().setThemeColor(widget.themeColor); }, child: Container( decoration: BoxDecoration( diff --git a/pubspec.lock b/pubspec.lock index 75c5bc4..e00f0d1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -664,6 +664,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" petitparser: dependency: transitive description: @@ -672,6 +696,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.0" + platform: + dependency: transitive + description: + name: platform + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + url: "https://pub.dev" + source: hosted + version: "3.1.3" plugin_platform_interface: dependency: transitive description: @@ -736,6 +768,62 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.8" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + url: "https://pub.dev" + source: hosted + version: "2.3.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" shelf: dependency: transitive description: @@ -885,6 +973,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" + win32: + dependency: transitive + description: + name: win32 + sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + url: "https://pub.dev" + source: hosted + version: "1.0.3" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f045b1b..59b26ea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,6 +54,7 @@ dependencies: animations: ^2.0.8 like_button: ^2.0.5 multi_dropdown: ^2.1.1 + shared_preferences: ^2.2.2 dev_dependencies: flutter_test: From 07092b5df0904f174f7dd74e4fcd8e69f9540388 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:13:53 +0200 Subject: [PATCH 334/475] Create shared preferences conts --- lib/core/constants/shared_preferences_constants.dart | 1 + 1 file changed, 1 insertion(+) create mode 100644 lib/core/constants/shared_preferences_constants.dart diff --git a/lib/core/constants/shared_preferences_constants.dart b/lib/core/constants/shared_preferences_constants.dart new file mode 100644 index 0000000..02dfa27 --- /dev/null +++ b/lib/core/constants/shared_preferences_constants.dart @@ -0,0 +1 @@ +const String savedColorConstant = 'saved_color'; \ No newline at end of file From d562a4f94bfae08cd563c8129f5fd4a35955b41c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:14:01 +0200 Subject: [PATCH 335/475] Create shared preferences service --- .../local/shared_preferences_service.dart | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/features/theme_change/data/data_sources/local/shared_preferences_service.dart diff --git a/lib/features/theme_change/data/data_sources/local/shared_preferences_service.dart b/lib/features/theme_change/data/data_sources/local/shared_preferences_service.dart new file mode 100644 index 0000000..f1d1f83 --- /dev/null +++ b/lib/features/theme_change/data/data_sources/local/shared_preferences_service.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/core/constants/shared_preferences_constants.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +abstract class SharedPreferencesService{ + Future storeColor({required Color color}); + Future getColor(); +} + +class SharedPreferencesServiceImpl implements SharedPreferencesService{ + @override + Future storeColor({required Color color}) async { + final prefs = await SharedPreferences.getInstance(); + + final colorValue = color.value; + + await prefs.setInt(savedColorConstant, colorValue); + } + + @override + Future getColor() async { + final prefs = await SharedPreferences.getInstance(); + + final colorValue = prefs.getInt(savedColorConstant) ?? Colors.deepPurple.value; + + return Color(colorValue); + } +} From a29bd187761a07e32c0c0a9098662b94b99e5213 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:14:09 +0200 Subject: [PATCH 336/475] Create shared preferences repository --- .../shared_preferences_repository_impl.dart | 16 ++++++++++++++++ .../shared_preferences_repository.dart | 6 ++++++ 2 files changed, 22 insertions(+) create mode 100644 lib/features/theme_change/data/repository/shared_preferences_repository_impl.dart create mode 100644 lib/features/theme_change/domain/repository/shared_preferences_repository.dart diff --git a/lib/features/theme_change/data/repository/shared_preferences_repository_impl.dart b/lib/features/theme_change/data/repository/shared_preferences_repository_impl.dart new file mode 100644 index 0000000..396fb76 --- /dev/null +++ b/lib/features/theme_change/data/repository/shared_preferences_repository_impl.dart @@ -0,0 +1,16 @@ +import 'dart:ui'; + +import 'package:poetlum/features/theme_change/data/data_sources/local/shared_preferences_service.dart'; +import 'package:poetlum/features/theme_change/domain/repository/shared_preferences_repository.dart'; + +class SharedPreferencesRepositoryImpl implements SharedPreferencesRepository{ + SharedPreferencesRepositoryImpl(this._sharedPreferencesService); + + final SharedPreferencesService _sharedPreferencesService; + + @override + Future getColor() async => _sharedPreferencesService.getColor(); + + @override + Future storeColor({required Color color}) async => _sharedPreferencesService.storeColor(color: color); +} diff --git a/lib/features/theme_change/domain/repository/shared_preferences_repository.dart b/lib/features/theme_change/domain/repository/shared_preferences_repository.dart new file mode 100644 index 0000000..47f082a --- /dev/null +++ b/lib/features/theme_change/domain/repository/shared_preferences_repository.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; + +abstract class SharedPreferencesRepository{ + Future storeColor({required Color color}); + Future getColor(); +} From b7460331c9f7b01b197a8a8f22b482c23a2bd86a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:14:19 +0200 Subject: [PATCH 337/475] Create use cases --- .../usecases/get_color/get_color_usecase.dart | 12 ++++++++++++ .../usecases/save_color/save_color_usecase.dart | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 lib/features/theme_change/domain/usecases/get_color/get_color_usecase.dart create mode 100644 lib/features/theme_change/domain/usecases/save_color/save_color_usecase.dart diff --git a/lib/features/theme_change/domain/usecases/get_color/get_color_usecase.dart b/lib/features/theme_change/domain/usecases/get_color/get_color_usecase.dart new file mode 100644 index 0000000..09786b6 --- /dev/null +++ b/lib/features/theme_change/domain/usecases/get_color/get_color_usecase.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/theme_change/domain/repository/shared_preferences_repository.dart'; + +class GetColorUseCase implements UseCase{ + GetColorUseCase(this._sharedPreferencesRepository); + + final SharedPreferencesRepository _sharedPreferencesRepository; + + @override + Future call({void params}) async => _sharedPreferencesRepository.getColor(); +} diff --git a/lib/features/theme_change/domain/usecases/save_color/save_color_usecase.dart b/lib/features/theme_change/domain/usecases/save_color/save_color_usecase.dart new file mode 100644 index 0000000..7a44e9c --- /dev/null +++ b/lib/features/theme_change/domain/usecases/save_color/save_color_usecase.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/theme_change/domain/repository/shared_preferences_repository.dart'; + +class SaveColorUseCase implements UseCase{ + SaveColorUseCase(this._sharedPreferencesRepository); + + final SharedPreferencesRepository _sharedPreferencesRepository; + + @override + Future call({Color? params}) async { + if(params != null){ + await _sharedPreferencesRepository.storeColor(color: params); + } + } +} From c42bbfb1c5c632668c4d48f6de60c9c9460d5c34 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:14:53 +0200 Subject: [PATCH 338/475] Init all the stuff in the DI --- lib/core/dependency_injection.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index dff91f3..5f6112c 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -34,6 +34,11 @@ import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_p import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/theme_change/data/data_sources/local/shared_preferences_service.dart'; +import 'package:poetlum/features/theme_change/data/repository/shared_preferences_repository_impl.dart'; +import 'package:poetlum/features/theme_change/domain/repository/shared_preferences_repository.dart'; +import 'package:poetlum/features/theme_change/domain/usecases/get_color/get_color_usecase.dart'; +import 'package:poetlum/features/theme_change/domain/usecases/save_color/save_color_usecase.dart'; import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; GetIt getIt = GetIt.instance; @@ -51,12 +56,14 @@ void initializeDependencies() { ..registerSingleton(PoemApiService(getIt())) ..registerSingleton(FirebaseServiceImpl()) ..registerSingleton(FirebaseDatabaseServiceImpl()) + ..registerSingleton(SharedPreferencesServiceImpl()) // Repository ..registerSingleton(AuthenticationRepositoryImpl()) ..registerSingleton(PoemRepositoryImpl(getIt())) ..registerSingleton(FirebaseRepositoryImpl(getIt())) ..registerSingleton(FirebaseDatabaseRepositoryImpl(getIt())) + ..registerSingleton(SharedPreferencesRepositoryImpl(getIt())) ..registerSingleton(UserRepositoryImpl(FirebaseAuth.instance)) // Usecase @@ -75,6 +82,8 @@ void initializeDependencies() { ..registerSingleton(UpdatePoemsInCollectionUseCase(getIt())) ..registerSingleton(GetPoemsInCollectionUseCase(getIt())) ..registerSingleton(IsCollectionExistsUseCase(getIt())) + ..registerSingleton(SaveColorUseCase(getIt())) + ..registerSingleton(GetColorUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -84,7 +93,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) - ..registerFactory(() => ThemeCubit()) + ..registerFactory(() => ThemeCubit(getIt(), getIt())) ..registerFactory( () => FirebaseDatabaseCubit( getIt(), From 6ce81eb12e9948c1e935883f98b701af406ceae3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:15:01 +0200 Subject: [PATCH 339/475] Update cubit --- .../presentation/bloc/change_theme_cubit.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart b/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart index 21c8008..3f12111 100644 --- a/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart +++ b/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart @@ -1,16 +1,25 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/config/theme/app_theme.dart'; +import 'package:poetlum/features/theme_change/domain/usecases/get_color/get_color_usecase.dart'; +import 'package:poetlum/features/theme_change/domain/usecases/save_color/save_color_usecase.dart'; import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_state.dart'; class ThemeCubit extends Cubit { - ThemeCubit() : super(ThemeState.initial()); + ThemeCubit(this._saveColorUseCase, this._getColorUseCase) : super(ThemeState.initial()); - void setThemeColor(Color themeColor) { + final SaveColorUseCase _saveColorUseCase; + final GetColorUseCase _getColorUseCase; + + Future setThemeColor(Color themeColor) async { emit( ThemeState( themeData: theme(themeColor), ), ); + + await _saveColorUseCase(params: themeColor); } + + Future getThemeColor() async => _getColorUseCase(); } From cb0d26f395b337f57a8513271a949acc3821cf28 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:23:12 +0200 Subject: [PATCH 340/475] Make onTap async --- .../widgets/color_option_button.dart | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index 3ac1eb2..3a2d5d0 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -24,29 +24,29 @@ class _ColorOptionButtonState extends State with TickerProvi @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.all(30), - child: RotationTransition( - turns: rotationAnimation, - child: GestureDetector( - onTap: (){ - playAnimation(); - - context.read().setThemeColor(widget.themeColor); - }, - child: Container( - decoration: BoxDecoration( - color: widget.themeColor, - borderRadius: const BorderRadius.all(Radius.circular(20)), - boxShadow: const [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.2), - offset: Offset(4, 4), - blurRadius: 5, - ) - ] - ), + padding: const EdgeInsets.all(30), + child: RotationTransition( + turns: rotationAnimation, + child: GestureDetector( + onTap: () async { + playAnimation(); + + await context.read().setThemeColor(widget.themeColor); + }, + child: Container( + decoration: BoxDecoration( + color: widget.themeColor, + borderRadius: const BorderRadius.all(Radius.circular(20)), + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.2), + offset: Offset(4, 4), + blurRadius: 5, + ), + ], ), ), - ) - ); -} \ No newline at end of file + ), + ), + ); +} From ce7b4d02a1392dfc039681e2f7c02cd50ce28fcd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:36:44 +0200 Subject: [PATCH 341/475] Add field to save the color --- .../theme_change/presentation/bloc/change_theme_cubit.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart b/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart index 3f12111..8eec52a 100644 --- a/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart +++ b/lib/features/theme_change/presentation/bloc/change_theme_cubit.dart @@ -11,14 +11,16 @@ class ThemeCubit extends Cubit { final SaveColorUseCase _saveColorUseCase; final GetColorUseCase _getColorUseCase; - Future setThemeColor(Color themeColor) async { + Future setThemeColor({required Color themeColor, required bool needSave}) async { emit( ThemeState( themeData: theme(themeColor), ), ); - await _saveColorUseCase(params: themeColor); + if(needSave){ + await _saveColorUseCase(params: themeColor); + } } Future getThemeColor() async => _getColorUseCase(); From d8202aa7a339b4caa5973206fde2d0c8e8fb2972 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:36:57 +0200 Subject: [PATCH 342/475] Init theme on app startup --- .../widgets/init_theme_widget.dart | 29 +++++++++++++++++++ lib/main.dart | 5 +++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 lib/features/theme_change/presentation/widgets/init_theme_widget.dart diff --git a/lib/features/theme_change/presentation/widgets/init_theme_widget.dart b/lib/features/theme_change/presentation/widgets/init_theme_widget.dart new file mode 100644 index 0000000..8b28f2e --- /dev/null +++ b/lib/features/theme_change/presentation/widgets/init_theme_widget.dart @@ -0,0 +1,29 @@ +// ignore_for_file: use_build_context_synchronously + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; + +class InitTheme extends StatelessWidget { + const InitTheme({super.key, required this.child}); + + final Widget child; + + Future initTheme(BuildContext context) async{ + final themeColor = await context.read().getThemeColor(); + + await context.read().setThemeColor(themeColor: themeColor, needSave: false); + } + + @override + Widget build(BuildContext context) => FutureBuilder( + future: initTheme(context), + builder: (context, snapshot){ + if(snapshot.connectionState == ConnectionState.waiting){ + return const Center(child: CircularProgressIndicator()); + } + + return child; + }, + ); +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 61fbdba..2f9b770 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:poetlum/features/dependency_injection/presentation/widgets/init_ import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; +import 'package:poetlum/features/theme_change/presentation/widgets/init_theme_widget.dart'; import 'package:poetlum/firebase_options.dart'; void main() async { @@ -15,7 +16,9 @@ void main() async { child: const InitDependencies( child: InitCrashlyticsWidget( child: InitBlocs( - child: PoetlumApp(), + child: InitTheme( + child: PoetlumApp(), + ), ), ), ), From 8b369fe5b10c80985c4d96aac65ef53c5aff0e08 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:37:12 +0200 Subject: [PATCH 343/475] Save theme color --- .../theme_change/presentation/widgets/color_option_button.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index 3a2d5d0..dcb98d0 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -31,7 +31,7 @@ class _ColorOptionButtonState extends State with TickerProvi onTap: () async { playAnimation(); - await context.read().setThemeColor(widget.themeColor); + await context.read().setThemeColor(themeColor: widget.themeColor, needSave: true); }, child: Container( decoration: BoxDecoration( From f5a33530c8ba4c13edaf1289a0739dd505e94ee1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:42:57 +0200 Subject: [PATCH 344/475] Add title styling --- .../app_bar/buttons/settings_button.dart | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 1a29abc..7574291 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -36,7 +36,8 @@ class _SettingsButtonState extends State with TickerProviderStat Widget _buildBottomSheetContent() => SizedBox( child: Column( children: [ - const Text('Choose your theme'), + const _Title(text: 'Choose your theme'), + Expanded( child: GridView.count( crossAxisCount: 3, @@ -47,3 +48,21 @@ class _SettingsButtonState extends State with TickerProviderStat ), ); } + +class _Title extends StatelessWidget { + const _Title({required this.text}); + + final String text; + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(vertical: 30), + child: Text( + text, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ); +} From da98765ae03d93202aa11ea705554fe8166a55b3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:48:55 +0200 Subject: [PATCH 345/475] Make title scrollable as well --- .../widgets/app_bar/buttons/settings_button.dart | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 7574291..0d60cfe 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -33,16 +33,15 @@ class _SettingsButtonState extends State with TickerProviderStat super.dispose(); } - Widget _buildBottomSheetContent() => SizedBox( + Widget _buildBottomSheetContent() => SingleChildScrollView( child: Column( children: [ const _Title(text: 'Choose your theme'), - - Expanded( - child: GridView.count( - crossAxisCount: 3, - children: ColorOptions.colors, - ), + GridView.count( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: 3, + children: ColorOptions.colors, ), ], ), @@ -60,7 +59,7 @@ class _Title extends StatelessWidget { child: Text( text, style: const TextStyle( - fontSize: 22, + fontSize: 24, fontWeight: FontWeight.bold, ), ), From ece3a5f0307f9ea45f104194df255ba2f0317afa Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:17:18 +0200 Subject: [PATCH 346/475] Install share_plus package --- pubspec.lock | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 1 + 2 files changed, 97 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index e00f0d1..f9ec02e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -209,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5" + url: "https://pub.dev" + source: hosted + version: "0.3.3+7" crypto: dependency: transitive description: @@ -664,6 +672,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" path_provider_linux: dependency: transitive description: @@ -768,6 +800,22 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.8" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd + url: "https://pub.dev" + source: hosted + version: "7.2.1" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 + url: "https://pub.dev" + source: hosted + version: "3.3.1" shared_preferences: dependency: "direct main" description: @@ -869,6 +917,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -941,6 +997,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 + url: "https://pub.dev" + source: hosted + version: "4.2.1" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 59b26ea..2f49660 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,7 @@ dependencies: like_button: ^2.0.5 multi_dropdown: ^2.1.1 shared_preferences: ^2.2.2 + share_plus: ^7.2.1 dev_dependencies: flutter_test: From 300d4371dcc1855360583bde79eb0fafc9e6cf62 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:17:35 +0200 Subject: [PATCH 347/475] Delete ugly themes --- .../theme_change/presentation/widgets/color_options.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/features/theme_change/presentation/widgets/color_options.dart b/lib/features/theme_change/presentation/widgets/color_options.dart index 49b4692..136964b 100644 --- a/lib/features/theme_change/presentation/widgets/color_options.dart +++ b/lib/features/theme_change/presentation/widgets/color_options.dart @@ -15,7 +15,7 @@ class ColorOptions{ ColorOptionButton(themeColor: Colors.blueAccent), ColorOptionButton(themeColor: Colors.cyanAccent), - ColorOptionButton(themeColor: Colors.limeAccent), + ColorOptionButton(themeColor: Color(0xFFD2E0FB)), ColorOptionButton(themeColor: Colors.pinkAccent), ColorOptionButton(themeColor: Colors.tealAccent), @@ -24,15 +24,11 @@ class ColorOptions{ ColorOptionButton(themeColor: Colors.indigoAccent), ColorOptionButton(themeColor: Colors.orangeAccent), - ColorOptionButton(themeColor: Colors.purpleAccent), ColorOptionButton(themeColor: Colors.yellowAccent), - ColorOptionButton(themeColor: Colors.lightBlueAccent), + ColorOptionButton(themeColor: Colors.deepOrangeAccent), ColorOptionButton(themeColor: Colors.deepPurpleAccent), - ColorOptionButton(themeColor: Colors.lightGreenAccent), - ColorOptionButton(themeColor: Colors.grey), - ColorOptionButton(themeColor: Colors.white) ]; } From cd0ae38ad0c8e32b39cc5512aee2cf6cc9cb3cde Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:40:33 +0200 Subject: [PATCH 348/475] Rename file --- .../{custom_like_button.dart => custom_save_button.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/features/poems_feed/presentation/widgets/poem_view/{custom_like_button.dart => custom_save_button.dart} (100%) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart rename to lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart From 7b5354185903b8b3ba3307fc9ef4753ac43c52e0 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:40:43 +0200 Subject: [PATCH 349/475] Create custom share button --- .../poem_view/custom_share_button.dart | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart new file mode 100644 index 0000000..6227f8a --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:share_plus/share_plus.dart'; + +class CustomShareButton extends StatelessWidget { + const CustomShareButton({super.key, required this.poemEntity}); + + final PoemEntity poemEntity; + + @override + Widget build(BuildContext context) => Align( + alignment: Alignment.centerRight, + child: IconButton( + onPressed: () async { + await Share.share('${poemEntity.title}\n\n${poemEntity.text}\n\nThis poem was written by: ${poemEntity.author}'); + }, + icon: const Icon(Icons.share), + tooltip: 'Share', + ), + ); +} From 5bc807dfd56b5d9c1e87d75a3d22f1e3ee2ffff5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:41:47 +0200 Subject: [PATCH 350/475] Add custom share button --- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 5 ++++- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 1f704b6..05e86ee 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_like_button.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; @@ -31,6 +32,8 @@ class PoemViewPage extends StatelessWidget { const CustomSpacer(heightFactor: 0.02), PoemAuthor(author: poemEntity.author ?? ''), const CustomSpacer(heightFactor: 0.02), + CustomShareButton(poemEntity: poemEntity), + const CustomSpacer(heightFactor: 0.02), PoemContent(text: poemEntity.text ?? ''), const CustomSpacer(heightFactor: 0.02), PoemLineCount(lineCount: poemEntity.linecount ?? 0), diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 12d2827..638d2e2 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; @@ -29,6 +30,8 @@ class SavedPoemViewPage extends StatelessWidget { const CustomSpacer(heightFactor: 0.02), PoemAuthor(author: poemEntity.author ?? ''), const CustomSpacer(heightFactor: 0.02), + CustomShareButton(poemEntity: poemEntity), + const CustomSpacer(heightFactor: 0.02), PoemContent(text: poemEntity.text ?? ''), const CustomSpacer(heightFactor: 0.02), PoemLineCount(lineCount: poemEntity.linecount ?? 0), From 4c62fdae91844e0d19467e112a3fcc181e92aadb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 19:22:07 +0200 Subject: [PATCH 351/475] Change icon --- .../application/presentation/pages/screens_wrapper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index d384e81..ce05ce7 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -45,7 +45,7 @@ class _ScreensWrapperState extends State { onTabChange: (value) => setState(() => screenIndex = value), gap: 12, tabs: const [ - GButton(icon: Icons.menu, text: 'Menu'), + GButton(icon: Icons.home, text: 'Menu'), GButton(icon: Icons.bookmark_outline_rounded, text: 'Saved poems'), ], ), From 260e36d89b7e09f9b18b329fbb2d5780e552a2fe Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Fri, 8 Dec 2023 19:47:42 +0200 Subject: [PATCH 352/475] Change icon --- .../application/presentation/pages/screens_wrapper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index ce05ce7..57d4e36 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -45,7 +45,7 @@ class _ScreensWrapperState extends State { onTabChange: (value) => setState(() => screenIndex = value), gap: 12, tabs: const [ - GButton(icon: Icons.home, text: 'Menu'), + GButton(icon: Icons.home_outlined, text: 'Menu'), GButton(icon: Icons.bookmark_outline_rounded, text: 'Saved poems'), ], ), From 8a46f70791ac448f27f16f66a5e9747a3b96252d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:34:14 +0200 Subject: [PATCH 353/475] Create animation constants --- lib/core/constants/animation_constants.dart | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 lib/core/constants/animation_constants.dart diff --git a/lib/core/constants/animation_constants.dart b/lib/core/constants/animation_constants.dart new file mode 100644 index 0000000..e05f7c2 --- /dev/null +++ b/lib/core/constants/animation_constants.dart @@ -0,0 +1,4 @@ +import 'package:flutter/material.dart'; + +const Curve animationCurve = Curves.easeOutSine; +const Duration animationDuration = Duration(milliseconds: 500); From 722b7d7d40cfde9c80130a3ca040ba8c46635fe8 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:34:29 +0200 Subject: [PATCH 354/475] Create animation widget --- .../widgets/animations/right_animation.dart | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 lib/features/poems_feed/presentation/widgets/animations/right_animation.dart diff --git a/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart b/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart new file mode 100644 index 0000000..0d3571c --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/core/constants/animation_constants.dart'; + +class RightAnimation extends StatelessWidget { + const RightAnimation({ + super.key, + required this.child, + required this.animationField, + required this.positionInitialValue, + required this.opacityInitialValue, + }); + + final Widget child; + final bool animationField; + final double positionInitialValue; + final double opacityInitialValue; + + @override + Widget build(BuildContext context) => TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: animationField ? 1.0 : 0.0), + duration: animationDuration, + curve: animationCurve, + builder: (context, value, child) => Opacity( + opacity: opacityInitialValue + (1 - opacityInitialValue) * value, + child: Transform.translate( + offset: Offset(-positionInitialValue * (1 - value), 0), + child: child, + ), + ), + child: child, + ); +} From 035661e8232da944598ea6dcfacb459c1d1440e3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:40:49 +0200 Subject: [PATCH 355/475] Add animation to the each widget --- .../widgets/drawer/custom_drawer.dart | 138 ++++++++++++++---- 1 file changed, 107 insertions(+), 31 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 204e137..93b82cb 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -1,8 +1,11 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; @@ -25,43 +28,116 @@ class _CustomDrawerState extends State { final TextEditingController _resultCountController = TextEditingController(); bool? _isRandom = false; + bool isHeaderAnimated = false; + bool isAuthorAnimated = false; + bool isTitleAnimated = false; + bool isLinesNumberAnimated = false; + bool isResultCountAnimated = false; + bool isCheckboxAnimated = false; + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isHeaderAnimated = val, + (val) => isAuthorAnimated = val, + (val) => isTitleAnimated = val, + (val) => isLinesNumberAnimated = val, + (val) => isResultCountAnimated = val, + (val) => isCheckboxAnimated = val, + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + @override Widget build(BuildContext context) => Drawer( child: SizedBox( height: MediaQuery.of(context).size.height, - child: ListView( - children: [ - CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), - - CustomTextField(hintText: 'Author', controller: _authorController), - const CustomSpacer(heightFactor: 0.04), - - CustomTextField(hintText: 'Title', controller: _titleController), - const CustomSpacer(heightFactor: 0.04), - - CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), - const CustomSpacer(heightFactor: 0.04), - - CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), - const CustomSpacer(heightFactor: 0.04), - - CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), - const CustomSpacer(heightFactor: 0.04), - - CustomSearchButton( - onPressed: () { - BlocProvider.of(context).add( - GetPoemsEvent( - author: _authorController.text, - title: _titleController.text, - lineCount: _numberOfLinesController.text, - poemCount: _resultCountController.text, - isRandom: _isRandom ?? false, + child: SafeArea( + child: SingleChildScrollView( + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.04), + RightAnimation( + animationField: isHeaderAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), + ), + + RightAnimation( + animationField: isAuthorAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomTextField(hintText: 'Author', controller: _authorController), + ), + const CustomSpacer(heightFactor: 0.04), + + RightAnimation( + animationField: isTitleAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomTextField(hintText: 'Title', controller: _titleController), + ), + const CustomSpacer(heightFactor: 0.04), + + RightAnimation( + animationField: isLinesNumberAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), + ), + const CustomSpacer(heightFactor: 0.04), + + RightAnimation( + animationField: isResultCountAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), + ), + const CustomSpacer(heightFactor: 0.04), + + RightAnimation( + animationField: isCheckboxAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), + ), + const CustomSpacer(heightFactor: 0.04), + + RightAnimation( + animationField: isButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomSearchButton( + onPressed: () { + BlocProvider.of(context).add( + GetPoemsEvent( + author: _authorController.text, + title: _titleController.text, + lineCount: _numberOfLinesController.text, + poemCount: _resultCountController.text, + isRandom: _isRandom ?? false, + ), + ); + }, ), - ); - }, + ), + ], ), - ], + ), ), ), ); From 050f49e76225ae9669b5bd8b8afbbdf4a83017b2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:41:27 +0200 Subject: [PATCH 356/475] Reduce heightFactor --- .../poems_feed/presentation/widgets/drawer/custom_drawer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 93b82cb..dc7ad46 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -69,7 +69,7 @@ class _CustomDrawerState extends State { child: SingleChildScrollView( child: Column( children: [ - const CustomSpacer(heightFactor: 0.04), + const CustomSpacer(heightFactor: 0.02), RightAnimation( animationField: isHeaderAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, From e11f8c741a2e437accd2a7b0d82e8528fa2bf4ae Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:44:02 +0200 Subject: [PATCH 357/475] Add animations to each PoemCard's component --- .../widgets/poems_feed/poem_card.dart | 60 +++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart index e243dff..1bdb605 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart @@ -1,15 +1,48 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:flutter/material.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; -class PoemCard extends StatelessWidget { +class PoemCard extends StatefulWidget { const PoemCard({super.key, required this.poemEntity}); final PoemEntity poemEntity; + @override + State createState() => _PoemCardState(); +} + +class _PoemCardState extends State { + bool isTitleAnimated = false; + bool isAuthorAnimated = false; + bool isTextAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isTitleAnimated = val, + (val) => isAuthorAnimated = val, + (val) => isTextAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + @override Widget build(BuildContext context) => GestureDetector( - onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: poemEntity), + onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: widget.poemEntity), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), @@ -22,11 +55,28 @@ class PoemCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - _TitleText(title: poemEntity.title), + RightAnimation( + animationField: isTitleAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: _TitleText(title: widget.poemEntity.title), + ), const SizedBox(height: 8), - _AuthorText(author: poemEntity.author), + + RightAnimation( + animationField: isAuthorAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: _AuthorText(author: widget.poemEntity.author), + ), const SizedBox(height: 16), - _PoemText(text: poemEntity.text, maxLength: 250), + + RightAnimation( + animationField: isTextAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: _PoemText(text: widget.poemEntity.text, maxLength: 250), + ), ], ), ), From 5eae95d3e70979e609abe06f8e226438eb956444 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:52:32 +0200 Subject: [PATCH 358/475] Fix dispose issue --- .../presentation/widgets/poems_feed/poem_card.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart index 1bdb605..82197c4 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart @@ -35,7 +35,11 @@ class _PoemCardState extends State { for (var i = 0; i < setters.length; i++) { Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } ); } } From c9e652d6ba84a0068b992b9593ac6fd82a80a3ec Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 18:35:33 +0200 Subject: [PATCH 359/475] Add animation to the CreateCollectionBottomSheetContent --- .../create_collection_bottom_sheet.dart | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index f9d958a..6f436eb 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -7,6 +7,7 @@ import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -25,11 +26,18 @@ class _CreateCollectionBottomSheetContentState extends State _selectController; + bool isHeaderAnimated = false; + bool isTextFieldAnimated = false; + bool isSelectAnimated = false; + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + @override void initState() { super.initState(); _collectionNameController = TextEditingController(); _selectController = MultiSelectController(); + _startAnimations(); } @override @@ -40,6 +48,21 @@ class _CreateCollectionBottomSheetContentState extends State[ + (val) => isHeaderAnimated = val, + (val) => isTextFieldAnimated = val, + (val) => isSelectAnimated = val, + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height / 1.15, @@ -47,15 +70,39 @@ class _CreateCollectionBottomSheetContentState extends State Date: Mon, 11 Dec 2023 18:46:04 +0200 Subject: [PATCH 360/475] Add animation in the WritePoemPage --- .../widgets/drawer/custom_drawer.dart | 14 ++-- .../presentation/pages/write_poem_page.dart | 79 +++++++++++++++---- .../create_collection_bottom_sheet.dart | 8 +- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index dc7ad46..5692509 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -72,14 +72,14 @@ class _CustomDrawerState extends State { const CustomSpacer(heightFactor: 0.02), RightAnimation( animationField: isHeaderAnimated, - positionInitialValue: MediaQuery.of(context).size.width/8, + positionInitialValue: MediaQuery.of(context).size.width/6, opacityInitialValue: 0, child: CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), ), RightAnimation( animationField: isAuthorAnimated, - positionInitialValue: MediaQuery.of(context).size.width/8, + positionInitialValue: MediaQuery.of(context).size.width/6, opacityInitialValue: 0, child: CustomTextField(hintText: 'Author', controller: _authorController), ), @@ -87,7 +87,7 @@ class _CustomDrawerState extends State { RightAnimation( animationField: isTitleAnimated, - positionInitialValue: MediaQuery.of(context).size.width/8, + positionInitialValue: MediaQuery.of(context).size.width/6, opacityInitialValue: 0, child: CustomTextField(hintText: 'Title', controller: _titleController), ), @@ -95,7 +95,7 @@ class _CustomDrawerState extends State { RightAnimation( animationField: isLinesNumberAnimated, - positionInitialValue: MediaQuery.of(context).size.width/8, + positionInitialValue: MediaQuery.of(context).size.width/6, opacityInitialValue: 0, child: CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), ), @@ -103,7 +103,7 @@ class _CustomDrawerState extends State { RightAnimation( animationField: isResultCountAnimated, - positionInitialValue: MediaQuery.of(context).size.width/8, + positionInitialValue: MediaQuery.of(context).size.width/6, opacityInitialValue: 0, child: CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), ), @@ -111,7 +111,7 @@ class _CustomDrawerState extends State { RightAnimation( animationField: isCheckboxAnimated, - positionInitialValue: MediaQuery.of(context).size.width/8, + positionInitialValue: MediaQuery.of(context).size.width/6, opacityInitialValue: 0, child: CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), ), @@ -119,7 +119,7 @@ class _CustomDrawerState extends State { RightAnimation( animationField: isButtonAnimated, - positionInitialValue: MediaQuery.of(context).size.width/8, + positionInitialValue: MediaQuery.of(context).size.width/6, opacityInitialValue: 0, child: CustomSearchButton( onPressed: () { diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 8a17a3d..036fddf 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -4,6 +4,7 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; @@ -21,6 +22,35 @@ class _WritePoemPageState extends State { final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); + bool isNameTextFieldAnimated = false; + bool isPoemTextFieldAnimated = false; + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isNameTextFieldAnimated = val, + (val) => isPoemTextFieldAnimated = val, + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } + ); + } + } + @override Widget build(BuildContext context) => BlocConsumer( listener: (context, state) { @@ -45,26 +75,43 @@ class _WritePoemPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const _CustomSpacer(heightFactor: 0.03), - _CustomTextField(hintText: 'Poem name', controller: _nameController, isLarge: false), + RightAnimation( + animationField: isNameTextFieldAnimated, + positionInitialValue: MediaQuery.of(context).size.width/3, + opacityInitialValue: 0, + child: _CustomTextField(hintText: 'Poem name', controller: _nameController, isLarge: false), + ), const _CustomSpacer(heightFactor: 0.03), - _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), + + RightAnimation( + animationField: isPoemTextFieldAnimated, + positionInitialValue: MediaQuery.of(context).size.width/3, + opacityInitialValue: 0, + child: _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), + ), const _CustomSpacer(heightFactor: 0.03), + if (state.status == FirebaseDatabaseStatus.submitting) const CircularProgressIndicator() - else FilledButton.tonal( - onPressed: () { - if (_formKey.currentState!.validate()) { - context.read().savePoem( - userId: widget._userRepository.getCurrentUser().userId!, - username: widget._userRepository.getCurrentUser().username!, - title: _nameController.text, - text: _contentController.text, - ); - } - }, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Text('Save'), + else RightAnimation( + animationField: isButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.width/3, + opacityInitialValue: 0, + child: FilledButton.tonal( + onPressed: () { + if (_formKey.currentState!.validate()) { + context.read().savePoem( + userId: widget._userRepository.getCurrentUser().userId!, + username: widget._userRepository.getCurrentUser().username!, + title: _nameController.text, + text: _contentController.text, + ); + } + }, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Text('Save'), + ), ), ), const _CustomSpacer(heightFactor: 0.03), diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 6f436eb..46b5b3c 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -73,7 +73,7 @@ class _CreateCollectionBottomSheetContentState extends State Date: Mon, 11 Dec 2023 19:17:23 +0200 Subject: [PATCH 361/475] Add animation to the color options --- .../app_bar/buttons/settings_button.dart | 76 +++++++++++++++---- .../widgets/animated_color_option_button.dart | 60 +++++++++++++++ 2 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 lib/features/theme_change/presentation/widgets/animated_color_option_button.dart diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 0d60cfe..8ce6cd2 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/features/theme_change/presentation/widgets/animated_color_option_button.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/color_options.dart'; class SettingsButton extends StatefulWidget { @@ -20,7 +22,7 @@ class _SettingsButtonState extends State with TickerProviderStat await showModalBottomSheet( context: context, - builder: (context) => _buildBottomSheetContent(), + builder: (context) => _BottomSheetContent(), ); }, icon: const Icon(Icons.settings), @@ -32,20 +34,6 @@ class _SettingsButtonState extends State with TickerProviderStat rotationController.dispose(); super.dispose(); } - - Widget _buildBottomSheetContent() => SingleChildScrollView( - child: Column( - children: [ - const _Title(text: 'Choose your theme'), - GridView.count( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: 3, - children: ColorOptions.colors, - ), - ], - ), - ); } class _Title extends StatelessWidget { @@ -65,3 +53,61 @@ class _Title extends StatelessWidget { ), ); } + +class _BottomSheetContent extends StatefulWidget { + const _BottomSheetContent(); + + @override + State<_BottomSheetContent> createState() => _BottomSheetContentState(); +} + +class _BottomSheetContentState extends State<_BottomSheetContent> { + bool isHeaderAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isHeaderAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + + @override + Widget build(BuildContext context) => SingleChildScrollView( + child: Column( + children: [ + RightAnimation( + animationField: isHeaderAnimated, + positionInitialValue: MediaQuery.of(context).size.width/6, + opacityInitialValue: 0, + child: const _Title(text: 'Choose your theme'), + ), + + GridView.count( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: 3, + children: List.generate( + ColorOptions.colors.length, + (index) => AnimatedColorOptionButton( + button: ColorOptions.colors[index], + delay: Duration(milliseconds: animationDelay.inMilliseconds * index), + positionInitialValue: MediaQuery.of(context).size.height/15, + ), + ), + ), + ], + ), + ); +} diff --git a/lib/features/theme_change/presentation/widgets/animated_color_option_button.dart b/lib/features/theme_change/presentation/widgets/animated_color_option_button.dart new file mode 100644 index 0000000..684aecc --- /dev/null +++ b/lib/features/theme_change/presentation/widgets/animated_color_option_button.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/features/theme_change/presentation/widgets/color_option_button.dart'; + +class AnimatedColorOptionButton extends StatefulWidget { + const AnimatedColorOptionButton({ + super.key, + required this.button, + required this.delay, + required this.positionInitialValue, + }); + + final ColorOptionButton button; + final Duration delay; + final double positionInitialValue; + + @override + State createState() => _AnimatedColorOptionButtonState(); +} + +class _AnimatedColorOptionButtonState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 500), + vsync: this, + ); + _animation = Tween(begin: 0, end: 1).animate( + CurvedAnimation(parent: _controller, curve: Curves.easeInOut), + ); + + Future.delayed(widget.delay).then((_) { + if (mounted) { + _controller.forward(); + } + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => AnimatedBuilder( + animation: _animation, + builder: (context, child) => Opacity( + opacity: _animation.value, + child: Transform.translate( + offset: Offset(0, widget.positionInitialValue * (1 - _animation.value)), + child: child, + ), + ), + child: widget.button, + ); +} From eb6faa877bf4febace024e8234f45894e33effca Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:31:19 +0200 Subject: [PATCH 362/475] Add settings button animation --- .../app_bar/buttons/settings_button.dart | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 8ce6cd2..5daca14 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -12,20 +12,46 @@ class SettingsButton extends StatefulWidget { } class _SettingsButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + @override - Widget build(BuildContext context) => RotationTransition( - turns: rotationAnimation, - child: IconButton( - tooltip: 'Settings', - onPressed: () async{ - playAnimation(); - - await showModalBottomSheet( - context: context, - builder: (context) => _BottomSheetContent(), - ); - }, - icon: const Icon(Icons.settings), + Widget build(BuildContext context) => RightAnimation( + animationField: isButtonAnimated , + positionInitialValue: MediaQuery.of(context).size.width/6, + opacityInitialValue: 0, + child: RotationTransition( + turns: rotationAnimation, + child: IconButton( + tooltip: 'Settings', + onPressed: () async{ + playAnimation(); + + await showModalBottomSheet( + context: context, + builder: (context) => const _BottomSheetContent(), + ); + }, + icon: const Icon(Icons.settings), + ), ), ); From d3e253cfc7f17acb707213600414512ebbd23fd3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:31:30 +0200 Subject: [PATCH 363/475] Add refresh button animation --- .../app_bar/buttons/refresh_button.dart | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart index c8ac6e4..2c11219 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -4,6 +4,7 @@ import 'package:poetlum/features/application/presentation/widgets/app_bar/button import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; class RefreshButton extends StatefulWidget { const RefreshButton({super.key}); @@ -13,20 +14,46 @@ class RefreshButton extends StatefulWidget { } class _RefreshButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 400); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + @override - Widget build(BuildContext context) => BlocBuilder( - builder: (context, state) => RotationTransition( - turns: rotationAnimation, - child: IconButton( - tooltip: 'Refresh poems list', - onPressed: state is RemotePoemLoading - ? null - : (){ - playAnimation(); - - BlocProvider.of(context).add(const GetInitialPoemsEvent()); - }, - icon: const Icon(Icons.refresh), + Widget build(BuildContext context) => RightAnimation( + animationField: isButtonAnimated , + positionInitialValue: MediaQuery.of(context).size.width/6, + opacityInitialValue: 0, + child: BlocBuilder( + builder: (context, state) => RotationTransition( + turns: rotationAnimation, + child: IconButton( + tooltip: 'Refresh poems list', + onPressed: state is RemotePoemLoading + ? null + : (){ + playAnimation(); + + BlocProvider.of(context).add(const GetInitialPoemsEvent()); + }, + icon: const Icon(Icons.refresh), + ), ), ), ); From 09ed3fca9e421ed3d52820bb6ea4c129cd0c8284 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:44:27 +0200 Subject: [PATCH 364/475] Create top animation --- .../widgets/animations/top_animation.dart | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 lib/features/poems_feed/presentation/widgets/animations/top_animation.dart diff --git a/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart b/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart new file mode 100644 index 0000000..640007c --- /dev/null +++ b/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:poetlum/core/constants/animation_constants.dart'; + +class TopAnimation extends StatelessWidget { + const TopAnimation({ + super.key, + required this.child, + required this.animationField, + required this.positionInitialValue, + required this.opacityInitialValue, + }); + + final Widget child; + final bool animationField; + final double positionInitialValue; + final double opacityInitialValue; + + @override + Widget build(BuildContext context) => TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: animationField ? 1.0 : 0.0), + duration: animationDuration, + curve: animationCurve, + builder: (context, value, child) => Opacity( + opacity: opacityInitialValue + (1 - opacityInitialValue) * value, + child: Transform.translate( + offset: Offset(0, positionInitialValue * (1 - value)), + child: child, + ), + ), + child: child, + ); +} From 2be6e399a76afbadbe9dee8f2d8363cdd5b98275 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:44:58 +0200 Subject: [PATCH 365/475] Change animation --- .../widgets/drawer/custom_drawer.dart | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 5692509..abfbc8b 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -5,7 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; @@ -70,56 +70,56 @@ class _CustomDrawerState extends State { child: Column( children: [ const CustomSpacer(heightFactor: 0.02), - RightAnimation( + TopAnimation( animationField: isHeaderAnimated, - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, opacityInitialValue: 0, child: CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), ), - RightAnimation( + TopAnimation( animationField: isAuthorAnimated, - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, opacityInitialValue: 0, child: CustomTextField(hintText: 'Author', controller: _authorController), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: isTitleAnimated, - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, opacityInitialValue: 0, child: CustomTextField(hintText: 'Title', controller: _titleController), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: isLinesNumberAnimated, - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, opacityInitialValue: 0, child: CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: isResultCountAnimated, - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, opacityInitialValue: 0, child: CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: isCheckboxAnimated, - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, opacityInitialValue: 0, child: CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: isButtonAnimated, - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, opacityInitialValue: 0, child: CustomSearchButton( onPressed: () { From 72e1c8ea048c4950cad99dce066a27a6104f5cde Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 20:22:22 +0200 Subject: [PATCH 366/475] Change shadow params --- .../presentation/widgets/color_option_button.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index dcb98d0..b063bdc 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -39,9 +39,9 @@ class _ColorOptionButtonState extends State with TickerProvi borderRadius: const BorderRadius.all(Radius.circular(20)), boxShadow: const [ BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.2), - offset: Offset(4, 4), - blurRadius: 5, + color: Color.fromRGBO(0, 0, 0, 0.5), + offset: Offset(5, 5), + blurRadius: 6, ), ], ), From afb8fe72bd282485dab6cc280b349237a0f52648 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:29:15 +0200 Subject: [PATCH 367/475] Add animations in the SavedPoemViewPage --- .../pages/saved_poem_view_page.dart | 84 +++++++++++++++++-- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 638d2e2..5f425ad 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; @@ -8,9 +9,47 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_ import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; -class SavedPoemViewPage extends StatelessWidget { +class SavedPoemViewPage extends StatefulWidget { const SavedPoemViewPage({super.key}); + @override + State createState() => _SavedPoemViewPageState(); +} + +class _SavedPoemViewPageState extends State { + bool isTitleAnimated = false; + bool isAuthorAnimated = false; + bool isShareButtonAnimated = false; + bool isContentAnimated = false; + bool isPoemLineAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isTitleAnimated = val, + (val) => isAuthorAnimated = val, + (val) => isShareButtonAnimated = val, + (val) => isContentAnimated = val, + (val) => isPoemLineAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } + ); + } + } + @override Widget build(BuildContext context) { final poemEntity = ModalRoute.of(context)?.settings.arguments as PoemEntity; @@ -24,17 +63,46 @@ class SavedPoemViewPage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( children: [ + const CustomSpacer(heightFactor: 0.04), + + TopAnimation( + animationField: isTitleAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemTitle(title: poemEntity.title ?? ''), + ), const CustomSpacer(heightFactor: 0.02), + + TopAnimation( + animationField: isAuthorAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemAuthor(author: poemEntity.author ?? ''), + ), const CustomSpacer(heightFactor: 0.02), - PoemTitle(title: poemEntity.title ?? ''), - const CustomSpacer(heightFactor: 0.02), - PoemAuthor(author: poemEntity.author ?? ''), - const CustomSpacer(heightFactor: 0.02), - CustomShareButton(poemEntity: poemEntity), + + TopAnimation( + animationField: isShareButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomShareButton(poemEntity: poemEntity), + ), const CustomSpacer(heightFactor: 0.02), - PoemContent(text: poemEntity.text ?? ''), + + TopAnimation( + animationField: isContentAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemContent(text: poemEntity.text ?? ''), + ), const CustomSpacer(heightFactor: 0.02), - PoemLineCount(lineCount: poemEntity.linecount ?? 0), + + TopAnimation( + animationField: isPoemLineAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemLineCount(lineCount: poemEntity.linecount ?? 0), + ), const CustomSpacer(heightFactor: 0.02), ], ), From 05a787b9b4b5ac87874520d6fa4ee105eaecb163 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:53:55 +0200 Subject: [PATCH 368/475] Add buttons animation --- .../screens/saved_poems_screen.dart | 76 ++++++++++++++----- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index ce674f5..6b5930d 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; @@ -23,10 +24,32 @@ class SavedPoemsScreen extends StatefulWidget { class _SavedPoemsScreenState extends State { Future?>? collectionsFuture; + bool isButton1Animated = false; + bool isButton2Animated = false; + final Duration animationDelay = const Duration(milliseconds: 300); + @override void initState() { super.initState(); collectionsFuture = initCollections(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isButton1Animated = val, + (val) => isButton2Animated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } + ); + } } Future?> initCollections() async => context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); @@ -69,28 +92,39 @@ class _SavedPoemsScreenState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - FilledButton( - child: const Text('Create a collection'), - onPressed: () async{ - await showModalBottomSheet( - context: context, - isScrollControlled: true, - builder:(context) => CreateCollectionBottomSheetContent( - poems: collections?[0].poems, - ), - ); - - - collections = await context.read().getUserCollections(getIt().getCurrentUser().userId!); - }, + RightAnimation( + animationField: isButton2Animated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: FilledButton( + child: const Text('Create a collection'), + onPressed: () async{ + await showModalBottomSheet( + context: context, + isScrollControlled: true, + builder:(context) => CreateCollectionBottomSheetContent( + poems: collections?[0].poems, + ), + ); + + + collections = await context.read().getUserCollections(getIt().getCurrentUser().userId!); + }, + ), ), - FilledButton.tonal( - onPressed: () => Navigator.pushNamedAndRemoveUntil( - context, - writePoemPageConstant, - (route) => false, - ), - child: const Text('Write a poem'), + + RightAnimation( + animationField: isButton1Animated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: FilledButton.tonal( + onPressed: () => Navigator.pushNamedAndRemoveUntil( + context, + writePoemPageConstant, + (route) => false, + ), + child: const Text('Write a poem'), + ), ), ], ), From cc7231f8d3a8271ff0210e3de2adea1479ddf71d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:58:23 +0200 Subject: [PATCH 369/475] Add indentation --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 6b5930d..3e88bce 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -35,7 +35,7 @@ class _SavedPoemsScreenState extends State { _startAnimations(); } - void _startAnimations() { + void _startAnimations() { final setters = [ (val) => isButton1Animated = val, (val) => isButton2Animated = val, From d1ab09d544f74549aee2032a59fe9a27a4f23241 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:00:26 +0200 Subject: [PATCH 370/475] Add CollectionCard animation --- .../presentation/widgets/collection_card.dart | 68 ++++++++++++++----- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 8c0dcf2..c7a6560 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -4,29 +4,60 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -class CollectionCard extends StatelessWidget { +class CollectionCard extends StatefulWidget { const CollectionCard({super.key, required this.collection}); final CollectionEntity collection; + @override + State createState() => _CollectionCardState(); +} + +class _CollectionCardState extends State { + bool isAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } + ); + } + } + @override Widget build(BuildContext context) => Dismissible( key: UniqueKey(), - direction: collection.isAllSavedPoems + direction: widget.collection.isAllSavedPoems ? DismissDirection.none : DismissDirection.horizontal, onDismissed: (direction) { context.read().deleteCollection( userId: getIt().getCurrentUser().userId!, - collectionName: collection.name ?? '', - poems: collection.poems ?? [], + collectionName: widget.collection.name ?? '', + poems: widget.collection.poems ?? [], ); }, child: GestureDetector( - onTap: () => Navigator.pushNamed(context, savedCollectionViewConstant, arguments: collection), + onTap: () => Navigator.pushNamed(context, savedCollectionViewConstant, arguments: widget.collection), child: SizedBox( height: MediaQuery.of(context).size.height / 4, child: Card( @@ -38,17 +69,22 @@ class CollectionCard extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(16), child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _TitleText(title: collection.name), - - if (collection.poems != null) Column( - children: collection.poems!.map( - (poem) => _InfoText(author: poem.author, title: poem.title), - ).toList(), - ) else const _EmptyCollectionText(), - ], + child: TopAnimation( + animationField: isAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _TitleText(title: widget.collection.name), + + if (widget.collection.poems != null) Column( + children: widget.collection.poems!.map( + (poem) => _InfoText(author: poem.author, title: poem.title), + ).toList(), + ) else const _EmptyCollectionText(), + ], + ), ), ), ), From 899cf809b62c578d5f415746c9fa2f77df3e72ba Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:17:36 +0200 Subject: [PATCH 371/475] Add PoemViewPage animation --- .../pages/poem_view/poem_view.dart | 92 +++++++++++++++++-- .../widgets/poem_view/custom_save_button.dart | 37 ++++++-- 2 files changed, 111 insertions(+), 18 deletions(-) diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 05e86ee..a940658 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; @@ -9,9 +10,49 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_ import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; -class PoemViewPage extends StatelessWidget { +class PoemViewPage extends StatefulWidget { const PoemViewPage({super.key}); + @override + State createState() => _PoemViewPageState(); +} + +class _PoemViewPageState extends State { + bool isSaveButtonAnimated = false; + bool isTitleAnimated = false; + bool isAuthorAnimated = false; + bool isShareButtonAnimated = false; + bool isContentAnimated = false; + bool isPoemLineAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isSaveButtonAnimated = val, + (val) => isTitleAnimated = val, + (val) => isAuthorAnimated = val, + (val) => isShareButtonAnimated = val, + (val) => isContentAnimated = val, + (val) => isPoemLineAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } + ); + } + } + @override Widget build(BuildContext context) { final poemEntity = (ModalRoute.of(context)?.settings.arguments ?? const PoemEntity()) as PoemEntity; @@ -25,18 +66,53 @@ class PoemViewPage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( children: [ + const CustomSpacer(heightFactor: 0.04), + RightAnimation( + animationField: isSaveButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomSaveButton(poemEntity: poemEntity), + ), const CustomSpacer(heightFactor: 0.02), - CustomSaveButton(poemEntity: poemEntity), - const CustomSpacer(heightFactor: 0.02), - PoemTitle(title: poemEntity.title ?? ''), + + RightAnimation( + animationField: isTitleAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemTitle(title: poemEntity.title ?? ''), + ), const CustomSpacer(heightFactor: 0.02), - PoemAuthor(author: poemEntity.author ?? ''), + + RightAnimation( + animationField: isAuthorAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemAuthor(author: poemEntity.author ?? ''), + ), const CustomSpacer(heightFactor: 0.02), - CustomShareButton(poemEntity: poemEntity), + + RightAnimation( + animationField: isShareButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: CustomShareButton(poemEntity: poemEntity), + ), const CustomSpacer(heightFactor: 0.02), - PoemContent(text: poemEntity.text ?? ''), + + RightAnimation( + animationField: isContentAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemContent(text: poemEntity.text ?? ''), + ), const CustomSpacer(heightFactor: 0.02), - PoemLineCount(lineCount: poemEntity.linecount ?? 0), + + RightAnimation( + animationField: isPoemLineAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: PoemLineCount(lineCount: poemEntity.linecount ?? 0), + ), const CustomSpacer(heightFactor: 0.02), ], ), diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart index 7510525..24bcd32 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart @@ -6,20 +6,37 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -class CustomSaveButton extends StatelessWidget { +class CustomSaveButton extends StatefulWidget { const CustomSaveButton({super.key, required this.poemEntity}); final PoemEntity poemEntity; @override - Widget build(BuildContext context) => FutureBuilder( - future: context.read().isPoemExists(poemEntity: poemEntity, userId: getIt().getCurrentUser().userId!), + State createState() => _CustomSaveButtonState(); +} + +class _CustomSaveButtonState extends State { + late Future isPoemSavedFuture; + + @override + void initState() { + super.initState(); + isPoemSavedFuture = context.read().isPoemExists( + poemEntity: widget.poemEntity, + userId: getIt().getCurrentUser().userId!, + ); + } + + @override + Widget build(BuildContext context) => FutureBuilder( + future: isPoemSavedFuture, builder: (context, snapshot) { - if(snapshot.connectionState == ConnectionState.waiting){ + if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); - } else{ + } else { + bool isLiked = snapshot.data ?? false; return LikeButton( - isLiked: snapshot.data, + isLiked: isLiked, size: 42, circleColor: CircleColor(start: Theme.of(context).colorScheme.primaryContainer, end: Theme.of(context).colorScheme.primary), bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), @@ -27,13 +44,13 @@ class CustomSaveButton extends StatelessWidget { if(isLiked == false){ await context.read().savePoem( userId: getIt().getCurrentUser().userId!, - username: poemEntity.author ?? '', - title: poemEntity.title ?? '', - text: poemEntity.text ?? '', + username: widget.poemEntity.author ?? '', + title: widget.poemEntity.title ?? '', + text: widget.poemEntity.text ?? '', ); } else{ await context.read().deletePoem( - poemEntity: poemEntity, + poemEntity: widget.poemEntity, userId: getIt().getCurrentUser().userId!, ); } From ccdc19786cb1b8d0eb58b1602d249c06186fd694 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:25:47 +0200 Subject: [PATCH 372/475] Add animations in the UpdateCollectionBottomSheetContent --- ...pdate_collection_bottom_sheet_content.dart | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 537e282..081a482 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -5,6 +5,7 @@ import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; @@ -25,6 +26,11 @@ class _UpdateCollectionBottomSheetContentState extends State> selectedValues = >[]; late List> allValues = >[]; + bool isTitleAnimated = false; + bool isSelectorAnimated = false; + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + @override void initState() { super.initState(); @@ -32,6 +38,25 @@ class _UpdateCollectionBottomSheetContentState extends State[ + (val) => isTitleAnimated = val, + (val) => isSelectorAnimated = val, + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } + ); + } } void _initPoemsInTheCollectionValues(){ @@ -64,11 +89,28 @@ class _UpdateCollectionBottomSheetContentState extends State Date: Mon, 11 Dec 2023 22:39:07 +0200 Subject: [PATCH 373/475] Add SavedPoemCard animation --- .../presentation/widgets/saved_poem_card.dart | 68 ++++++++++++++----- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 69b65b4..e66b0bd 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -4,34 +4,65 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -class SavedPoemCard extends StatelessWidget { +class SavedPoemCard extends StatefulWidget { const SavedPoemCard({super.key, required this.poemEntity, required this.collectionEntity}); final PoemEntity poemEntity; final CollectionEntity collectionEntity; + @override + State createState() => _SavedPoemCardState(); +} + +class _SavedPoemCardState extends State { + bool isAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + void _startAnimations() { + final setters = [ + (val) => isAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_){ + if (mounted) { + setState(() => setters[i](true)); + } + } + ); + } + } + @override Widget build(BuildContext context) => Dismissible( key: UniqueKey(), onDismissed: (direction) async{ - if(collectionEntity.isAllSavedPoems){ + if(widget.collectionEntity.isAllSavedPoems){ await context.read().deletePoemFromCollection( - poemEntity: poemEntity, + poemEntity: widget.poemEntity, userId: getIt().getCurrentUser().userId!, ); } else{ await context.read().deletePoemFromCollection( - poemEntity: poemEntity, + poemEntity: widget.poemEntity, userId: getIt().getCurrentUser().userId!, - collectionName: collectionEntity.name ?? '', + collectionName: widget.collectionEntity.name ?? '', ); } }, child: GestureDetector( - onTap: () => Navigator.pushNamed(context, savedPoemViewConstant, arguments: poemEntity), + onTap: () => Navigator.pushNamed(context, savedPoemViewConstant, arguments: widget.poemEntity), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), @@ -40,16 +71,21 @@ class SavedPoemCard extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Padding( padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _TitleText(title: poemEntity.title), - const SizedBox(height: 8), - _AuthorText(author: poemEntity.author), - const SizedBox(height: 16), - _PoemText(text: poemEntity.text, maxLength: 250), - ], + child: RightAnimation( + animationField: isAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + opacityInitialValue: 0, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TitleText(title: widget.poemEntity.title), + const SizedBox(height: 8), + _AuthorText(author: widget.poemEntity.author), + const SizedBox(height: 16), + _PoemText(text: widget.poemEntity.text, maxLength: 250), + ], + ), ), ), ), From c1ffe2b6a7afa73f96bfeaa0e23ee66162f35254 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:03:43 +0200 Subject: [PATCH 374/475] Add animation to the registration page --- .../pages/registration/registration_page.dart | 234 ++++++++++++++---- 1 file changed, 191 insertions(+), 43 deletions(-) diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index c82090f..8ec04bf 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; @@ -5,69 +7,161 @@ import 'package:poetlum/features/authorization/presentation/bloc/authorization/a import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; +import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; -class _Header extends StatelessWidget { +class _Header extends StatefulWidget { const _Header(); + @override + State<_Header> createState() => _HeaderState(); +} + +class _HeaderState extends State<_Header> { + bool isHeaderAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + void _startAnimations() { + final setters = [ + (val) => isHeaderAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + + @override + void initState() { + super.initState(); + _startAnimations(); + } + @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height/10, - child: const Column( + child: Column( children: [ - Spacer(), - Text( - 'Registration', - style: TextStyle( - fontSize: 22, + const Spacer(), + + TopAnimation( + animationField: isHeaderAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: const Text( + 'Registration', + style: TextStyle( + fontSize: 22, + ), ), ), - Spacer(), + + + const Spacer(), ], ), ); } -class _Form extends StatelessWidget { +class _Form extends StatefulWidget { const _Form(this.usernameController, this.emailController, this.passwordController); final TextEditingController usernameController; final TextEditingController emailController; final TextEditingController passwordController; + @override + State<_Form> createState() => _FormState(); +} + +class _FormState extends State<_Form> { + bool isUsernameAnimated = false; + bool isEmailAnimated = false; + bool isPasswordAnimated = false; + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + + Future _startAnimations() async { + await Future.delayed(const Duration(milliseconds: 200)); + + final setters = [ + (val) => isUsernameAnimated = val, + (val) => isEmailAnimated = val, + (val) => isPasswordAnimated = val, + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + unawaited( + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ), + ); + } + } + + @override + void initState() { + super.initState(); + _startAnimations(); + } + @override Widget build(BuildContext context) => BlocBuilder( builder: (context, state) => SizedBox( height: MediaQuery.of(context).size.height/2.75, child: Column( children: [ - UsernameTextField(controller: usernameController), + TopAnimation( + animationField: isUsernameAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: UsernameTextField(controller: widget.usernameController), + ), const Spacer(), - EmailTextField(controller: emailController), + TopAnimation( + animationField: isEmailAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: EmailTextField(controller: widget.emailController), + ), const Spacer(), - - PasswordTextField(controller: passwordController), + + TopAnimation( + animationField: isPasswordAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: PasswordTextField(controller: widget.passwordController), + ), const Spacer(), - - AuthButton< - AuthCubit, - AuthState, - RegisterFormValidationCubit, - RegisterFormValidationState - >( - isEnabled: state.isFormValid, - text: 'Register', - successfulToastText: 'Your registration was successful', - onPressed: () => context.read().register( - username: state.usernameValidationState.value, - email: state.emailValidationState.value, - password: state.passwordValidationState.value, + + TopAnimation( + animationField: isButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: AuthButton< + AuthCubit, + AuthState, + RegisterFormValidationCubit, + RegisterFormValidationState + >( + isEnabled: state.isFormValid, + text: 'Register', + successfulToastText: 'Your registration was successful', + onPressed: () => context.read().register( + username: state.usernameValidationState.value, + email: state.emailValidationState.value, + password: state.passwordValidationState.value, + ), + navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, screensWrapperPageConstant, (route) => false), ), - navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, screensWrapperPageConstant, (route) => false), ), ], ), @@ -75,26 +169,61 @@ class _Form extends StatelessWidget { ); } -class _Footer extends StatelessWidget { +class _Footer extends StatefulWidget { const _Footer(); + @override + State<_Footer> createState() => _FooterState(); +} + +class _FooterState extends State<_Footer> { + bool isFooterAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + Future _startAnimations() async { + await Future.delayed(const Duration(milliseconds: 1000)); + + final setters = [ + (val) => isFooterAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + unawaited( + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ), + ); + } + } + + @override + void initState() { + super.initState(); + _startAnimations(); + } + @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height/10, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Already have an account?'), - TextButton( - onPressed: (){ - Navigator.pushNamedAndRemoveUntil(context, loginPageConstant, (r) => false); - }, - child: const Text( - 'Login', - style: TextStyle(decoration: TextDecoration.underline), + child: TopAnimation( + animationField: isFooterAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Already have an account?'), + TextButton( + onPressed: (){ + Navigator.pushNamedAndRemoveUntil(context, loginPageConstant, (r) => false); + }, + child: const Text( + 'Login', + style: TextStyle(decoration: TextDecoration.underline), + ), ), - ), - ], + ], + ), ), ); } @@ -111,6 +240,23 @@ class _RegistrationPageState extends State { final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); + + + bool isTextAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + void _startAnimations() { + final setters = [ + (val) => isTextAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + @override void initState() { super.initState(); @@ -128,6 +274,8 @@ class _RegistrationPageState extends State { _passwordController.addListener(() { formCubit.passwordChanged(_passwordController.text); }); + + _startAnimations(); } @override From 55324b4db3d3a59c2fa74d9a752e33918726e015 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:16:43 +0200 Subject: [PATCH 375/475] Add animations to the login page --- .../presentation/pages/login/login_page.dart | 208 ++++++++++++++---- 1 file changed, 165 insertions(+), 43 deletions(-) diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 1b8c779..aca3f4d 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; @@ -8,62 +10,147 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; -class _Header extends StatelessWidget { +class _Header extends StatefulWidget { const _Header(); + @override + State<_Header> createState() => _HeaderState(); +} + +class _HeaderState extends State<_Header> { + bool isHeaderAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + void _startAnimations() { + final setters = [ + (val) => isHeaderAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + + @override + void initState() { + super.initState(); + _startAnimations(); + } + @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height/10, - child: const Column( + child: Column( children: [ - Spacer(), - Text( - 'Login', - style: TextStyle( - fontSize: 22, + const Spacer(), + + RightAnimation( + animationField: isHeaderAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: const Text( + 'Login', + style: TextStyle( + fontSize: 22, + ), ), ), - Spacer(), + + const Spacer(), ], ), ); } -class _Form extends StatelessWidget { +class _Form extends StatefulWidget { const _Form(this.emailController, this.passwordController); final TextEditingController emailController; final TextEditingController passwordController; + @override + State<_Form> createState() => _FormState(); +} + +class _FormState extends State<_Form> { + bool isUsernameAnimated = false; + bool isEmailAnimated = false; + bool isPasswordAnimated = false; + bool isButtonAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + + Future _startAnimations() async { + await Future.delayed(const Duration(milliseconds: 200)); + + final setters = [ + (val) => isEmailAnimated = val, + (val) => isPasswordAnimated = val, + (val) => isButtonAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + unawaited( + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ), + ); + } + } + + @override + void initState() { + super.initState(); + _startAnimations(); + } + @override Widget build(BuildContext context) => BlocBuilder( builder: (context, state) => SizedBox( height: MediaQuery.of(context).size.height/3.5, child: Column( children: [ - EmailTextField(controller: emailController), + RightAnimation( + animationField: isEmailAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: EmailTextField(controller: widget.emailController), + ), const Spacer(), - - PasswordTextField(controller: passwordController), + + RightAnimation( + animationField: isPasswordAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: PasswordTextField(controller: widget.passwordController), + ), const Spacer(), - - AuthButton< - AuthCubit, - AuthState, - LoginFormValidationCubit, - LoginFormValidationState - >( - isEnabled: state.isFormValid, - text: 'Login', - successfulToastText: 'Your login was successful', - onPressed: () { - context.read().login( - email: state.emailValidationState.value, - password: state.passwordValidationState.value, - ); - }, - navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, screensWrapperPageConstant, (route) => false), + + RightAnimation( + animationField: isButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: AuthButton< + AuthCubit, + AuthState, + LoginFormValidationCubit, + LoginFormValidationState + >( + isEnabled: state.isFormValid, + text: 'Login', + successfulToastText: 'Your login was successful', + onPressed: () { + context.read().login( + email: state.emailValidationState.value, + password: state.passwordValidationState.value, + ); + }, + navigateOnSuccess: () => Navigator.pushNamedAndRemoveUntil(context, screensWrapperPageConstant, (route) => false), + ), ), ], ), @@ -71,26 +158,61 @@ class _Form extends StatelessWidget { ); } -class _Footer extends StatelessWidget { +class _Footer extends StatefulWidget { const _Footer(); + @override + State<_Footer> createState() => _FooterState(); +} + +class _FooterState extends State<_Footer> { + bool isFooterAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + Future _startAnimations() async { + await Future.delayed(const Duration(milliseconds: 800)); + + final setters = [ + (val) => isFooterAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + unawaited( + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ), + ); + } + } + + @override + void initState() { + super.initState(); + _startAnimations(); + } + @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height/10, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text("Don't have an account?"), - TextButton( - onPressed: (){ - Navigator.pushNamedAndRemoveUntil(context, registerPageConstant, (r) => false); - }, - child: const Text( - 'Register', - style: TextStyle(decoration: TextDecoration.underline), + child: RightAnimation( + animationField: isFooterAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + opacityInitialValue: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Don't have an account?"), + TextButton( + onPressed: (){ + Navigator.pushNamedAndRemoveUntil(context, registerPageConstant, (r) => false); + }, + child: const Text( + 'Register', + style: TextStyle(decoration: TextDecoration.underline), + ), ), - ), - ], + ], + ), ), ); } From ac874e2fa7bad48669f4d209b029ee3530a33928 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:39:10 +0200 Subject: [PATCH 376/475] Remove unused code --- .../presentation/screens/saved_poems_screen.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 3e88bce..166296a 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -80,10 +80,6 @@ class _SavedPoemsScreenState extends State { var collections = snapshot.data; - if (collections == null || collections.isEmpty) { - return const Center(child: Text("You haven't saved any poems yet. :(")); - } - return SingleChildScrollView( child: Column( children: [ @@ -130,8 +126,9 @@ class _SavedPoemsScreenState extends State { ), ), if (collections == null || collections!.isEmpty) - const Text("You haven't saved any poems yet. :(") - else ListView.builder( + const Text("You haven't saved any poems yet. :(") , + if (!(collections == null || collections!.isEmpty)) + ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: collections!.length, @@ -148,3 +145,4 @@ class _SavedPoemsScreenState extends State { }, ); } + From 299dd6c275b3ef1da0e43f0eca4c6f2dbeb07050 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:42:36 +0200 Subject: [PATCH 377/475] Change animation type --- .../widgets/create_collection_bottom_sheet.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 46b5b3c..98ef025 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -7,7 +7,7 @@ import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -71,7 +71,7 @@ class _CreateCollectionBottomSheetContentState extends State Date: Tue, 12 Dec 2023 10:51:15 +0200 Subject: [PATCH 378/475] Remove unused fields --- .../widgets/app_bar/buttons/refresh_button.dart | 1 - .../widgets/app_bar/buttons/settings_button.dart | 2 -- .../authorization/presentation/pages/login/login_page.dart | 5 ----- .../presentation/pages/registration/registration_page.dart | 6 ------ .../poems_feed/presentation/pages/poem_view/poem_view.dart | 6 ------ .../presentation/widgets/animations/right_animation.dart | 4 +--- .../presentation/widgets/animations/top_animation.dart | 4 +--- .../presentation/widgets/drawer/custom_drawer.dart | 7 ------- .../presentation/widgets/poems_feed/poem_card.dart | 3 --- .../presentation/pages/saved_poem_view_page.dart | 5 ----- .../saved_poems/presentation/pages/write_poem_page.dart | 3 --- .../presentation/screens/saved_poems_screen.dart | 2 -- .../saved_poems/presentation/widgets/collection_card.dart | 1 - .../widgets/create_collection_bottom_sheet.dart | 4 ---- .../saved_poems/presentation/widgets/saved_poem_card.dart | 1 - .../widgets/update_collection_bottom_sheet_content.dart | 3 --- 16 files changed, 2 insertions(+), 55 deletions(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart index 2c11219..59f3d69 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -39,7 +39,6 @@ class _RefreshButtonState extends State with TickerProviderStateM Widget build(BuildContext context) => RightAnimation( animationField: isButtonAnimated , positionInitialValue: MediaQuery.of(context).size.width/6, - opacityInitialValue: 0, child: BlocBuilder( builder: (context, state) => RotationTransition( turns: rotationAnimation, diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 5daca14..cf172d1 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -37,7 +37,6 @@ class _SettingsButtonState extends State with TickerProviderStat Widget build(BuildContext context) => RightAnimation( animationField: isButtonAnimated , positionInitialValue: MediaQuery.of(context).size.width/6, - opacityInitialValue: 0, child: RotationTransition( turns: rotationAnimation, child: IconButton( @@ -116,7 +115,6 @@ class _BottomSheetContentState extends State<_BottomSheetContent> { RightAnimation( animationField: isHeaderAnimated, positionInitialValue: MediaQuery.of(context).size.width/6, - opacityInitialValue: 0, child: const _Title(text: 'Choose your theme'), ), diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index aca3f4d..1ad933e 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -51,7 +51,6 @@ class _HeaderState extends State<_Header> { RightAnimation( animationField: isHeaderAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: const Text( 'Login', style: TextStyle( @@ -117,7 +116,6 @@ class _FormState extends State<_Form> { RightAnimation( animationField: isEmailAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: EmailTextField(controller: widget.emailController), ), const Spacer(), @@ -125,7 +123,6 @@ class _FormState extends State<_Form> { RightAnimation( animationField: isPasswordAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: PasswordTextField(controller: widget.passwordController), ), const Spacer(), @@ -133,7 +130,6 @@ class _FormState extends State<_Form> { RightAnimation( animationField: isButtonAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: AuthButton< AuthCubit, AuthState, @@ -197,7 +193,6 @@ class _FooterState extends State<_Footer> { child: RightAnimation( animationField: isFooterAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 8ec04bf..48caa49 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -52,7 +52,6 @@ class _HeaderState extends State<_Header> { TopAnimation( animationField: isHeaderAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: const Text( 'Registration', style: TextStyle( @@ -121,7 +120,6 @@ class _FormState extends State<_Form> { TopAnimation( animationField: isUsernameAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: UsernameTextField(controller: widget.usernameController), ), const Spacer(), @@ -129,7 +127,6 @@ class _FormState extends State<_Form> { TopAnimation( animationField: isEmailAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: EmailTextField(controller: widget.emailController), ), const Spacer(), @@ -137,7 +134,6 @@ class _FormState extends State<_Form> { TopAnimation( animationField: isPasswordAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: PasswordTextField(controller: widget.passwordController), ), const Spacer(), @@ -145,7 +141,6 @@ class _FormState extends State<_Form> { TopAnimation( animationField: isButtonAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: AuthButton< AuthCubit, AuthState, @@ -208,7 +203,6 @@ class _FooterState extends State<_Footer> { child: TopAnimation( animationField: isFooterAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index a940658..624179e 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -70,7 +70,6 @@ class _PoemViewPageState extends State { RightAnimation( animationField: isSaveButtonAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: CustomSaveButton(poemEntity: poemEntity), ), const CustomSpacer(heightFactor: 0.02), @@ -78,7 +77,6 @@ class _PoemViewPageState extends State { RightAnimation( animationField: isTitleAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemTitle(title: poemEntity.title ?? ''), ), const CustomSpacer(heightFactor: 0.02), @@ -86,7 +84,6 @@ class _PoemViewPageState extends State { RightAnimation( animationField: isAuthorAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemAuthor(author: poemEntity.author ?? ''), ), const CustomSpacer(heightFactor: 0.02), @@ -94,7 +91,6 @@ class _PoemViewPageState extends State { RightAnimation( animationField: isShareButtonAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: CustomShareButton(poemEntity: poemEntity), ), const CustomSpacer(heightFactor: 0.02), @@ -102,7 +98,6 @@ class _PoemViewPageState extends State { RightAnimation( animationField: isContentAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemContent(text: poemEntity.text ?? ''), ), const CustomSpacer(heightFactor: 0.02), @@ -110,7 +105,6 @@ class _PoemViewPageState extends State { RightAnimation( animationField: isPoemLineAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemLineCount(lineCount: poemEntity.linecount ?? 0), ), const CustomSpacer(heightFactor: 0.02), diff --git a/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart b/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart index 0d3571c..35cda25 100644 --- a/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart +++ b/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart @@ -7,13 +7,11 @@ class RightAnimation extends StatelessWidget { required this.child, required this.animationField, required this.positionInitialValue, - required this.opacityInitialValue, }); final Widget child; final bool animationField; final double positionInitialValue; - final double opacityInitialValue; @override Widget build(BuildContext context) => TweenAnimationBuilder( @@ -21,7 +19,7 @@ class RightAnimation extends StatelessWidget { duration: animationDuration, curve: animationCurve, builder: (context, value, child) => Opacity( - opacity: opacityInitialValue + (1 - opacityInitialValue) * value, + opacity: value, child: Transform.translate( offset: Offset(-positionInitialValue * (1 - value), 0), child: child, diff --git a/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart b/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart index 640007c..eed94b5 100644 --- a/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart +++ b/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart @@ -7,13 +7,11 @@ class TopAnimation extends StatelessWidget { required this.child, required this.animationField, required this.positionInitialValue, - required this.opacityInitialValue, }); final Widget child; final bool animationField; final double positionInitialValue; - final double opacityInitialValue; @override Widget build(BuildContext context) => TweenAnimationBuilder( @@ -21,7 +19,7 @@ class TopAnimation extends StatelessWidget { duration: animationDuration, curve: animationCurve, builder: (context, value, child) => Opacity( - opacity: opacityInitialValue + (1 - opacityInitialValue) * value, + opacity: value, child: Transform.translate( offset: Offset(0, positionInitialValue * (1 - value)), child: child, diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index abfbc8b..586d5cd 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -73,14 +73,12 @@ class _CustomDrawerState extends State { TopAnimation( animationField: isHeaderAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), ), TopAnimation( animationField: isAuthorAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: CustomTextField(hintText: 'Author', controller: _authorController), ), const CustomSpacer(heightFactor: 0.04), @@ -88,7 +86,6 @@ class _CustomDrawerState extends State { TopAnimation( animationField: isTitleAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: CustomTextField(hintText: 'Title', controller: _titleController), ), const CustomSpacer(heightFactor: 0.04), @@ -96,7 +93,6 @@ class _CustomDrawerState extends State { TopAnimation( animationField: isLinesNumberAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), ), const CustomSpacer(heightFactor: 0.04), @@ -104,7 +100,6 @@ class _CustomDrawerState extends State { TopAnimation( animationField: isResultCountAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), ), const CustomSpacer(heightFactor: 0.04), @@ -112,7 +107,6 @@ class _CustomDrawerState extends State { TopAnimation( animationField: isCheckboxAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), ), const CustomSpacer(heightFactor: 0.04), @@ -120,7 +114,6 @@ class _CustomDrawerState extends State { TopAnimation( animationField: isButtonAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: CustomSearchButton( onPressed: () { BlocProvider.of(context).add( diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart index 82197c4..0e8042a 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart @@ -62,7 +62,6 @@ class _PoemCardState extends State { RightAnimation( animationField: isTitleAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: _TitleText(title: widget.poemEntity.title), ), const SizedBox(height: 8), @@ -70,7 +69,6 @@ class _PoemCardState extends State { RightAnimation( animationField: isAuthorAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: _AuthorText(author: widget.poemEntity.author), ), const SizedBox(height: 16), @@ -78,7 +76,6 @@ class _PoemCardState extends State { RightAnimation( animationField: isTextAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: _PoemText(text: widget.poemEntity.text, maxLength: 250), ), ], diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 5f425ad..6e31d59 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -68,7 +68,6 @@ class _SavedPoemViewPageState extends State { TopAnimation( animationField: isTitleAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemTitle(title: poemEntity.title ?? ''), ), const CustomSpacer(heightFactor: 0.02), @@ -76,7 +75,6 @@ class _SavedPoemViewPageState extends State { TopAnimation( animationField: isAuthorAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemAuthor(author: poemEntity.author ?? ''), ), const CustomSpacer(heightFactor: 0.02), @@ -84,7 +82,6 @@ class _SavedPoemViewPageState extends State { TopAnimation( animationField: isShareButtonAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: CustomShareButton(poemEntity: poemEntity), ), const CustomSpacer(heightFactor: 0.02), @@ -92,7 +89,6 @@ class _SavedPoemViewPageState extends State { TopAnimation( animationField: isContentAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemContent(text: poemEntity.text ?? ''), ), const CustomSpacer(heightFactor: 0.02), @@ -100,7 +96,6 @@ class _SavedPoemViewPageState extends State { TopAnimation( animationField: isPoemLineAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: PoemLineCount(lineCount: poemEntity.linecount ?? 0), ), const CustomSpacer(heightFactor: 0.02), diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 036fddf..e26baa7 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -78,7 +78,6 @@ class _WritePoemPageState extends State { RightAnimation( animationField: isNameTextFieldAnimated, positionInitialValue: MediaQuery.of(context).size.width/3, - opacityInitialValue: 0, child: _CustomTextField(hintText: 'Poem name', controller: _nameController, isLarge: false), ), const _CustomSpacer(heightFactor: 0.03), @@ -86,7 +85,6 @@ class _WritePoemPageState extends State { RightAnimation( animationField: isPoemTextFieldAnimated, positionInitialValue: MediaQuery.of(context).size.width/3, - opacityInitialValue: 0, child: _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), ), const _CustomSpacer(heightFactor: 0.03), @@ -96,7 +94,6 @@ class _WritePoemPageState extends State { else RightAnimation( animationField: isButtonAnimated, positionInitialValue: MediaQuery.of(context).size.width/3, - opacityInitialValue: 0, child: FilledButton.tonal( onPressed: () { if (_formKey.currentState!.validate()) { diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 166296a..f07b981 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -91,7 +91,6 @@ class _SavedPoemsScreenState extends State { RightAnimation( animationField: isButton2Animated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: FilledButton( child: const Text('Create a collection'), onPressed: () async{ @@ -112,7 +111,6 @@ class _SavedPoemsScreenState extends State { RightAnimation( animationField: isButton1Animated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: FilledButton.tonal( onPressed: () => Navigator.pushNamedAndRemoveUntil( context, diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index c7a6560..c687b49 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -72,7 +72,6 @@ class _CollectionCardState extends State { child: TopAnimation( animationField: isAnimated, positionInitialValue: MediaQuery.of(context).size.height/14, - opacityInitialValue: 0, child: Column( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 98ef025..17dfa4e 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -74,7 +74,6 @@ class _CreateCollectionBottomSheetContentState extends State { child: RightAnimation( animationField: isAnimated, positionInitialValue: MediaQuery.of(context).size.width/8, - opacityInitialValue: 0, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 081a482..8be8a40 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -92,7 +92,6 @@ class _UpdateCollectionBottomSheetContentState extends State Date: Tue, 12 Dec 2023 10:54:07 +0200 Subject: [PATCH 379/475] Add text animation --- .../presentation/screens/saved_poems_screen.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index f07b981..7edfe9d 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -6,6 +6,7 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; @@ -26,6 +27,7 @@ class _SavedPoemsScreenState extends State { bool isButton1Animated = false; bool isButton2Animated = false; + bool isTextAnimated = false; final Duration animationDelay = const Duration(milliseconds: 300); @override @@ -39,6 +41,7 @@ class _SavedPoemsScreenState extends State { final setters = [ (val) => isButton1Animated = val, (val) => isButton2Animated = val, + (val) => isTextAnimated = val, ]; for (var i = 0; i < setters.length; i++) { @@ -124,7 +127,11 @@ class _SavedPoemsScreenState extends State { ), ), if (collections == null || collections!.isEmpty) - const Text("You haven't saved any poems yet. :(") , + TopAnimation( + animationField: isTextAnimated, + positionInitialValue: MediaQuery.of(context).size.width/8, + child: const Text("You haven't saved any poems yet. :(") , + ), if (!(collections == null || collections!.isEmpty)) ListView.builder( shrinkWrap: true, From f0fa7a2e18e3cae701aca4585831661ffb19016d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:03:41 +0200 Subject: [PATCH 380/475] Fix internet connection monitoring --- lib/main.dart | 17 ++++---- pubspec.lock | 108 +++++++++++++++++++++++++------------------------- 2 files changed, 64 insertions(+), 61 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 2f9b770..4e3629d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:poetlum/features/application/poetlum_app.dart'; import 'package:poetlum/features/dependency_injection/presentation/widgets/init_dependencies.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_firebase_widget.dart'; +import 'package:poetlum/features/internet_connection_monitoring/presentation/widgets/init_network_controller.dart'; import 'package:poetlum/features/multi_bloc_provider/presentation/init_blocs.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/init_theme_widget.dart'; import 'package:poetlum/firebase_options.dart'; @@ -11,13 +12,15 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( - InitFirebaseWidget( - options: DefaultFirebaseOptions.currentPlatform, - child: const InitDependencies( - child: InitCrashlyticsWidget( - child: InitBlocs( - child: InitTheme( - child: PoetlumApp(), + InitNetworkController( + child: InitFirebaseWidget( + options: DefaultFirebaseOptions.currentPlatform, + child: const InitDependencies( + child: InitCrashlyticsWidget( + child: InitBlocs( + child: InitTheme( + child: PoetlumApp(), + ), ), ), ), diff --git a/pubspec.lock b/pubspec.lock index f9ec02e..855ba2c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "76c15c4167f820b74abcd8c6fc5e393e1ed5e1207a34e9b22953603e03b3ba6a" + sha256: f5628cd9c92ed11083f425fd1f8f1bc60ecdda458c81d73b143aeda036c35fe7 url: "https://pub.dev" source: hosted - version: "1.3.9" + version: "1.3.16" analyzer: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.9" args: dependency: transitive description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" build_runner_core: dependency: transitive description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: built_value - sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e" + sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 url: "https://pub.dev" source: hosted - version: "8.7.0" + version: "8.8.1" characters: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.8.0" collection: dependency: transitive description: @@ -189,10 +189,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f + sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.2" connectivity_plus_platform_interface: dependency: transitive description: @@ -237,26 +237,26 @@ packages: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" dbus: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" dio: dependency: "direct main" description: name: dio - sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7" + sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" url: "https://pub.dev" source: hosted - version: "5.3.3" + version: "5.4.0" email_validator: dependency: "direct main" description: @@ -301,58 +301,58 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: be3edadbd6e3a7840d9eae38ec3dfc6d83efab5c6e0d5cee5f758341d0643c30 + sha256: "5e92d510eacd66c354718fd9cc8f66ffdfa025640b645c4742297fb973770508" url: "https://pub.dev" source: hosted - version: "10.6.1" + version: "10.7.4" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: fc7c032f769e2c37bc7aa84eb44b02d17d53de8c256882072dbedbf1def158b8 + sha256: "72977325a72af5ebb8e53b5c5533cb2e33eec481cd46210cfe5427f5efba55d8" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.8.4" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "219e5995e79aff8deb3d633041dbdb8f704c1f5a044e970eed141ad851caf9d1" + sha256: "8b9710be7e292e2a5ad34fff449d4b668c5808fb339649e69181727a4534f579" url: "https://pub.dev" source: hosted - version: "0.5.5+3" + version: "0.5.5+11" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: "46129e2733336ac77377174c3ca33a68cca2cd5848504aad63028aeb92afb7b2" + sha256: a87cfdd16b2bd04ca4a61e8b8d95daafaae487aa4c6533c9497199d631eefe6f url: "https://pub.dev" source: hosted - version: "4.11.1" + version: "4.15.2" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: b89936896b2cc02496b97e486793fd4bcf8c51beb99d6a7223c0eea2352d404e + sha256: "4ca90755469fd20bf32070a3ff4dcb6256cfafc47230cc021261acced672ed3c" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.8" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "88b7655c9394d723e121fd53f115d876c32260b8c8b499bdc3108341044a8306" + sha256: "8eb2b4d35ed46405783c135cdf7c4c2d2769abe09cee60fcd8080c0475a9e8b5" url: "https://pub.dev" source: hosted - version: "5.8.4" + version: "5.8.11" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "57bba167105d2315d243a4524939406df688f38a5b6d7a4159382bbbe43cdd00" + sha256: "96607c0e829a581c2a483c658f04e8b159964c3bae2730f73297070bc85d40bb" url: "https://pub.dev" source: hosted - version: "2.19.0" + version: "2.24.2" firebase_core_platform_interface: dependency: transitive description: @@ -365,50 +365,50 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "0631a2ec971dbc540275e2fa00c3a8a2676f0a7adbc3c197d6fba569db689d97" + sha256: d585bdf3c656c3f7821ba1bd44da5f13365d22fcecaf5eb75c4295246aaa83c0 url: "https://pub.dev" source: hosted - version: "2.8.1" + version: "2.10.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: c58902959e7a73aadcf82c87e8d64f6eb10b5287fd7aaadfab5c2ef34ec11ff5 + sha256: "5ccdf05de039f9544d0ba41c5ae2052ca2425985d32229911b09f69981164518" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.4.8" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "9d2f76bda1542615f75fb501a8c7afc6288d69c3728fdc762bf3d51f0ee0f4cb" + sha256: "359197344def001589c84f8d1d57c05f6e2e773f559205610ce58c25e2045a57" url: "https://pub.dev" source: hosted - version: "3.6.9" + version: "3.6.16" firebase_database: dependency: "direct main" description: name: firebase_database - sha256: b7e4ab1bc08353adb645aa3b86ceed3e018eea33451732376e751479d13c8e2f + sha256: "80f67c961ed81341ac93120750120469ac3122baa8c0d3374a620f5834b5519f" url: "https://pub.dev" source: hosted - version: "10.3.1" + version: "10.3.8" firebase_database_platform_interface: dependency: transitive description: name: firebase_database_platform_interface - sha256: "69e696ae07ba1f3a9f6e6cda2ee3b78c7426cddf31afd2029e0d594926e95563" + sha256: "4366ade2390f8799a317bb13af29c2a1fdfc84f4d04372094756b86a6cbfd305" url: "https://pub.dev" source: hosted - version: "0.2.5+9" + version: "0.2.5+16" firebase_database_web: dependency: transitive description: name: firebase_database_web - sha256: "77899e5a4dc10b5e5a1fe2ff7840143835644351adff9b64bbf9d48c496ea3a7" + sha256: "4920a83b917493b37fd408cbb01c289ef8a422d9ed48982f908a9850290262f9" url: "https://pub.dev" source: hosted - version: "0.2.3+9" + version: "0.2.3+16" fixnum: dependency: transitive description: @@ -442,10 +442,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -636,10 +636,10 @@ packages: dependency: "direct main" description: name: multi_dropdown - sha256: "43f580a3a641462d6962c1dd4bccede40884d9210519b6f8809337e39b62bc2f" + sha256: "85647129b9ee7aedf7db90165c54403879c7ca1876b827c3c042c6631e55bd11" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" nested: dependency: transitive description: @@ -740,10 +740,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" pointycastle: dependency: transitive description: @@ -764,10 +764,10 @@ packages: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: From 3214930f971b2a3caeecb20112b7c8eeb098dcb3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:38:37 +0200 Subject: [PATCH 381/475] Fix unrefreshing poems issue --- .../presentation/pages/saved_collection_view_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 802a3ea..29ffe21 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -69,9 +69,9 @@ class _SavedCollectionViewPageState extends State { child: const Icon(Icons.edit), ), body: BlocConsumer( - listener: (context, state) { + listener: (context, state) async { if (state.status == FirebaseDatabaseStatus.needsRefresh) { - initPoems(); + await initPoems(); } }, builder: (context, state) { From 4d9f3c5785254d4d705bd49e2d1d761ff98750c9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:02:37 +0200 Subject: [PATCH 382/475] Create loader widget --- .../presentation/widgets/loader.dart | 36 ++++++ pubspec.lock | 108 +++++++++--------- 2 files changed, 90 insertions(+), 54 deletions(-) create mode 100644 lib/features/application/presentation/widgets/loader.dart diff --git a/lib/features/application/presentation/widgets/loader.dart b/lib/features/application/presentation/widgets/loader.dart new file mode 100644 index 0000000..e248922 --- /dev/null +++ b/lib/features/application/presentation/widgets/loader.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class Loader extends StatelessWidget { + const Loader({super.key, required this.text}); + + final String text; + + @override + Widget build(BuildContext context) => Center( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CircularProgressIndicator(), + _Label(text: text), + ], + ), + ); +} + +class _Label extends StatelessWidget { + const _Label({required this.text}); + + final String text; + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Text( + text, + style: const TextStyle( + fontSize: 18, + ), + ), + ); +} diff --git a/pubspec.lock b/pubspec.lock index 855ba2c..f9ec02e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: f5628cd9c92ed11083f425fd1f8f1bc60ecdda458c81d73b143aeda036c35fe7 + sha256: "76c15c4167f820b74abcd8c6fc5e393e1ed5e1207a34e9b22953603e03b3ba6a" url: "https://pub.dev" source: hosted - version: "1.3.16" + version: "1.3.9" analyzer: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" + sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" url: "https://pub.dev" source: hosted - version: "3.4.9" + version: "3.4.6" args: dependency: transitive description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.0" build_resolvers: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" + sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" url: "https://pub.dev" source: hosted - version: "2.4.7" + version: "2.4.6" build_runner_core: dependency: transitive description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: built_value - sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 + sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e" url: "https://pub.dev" source: hosted - version: "8.8.1" + version: "8.7.0" characters: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: code_builder - sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f + sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.7.0" collection: dependency: transitive description: @@ -189,10 +189,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" + sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.1" connectivity_plus_platform_interface: dependency: transitive description: @@ -237,26 +237,26 @@ packages: dependency: transitive description: name: dart_style - sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" + sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.3" dbus: dependency: transitive description: name: dbus - sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.8" dio: dependency: "direct main" description: name: dio - sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.3.3" email_validator: dependency: "direct main" description: @@ -301,58 +301,58 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: "5e92d510eacd66c354718fd9cc8f66ffdfa025640b645c4742297fb973770508" + sha256: be3edadbd6e3a7840d9eae38ec3dfc6d83efab5c6e0d5cee5f758341d0643c30 url: "https://pub.dev" source: hosted - version: "10.7.4" + version: "10.6.1" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "72977325a72af5ebb8e53b5c5533cb2e33eec481cd46210cfe5427f5efba55d8" + sha256: fc7c032f769e2c37bc7aa84eb44b02d17d53de8c256882072dbedbf1def158b8 url: "https://pub.dev" source: hosted - version: "3.8.4" + version: "3.7.3" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "8b9710be7e292e2a5ad34fff449d4b668c5808fb339649e69181727a4534f579" + sha256: "219e5995e79aff8deb3d633041dbdb8f704c1f5a044e970eed141ad851caf9d1" url: "https://pub.dev" source: hosted - version: "0.5.5+11" + version: "0.5.5+3" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: a87cfdd16b2bd04ca4a61e8b8d95daafaae487aa4c6533c9497199d631eefe6f + sha256: "46129e2733336ac77377174c3ca33a68cca2cd5848504aad63028aeb92afb7b2" url: "https://pub.dev" source: hosted - version: "4.15.2" + version: "4.11.1" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "4ca90755469fd20bf32070a3ff4dcb6256cfafc47230cc021261acced672ed3c" + sha256: b89936896b2cc02496b97e486793fd4bcf8c51beb99d6a7223c0eea2352d404e url: "https://pub.dev" source: hosted - version: "7.0.8" + version: "7.0.1" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "8eb2b4d35ed46405783c135cdf7c4c2d2769abe09cee60fcd8080c0475a9e8b5" + sha256: "88b7655c9394d723e121fd53f115d876c32260b8c8b499bdc3108341044a8306" url: "https://pub.dev" source: hosted - version: "5.8.11" + version: "5.8.4" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "96607c0e829a581c2a483c658f04e8b159964c3bae2730f73297070bc85d40bb" + sha256: "57bba167105d2315d243a4524939406df688f38a5b6d7a4159382bbbe43cdd00" url: "https://pub.dev" source: hosted - version: "2.24.2" + version: "2.19.0" firebase_core_platform_interface: dependency: transitive description: @@ -365,50 +365,50 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: d585bdf3c656c3f7821ba1bd44da5f13365d22fcecaf5eb75c4295246aaa83c0 + sha256: "0631a2ec971dbc540275e2fa00c3a8a2676f0a7adbc3c197d6fba569db689d97" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.8.1" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "5ccdf05de039f9544d0ba41c5ae2052ca2425985d32229911b09f69981164518" + sha256: c58902959e7a73aadcf82c87e8d64f6eb10b5287fd7aaadfab5c2ef34ec11ff5 url: "https://pub.dev" source: hosted - version: "3.4.8" + version: "3.4.1" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "359197344def001589c84f8d1d57c05f6e2e773f559205610ce58c25e2045a57" + sha256: "9d2f76bda1542615f75fb501a8c7afc6288d69c3728fdc762bf3d51f0ee0f4cb" url: "https://pub.dev" source: hosted - version: "3.6.16" + version: "3.6.9" firebase_database: dependency: "direct main" description: name: firebase_database - sha256: "80f67c961ed81341ac93120750120469ac3122baa8c0d3374a620f5834b5519f" + sha256: b7e4ab1bc08353adb645aa3b86ceed3e018eea33451732376e751479d13c8e2f url: "https://pub.dev" source: hosted - version: "10.3.8" + version: "10.3.1" firebase_database_platform_interface: dependency: transitive description: name: firebase_database_platform_interface - sha256: "4366ade2390f8799a317bb13af29c2a1fdfc84f4d04372094756b86a6cbfd305" + sha256: "69e696ae07ba1f3a9f6e6cda2ee3b78c7426cddf31afd2029e0d594926e95563" url: "https://pub.dev" source: hosted - version: "0.2.5+16" + version: "0.2.5+9" firebase_database_web: dependency: transitive description: name: firebase_database_web - sha256: "4920a83b917493b37fd408cbb01c289ef8a422d9ed48982f908a9850290262f9" + sha256: "77899e5a4dc10b5e5a1fe2ff7840143835644351adff9b64bbf9d48c496ea3a7" url: "https://pub.dev" source: hosted - version: "0.2.3+16" + version: "0.2.3+9" fixnum: dependency: transitive description: @@ -442,10 +442,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 + sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -636,10 +636,10 @@ packages: dependency: "direct main" description: name: multi_dropdown - sha256: "85647129b9ee7aedf7db90165c54403879c7ca1876b827c3c042c6631e55bd11" + sha256: "43f580a3a641462d6962c1dd4bccede40884d9210519b6f8809337e39b62bc2f" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" nested: dependency: transitive description: @@ -740,10 +740,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.6" pointycastle: dependency: transitive description: @@ -764,10 +764,10 @@ packages: dependency: transitive description: name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.0.5" pub_semver: dependency: transitive description: From 14ff8d82f94051f30eb6d6ee6b6f41f543463671 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:02:50 +0200 Subject: [PATCH 383/475] Add new loader --- .../presentation/widgets/init_firebase_widget.dart | 4 ++-- .../firebase/presentation/widgets/loaders/loader.dart | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 lib/features/firebase/presentation/widgets/loaders/loader.dart diff --git a/lib/features/firebase/presentation/widgets/init_firebase_widget.dart b/lib/features/firebase/presentation/widgets/init_firebase_widget.dart index c3f4156..abf18ea 100644 --- a/lib/features/firebase/presentation/widgets/init_firebase_widget.dart +++ b/lib/features/firebase/presentation/widgets/init_firebase_widget.dart @@ -1,6 +1,6 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; -import 'package:poetlum/features/firebase/presentation/widgets/loaders/loader.dart'; +import 'package:poetlum/features/application/presentation/widgets/loader.dart'; class InitFirebaseWidget extends StatefulWidget { const InitFirebaseWidget({ @@ -34,7 +34,7 @@ class _InitFirebaseWidgetState extends State { } else{ return MaterialApp( home: Scaffold( - body: widget.loader ?? const Loader(), + body: widget.loader ?? const Loader(text: 'Initializing Firebase'), ), ); } diff --git a/lib/features/firebase/presentation/widgets/loaders/loader.dart b/lib/features/firebase/presentation/widgets/loaders/loader.dart deleted file mode 100644 index 19a0a58..0000000 --- a/lib/features/firebase/presentation/widgets/loaders/loader.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter/material.dart'; - -class Loader extends StatelessWidget { - const Loader({super.key}); - - @override - Widget build(BuildContext context) => const Center(child: CircularProgressIndicator()); -} From 2efd58f14906e650101529860e9cd8aed2c6b283 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:35:04 +0200 Subject: [PATCH 384/475] Add loader in the AuthWrapper --- lib/features/application/presentation/pages/auth_wrapper.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/application/presentation/pages/auth_wrapper.dart b/lib/features/application/presentation/pages/auth_wrapper.dart index 177b492..e6e4f00 100644 --- a/lib/features/application/presentation/pages/auth_wrapper.dart +++ b/lib/features/application/presentation/pages/auth_wrapper.dart @@ -2,6 +2,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/pages/screens_wrapper.dart'; +import 'package:poetlum/features/application/presentation/widgets/loader.dart'; import 'package:poetlum/features/authorization/domain/repository/auth_repository.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; @@ -25,7 +26,7 @@ class AuthWrapper extends StatelessWidget { return const Scaffold( body: Center( - child: CircularProgressIndicator(), + child: Loader(text: 'Logging in...'), ), ); }, From f4bacb3b62f14f0301e3b3a5d4c024853f2c528d Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:41:18 +0200 Subject: [PATCH 385/475] Add more padding --- lib/features/application/presentation/widgets/loader.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/application/presentation/widgets/loader.dart b/lib/features/application/presentation/widgets/loader.dart index e248922..e186558 100644 --- a/lib/features/application/presentation/widgets/loader.dart +++ b/lib/features/application/presentation/widgets/loader.dart @@ -25,7 +25,7 @@ class _Label extends StatelessWidget { @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(vertical: 20), + padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40), child: Text( text, style: const TextStyle( From 8592739e0ed1689bcf0e517b68104cf56d1c96a9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:41:32 +0200 Subject: [PATCH 386/475] Add PoemsFeedScreen loader --- .../poems_feed/presentation/screens/poems_feed_screen.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index 544e0ab..bc4bd23 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/features/application/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; @@ -12,7 +13,7 @@ class PoemsFeedScreen extends StatelessWidget { Widget build(BuildContext context) => BlocBuilder( builder: (context, state) { if(state is RemotePoemLoading){ - return const Center(child: CircularProgressIndicator(),); + return const Loader(text: 'Grabbing some amazing amazing poems to read 📚'); } if(state is RemotePoemError){ From f784f6d4177f2f8e7aa263790cf6df3f05fbe5dd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:46:51 +0200 Subject: [PATCH 387/475] Add loader in the SavedPoemsScreen --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 7edfe9d..2d7edd5 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/features/application/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; @@ -68,13 +69,13 @@ class _SavedPoemsScreenState extends State { }, builder: (context, state) { if (state.status == FirebaseDatabaseStatus.submitting) { - return const Center(child: CircularProgressIndicator()); + return const Loader(text: 'Hitching a ride to our super database 🚀'); } else { return FutureBuilder?>( future: collectionsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); + return const Loader(text: 'Snatching your poems from our top-secret database 🕵️‍♂️'); } if (snapshot.hasError) { From b9a7a2ed290a6bcdb3c79f1ed897f0da05267342 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:49:38 +0200 Subject: [PATCH 388/475] Add loader to the SavedCollectionViewPage --- .../presentation/pages/saved_collection_view_page.dart | 3 ++- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 29ffe21..913ca1c 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/features/application/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; @@ -76,7 +77,7 @@ class _SavedCollectionViewPageState extends State { }, builder: (context, state) { if (state.status == FirebaseDatabaseStatus.submitting) { - return const Center(child: CircularProgressIndicator()); + return const Loader(text: 'Snatching your poems from our top-secret database 🕵️‍♂️'); } else { return poemsInTheCollection.isEmpty ? const Padding( diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 2d7edd5..2b3bcff 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -75,7 +75,7 @@ class _SavedPoemsScreenState extends State { future: collectionsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const Loader(text: 'Snatching your poems from our top-secret database 🕵️‍♂️'); + return const Loader(text: 'Snatching your collections from our top-secret database 🕵️‍♂️'); } if (snapshot.hasError) { From f8367598655ce6e0cf0eb663b94b2e82bdd7e0c5 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:50:39 +0200 Subject: [PATCH 389/475] Rename file --- .../poems_feed/presentation/widgets/drawer/custom_drawer.dart | 2 +- .../{custom_search_buttond.dart => custom_search_button.dart} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/features/poems_feed/presentation/widgets/drawer/{custom_search_buttond.dart => custom_search_button.dart} (100%) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 586d5cd..58d07c7 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -9,7 +9,7 @@ import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_ import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_search_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; class CustomDrawer extends StatefulWidget { diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_search_button.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/drawer/custom_search_buttond.dart rename to lib/features/poems_feed/presentation/widgets/drawer/custom_search_button.dart From 9c1984794731610af7a228ba5b4e401cfee36d40 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:07:21 +0200 Subject: [PATCH 390/475] Fix the deletion of multiple poems at once --- .../remote/firebase_api_service.dart | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 0ef382e..af67ad5 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -198,6 +198,7 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @override Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}) async { final userRef = FirebaseDatabase.instance.ref(userId); + var poemDeleted = false; if (collectionName == null) { final poemsRef = userRef.child('poems'); @@ -206,14 +207,17 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { final snapshot = await poemQuery.get(); if (snapshot.exists) { final poems = snapshot.value as Map; - poems.forEach((key, value) { - final poemData = Map.from(value as Map); + for (final key in poems.keys) { + if (poemDeleted) break; + + final poemData = Map.from(poems[key] as Map); final poemModel = PoemModel.fromFirebase(poemData); if (_isPoemEqual(poemModel, poemToDelete)) { - poemsRef.child(key).remove(); + await poemsRef.child(key).remove(); + poemDeleted = true; } - }); + } } } else { final collectionsRef = userRef.child('collections'); @@ -224,8 +228,10 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { } final collections = collectionsSnapshot.value as Map; - + for (final key in collections.keys) { + if (poemDeleted) break; + final value = collections[key]; if (value['name'] == collectionName && value['poems'] != null) { final collectionPoemsRef = collectionsRef.child('$key/poems'); @@ -234,14 +240,20 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { if (collectionPoemsSnapshot.exists) { final collectionPoems = collectionPoemsSnapshot.value as List; final updatedPoems = collectionPoems.where((poemData) { - if (poemData == null) return false; + if (poemData == null || poemDeleted) return false; final poemModel = PoemModel.fromFirebase(Map.from(poemData as Map)); - - return !_isPoemEqual(poemModel, poemToDelete); + + if (_isPoemEqual(poemModel, poemToDelete)) { + poemDeleted = true; + return false; + } + return true; }).toList(); - await collectionPoemsRef.set(updatedPoems); + if (poemDeleted) { + await collectionPoemsRef.set(updatedPoems); + } } } } From 64a100cfab88e77b838cbe89f67e3bb17384e7f9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:02:19 +0200 Subject: [PATCH 391/475] Add userId logging --- .../presentation/pages/screens_wrapper.dart | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index 57d4e36..6c9a928 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -1,8 +1,11 @@ import 'package:animations/animations.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/saved_poems/presentation/screens/saved_poems_screen.dart'; @@ -28,18 +31,29 @@ class _ScreensWrapperState extends State { title: 'Poetlum', ), drawer: CustomDrawer(getIt()), - body: PageTransitionSwitcher( - child: screens[screenIndex], - transitionBuilder: ( - child, - primaryAnimation, - secondaryAnimation, - ) => - FadeThroughTransition( - animation: primaryAnimation, - secondaryAnimation: secondaryAnimation, - child: child, - ), + body: FutureBuilder( + future: FirebaseAnalytics.instance.setUserId( + id: getIt().getCurrentUser().userId, + ), + builder: (context, snapshot) { + if(snapshot.connectionState == ConnectionState.waiting){ + return const Loader(text: 'Setting up analytics 🔎'); + } else{ + return PageTransitionSwitcher( + child: screens[screenIndex], + transitionBuilder: ( + child, + primaryAnimation, + secondaryAnimation, + ) => + FadeThroughTransition( + animation: primaryAnimation, + secondaryAnimation: secondaryAnimation, + child: child, + ), + ); + } + }, ), bottomNavigationBar: GNav( onTabChange: (value) => setState(() => screenIndex = value), From 466596d6eb7b169670b0f64ff3e831827b48bac1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:16:44 +0200 Subject: [PATCH 392/475] Add FirebaseAnalyticsObserver --- lib/features/application/poetlum_app.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/features/application/poetlum_app.dart b/lib/features/application/poetlum_app.dart index c115b16..b041396 100644 --- a/lib/features/application/poetlum_app.dart +++ b/lib/features/application/poetlum_app.dart @@ -1,3 +1,4 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; @@ -23,6 +24,9 @@ class PoetlumApp extends StatelessWidget { title: 'Poetlum', theme: state.themeData, initialRoute: authWrapperPageConstant, + navigatorObservers: [ + FirebaseAnalyticsObserver(analytics: FirebaseAnalytics.instance), + ], routes: { authWrapperPageConstant:(_) => const AuthWrapper(), registerPageConstant: (_) => const RegistrationPage(), From 97abbc84ce441a403dd696cdd9cd71162f1e8d67 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:21:28 +0200 Subject: [PATCH 393/475] Create analytics constants --- lib/core/constants/analytics_constants.dart | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 lib/core/constants/analytics_constants.dart diff --git a/lib/core/constants/analytics_constants.dart b/lib/core/constants/analytics_constants.dart new file mode 100644 index 0000000..044ec85 --- /dev/null +++ b/lib/core/constants/analytics_constants.dart @@ -0,0 +1,2 @@ +const String theme = 'theme'; +const String color = 'color'; From 912060d5892bd0b93053d7b4b450e9cc3d1591da Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:21:36 +0200 Subject: [PATCH 394/475] Add color change logging --- .../presentation/widgets/color_option_button.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index b063bdc..64d9b20 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -1,5 +1,7 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/constants/analytics_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; @@ -30,6 +32,13 @@ class _ColorOptionButtonState extends State with TickerProvi child: GestureDetector( onTap: () async { playAnimation(); + + await FirebaseAnalytics.instance.logEvent( + name: theme, + parameters: { + color: '${widget.themeColor.red} ${widget.themeColor.green} ${widget.themeColor.blue}', + }, + ); await context.read().setThemeColor(themeColor: widget.themeColor, needSave: true); }, From 4bb90805784ec96f61d0c999b07adf1834e8c3e4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:25:25 +0200 Subject: [PATCH 395/475] Change log logic --- .../presentation/widgets/color_option_button.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index 64d9b20..40b38d7 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -33,11 +35,13 @@ class _ColorOptionButtonState extends State with TickerProvi onTap: () async { playAnimation(); - await FirebaseAnalytics.instance.logEvent( + unawaited( + FirebaseAnalytics.instance.logEvent( name: theme, - parameters: { - color: '${widget.themeColor.red} ${widget.themeColor.green} ${widget.themeColor.blue}', - }, + parameters: { + color: 'R: ${widget.themeColor.red}, G: ${widget.themeColor.green}, B: ${widget.themeColor.blue}', + }, + ), ); await context.read().setThemeColor(themeColor: widget.themeColor, needSave: true); From ca20d641ea3798fb2ec178b10d2029510291c235 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:33:33 +0200 Subject: [PATCH 396/475] Add auth logging --- lib/core/constants/analytics_constants.dart | 2 -- .../presentation/widgets/auth_button.dart | 13 +++++++++++++ .../presentation/widgets/color_option_button.dart | 5 ++--- 3 files changed, 15 insertions(+), 5 deletions(-) delete mode 100644 lib/core/constants/analytics_constants.dart diff --git a/lib/core/constants/analytics_constants.dart b/lib/core/constants/analytics_constants.dart deleted file mode 100644 index 044ec85..0000000 --- a/lib/core/constants/analytics_constants.dart +++ /dev/null @@ -1,2 +0,0 @@ -const String theme = 'theme'; -const String color = 'color'; diff --git a/lib/features/authorization/presentation/widgets/auth_button.dart b/lib/features/authorization/presentation/widgets/auth_button.dart index fa1582d..6268a52 100644 --- a/lib/features/authorization/presentation/widgets/auth_button.dart +++ b/lib/features/authorization/presentation/widgets/auth_button.dart @@ -1,3 +1,4 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -25,8 +26,20 @@ class AuthButton< listener: (__, registerState) { if (registerState.status == AuthStatus.success) { _showPositiveToast(successfulToastText); + FirebaseAnalytics.instance.logEvent( + name: 'auth', + parameters: { + 'status': 'success', + }, + ); navigateOnSuccess(); } else if (registerState.status == AuthStatus.error) { + FirebaseAnalytics.instance.logEvent( + name: 'auth', + parameters: { + 'status': 'failed', + }, + ); _showNegativeToast(registerState.errorMessage ?? 'Unknown error'); } }, diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index 40b38d7..b5bb95e 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/core/constants/analytics_constants.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; @@ -37,9 +36,9 @@ class _ColorOptionButtonState extends State with TickerProvi unawaited( FirebaseAnalytics.instance.logEvent( - name: theme, + name: 'theme', parameters: { - color: 'R: ${widget.themeColor.red}, G: ${widget.themeColor.green}, B: ${widget.themeColor.blue}', + 'color': 'R: ${widget.themeColor.red}, G: ${widget.themeColor.green}, B: ${widget.themeColor.blue}', }, ), ); From 9b6a76179878ea129632fd5d553b9b756c453408 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:29:21 +0200 Subject: [PATCH 397/475] Add ScreensWrapper logging --- .../presentation/pages/screens_wrapper.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index 6c9a928..d20577d 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -56,7 +56,16 @@ class _ScreensWrapperState extends State { }, ), bottomNavigationBar: GNav( - onTabChange: (value) => setState(() => screenIndex = value), + onTabChange: (value){ + FirebaseAnalytics.instance.logEvent( + name: 'screen_navigation', + parameters: { + 'screen_index': value, + }, + ); + + setState(() => screenIndex = value); + }, gap: 12, tabs: const [ GButton(icon: Icons.home_outlined, text: 'Menu'), From 1c415478f05bea6d20e3a0df378ab6f8b4e11878 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:29:31 +0200 Subject: [PATCH 398/475] Add refresh button logging --- .../presentation/widgets/app_bar/buttons/refresh_button.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart index 59f3d69..de344c2 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -1,3 +1,4 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; @@ -48,6 +49,10 @@ class _RefreshButtonState extends State with TickerProviderStateM ? null : (){ playAnimation(); + + FirebaseAnalytics.instance.logEvent( + name: 'refresh_poems', + ); BlocProvider.of(context).add(const GetInitialPoemsEvent()); }, From 5f7e85b7100bbe32a0e219b9bca40a6b81d416d6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:29:47 +0200 Subject: [PATCH 399/475] Add settings button logging --- .../widgets/app_bar/buttons/settings_button.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index cf172d1..297ec11 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -1,3 +1,4 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; @@ -18,6 +19,12 @@ class _SettingsButtonState extends State with TickerProviderStat @override void initState() { super.initState(); + FirebaseAnalytics.instance.logEvent( + name: 'theme', + parameters: { + 'opened': 'true', + }, + ); _startAnimations(); } From e1339069c9d1bcb3d576a10f494f0e0d9a4b2b41 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:29:55 +0200 Subject: [PATCH 400/475] Add drawer logging --- .../presentation/widgets/drawer/custom_drawer.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 58d07c7..c35fe06 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; @@ -116,6 +117,17 @@ class _CustomDrawerState extends State { positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomSearchButton( onPressed: () { + FirebaseAnalytics.instance.logEvent( + name: 'search_poem', + parameters: { + 'author': _authorController.text, + 'title': _titleController.text, + 'line_count': _numberOfLinesController.text, + 'poem_count': _resultCountController.text, + 'is_random': _isRandom.toString(), + }, + ); + BlocProvider.of(context).add( GetPoemsEvent( author: _authorController.text, From 7ee4573f675035dfdb834ebb8ed1a3ac464e831e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:03 +0200 Subject: [PATCH 401/475] Add save button logging --- .../widgets/poem_view/custom_save_button.dart | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart index 24bcd32..4286ce7 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:like_button/like_button.dart'; @@ -34,13 +37,25 @@ class _CustomSaveButtonState extends State { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else { - bool isLiked = snapshot.data ?? false; + final isLiked = snapshot.data ?? false; return LikeButton( isLiked: isLiked, size: 42, circleColor: CircleColor(start: Theme.of(context).colorScheme.primaryContainer, end: Theme.of(context).colorScheme.primary), bubblesColor: BubblesColor(dotPrimaryColor: Theme.of(context).colorScheme.primaryContainer, dotSecondaryColor: Theme.of(context).colorScheme.primary), onTap: (isLiked) async { + unawaited( + FirebaseAnalytics.instance.logEvent( + name: 'collection_save', + parameters: { + 'title': widget.poemEntity.title, + 'author': widget.poemEntity.author, + 'linecount': widget.poemEntity.linecount, + 'is_saved': (!isLiked).toString(), + }, + ), + ); + if(isLiked == false){ await context.read().savePoem( userId: getIt().getCurrentUser().userId!, From 048864e063263cd3e499e78c4eaf4581488b3c6f Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:11 +0200 Subject: [PATCH 402/475] Add share button logging --- .../widgets/poem_view/custom_share_button.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart index 6227f8a..9359f2c 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:share_plus/share_plus.dart'; @@ -12,6 +15,17 @@ class CustomShareButton extends StatelessWidget { alignment: Alignment.centerRight, child: IconButton( onPressed: () async { + unawaited( + FirebaseAnalytics.instance.logEvent( + name: 'share', + parameters: { + 'title': poemEntity.title, + 'author': poemEntity.author, + 'linecount': poemEntity.linecount, + }, + ), + ); + await Share.share('${poemEntity.title}\n\n${poemEntity.text}\n\nThis poem was written by: ${poemEntity.author}'); }, icon: const Icon(Icons.share), From 74fda9de634878534931852654e38831a471007a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:17 +0200 Subject: [PATCH 403/475] Add poem card logging --- .../presentation/widgets/poems_feed/poem_card.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart index 0e8042a..6987a97 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; @@ -46,7 +47,18 @@ class _PoemCardState extends State { @override Widget build(BuildContext context) => GestureDetector( - onTap: () => Navigator.pushNamed(context, poemViewPageConstant, arguments: widget.poemEntity), + onTap: () { + FirebaseAnalytics.instance.logEvent( + name: 'poem_card', + parameters: { + 'title': widget.poemEntity.title, + 'author': widget.poemEntity.author, + 'linecount': widget.poemEntity.linecount, + }, + ); + + Navigator.pushNamed(context, poemViewPageConstant, arguments: widget.poemEntity); + }, child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), From 2c5b1e8779a22992848120fcc7b16f7f951a4dc6 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:24 +0200 Subject: [PATCH 404/475] Add write poem page logging --- .../presentation/pages/write_poem_page.dart | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index e26baa7..9598d24 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -1,3 +1,4 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -30,6 +31,12 @@ class _WritePoemPageState extends State { @override void initState() { super.initState(); + FirebaseAnalytics.instance.logEvent( + name: 'write_poem', + parameters: { + 'opened': 'true', + }, + ); _startAnimations(); } @@ -55,8 +62,23 @@ class _WritePoemPageState extends State { Widget build(BuildContext context) => BlocConsumer( listener: (context, state) { if (state.status == FirebaseDatabaseStatus.success) { + FirebaseAnalytics.instance.logEvent( + name: 'write_poem', + parameters: { + 'success': 'true', + 'username': widget._userRepository.getCurrentUser().username!, + 'title': _nameController.text, + 'text': _contentController.text, + }, + ); _showPositiveToast('Your amazing poem has been saved! :D'); } else if (state.status == FirebaseDatabaseStatus.error) { + FirebaseAnalytics.instance.logEvent( + name: 'write_poem', + parameters: { + 'success': 'false', + }, + ); _showNegativeToast('An error occurred :('); } }, @@ -96,6 +118,13 @@ class _WritePoemPageState extends State { positionInitialValue: MediaQuery.of(context).size.width/3, child: FilledButton.tonal( onPressed: () { + FirebaseAnalytics.instance.logEvent( + name: 'write_poem', + parameters: { + 'button_pressed': 'true', + }, + ); + if (_formKey.currentState!.validate()) { context.read().savePoem( userId: widget._userRepository.getCurrentUser().userId!, From c40fe3cada1c83b4415ee1f4db2f3f10f0f966f2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:30 +0200 Subject: [PATCH 405/475] Add collection card logging --- .../presentation/widgets/collection_card.dart | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index c687b49..d78ef0c 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -1,3 +1,4 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; @@ -50,6 +51,15 @@ class _CollectionCardState extends State { ? DismissDirection.none : DismissDirection.horizontal, onDismissed: (direction) { + FirebaseAnalytics.instance.logEvent( + name: 'collection_card', + parameters: { + 'deleted': 'true', + 'name': widget.collection.name, + 'poems_count': widget.collection.poems?.length, + }, + ); + context.read().deleteCollection( userId: getIt().getCurrentUser().userId!, collectionName: widget.collection.name ?? '', @@ -57,7 +67,17 @@ class _CollectionCardState extends State { ); }, child: GestureDetector( - onTap: () => Navigator.pushNamed(context, savedCollectionViewConstant, arguments: widget.collection), + onTap: (){ + FirebaseAnalytics.instance.logEvent( + name: 'collection_card', + parameters: { + 'name': widget.collection.name, + 'poems_count': widget.collection.poems?.length, + }, + ); + + Navigator.pushNamed(context, savedCollectionViewConstant, arguments: widget.collection); + }, child: SizedBox( height: MediaQuery.of(context).size.height / 4, child: Card( From a3739f4b00deffd54646dcdc145c2a0598118357 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:40 +0200 Subject: [PATCH 406/475] Add saved poem card logging --- .../presentation/widgets/saved_poem_card.dart | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 8e19e56..437b2a7 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; @@ -48,6 +51,19 @@ class _SavedPoemCardState extends State { Widget build(BuildContext context) => Dismissible( key: UniqueKey(), onDismissed: (direction) async{ + unawaited( + FirebaseAnalytics.instance.logEvent( + name: 'saved_poem_card', + parameters: { + 'deleted': 'true', + 'author': widget.poemEntity.author, + 'title': widget.poemEntity.title, + 'line_count': widget.poemEntity.linecount, + 'collecton_name': widget.collectionEntity.name, + }, + ), + ); + if(widget.collectionEntity.isAllSavedPoems){ await context.read().deletePoemFromCollection( poemEntity: widget.poemEntity, @@ -62,7 +78,19 @@ class _SavedPoemCardState extends State { } }, child: GestureDetector( - onTap: () => Navigator.pushNamed(context, savedPoemViewConstant, arguments: widget.poemEntity), + onTap: (){ + FirebaseAnalytics.instance.logEvent( + name: 'saved_poem_card', + parameters: { + 'author': widget.poemEntity.author, + 'title': widget.poemEntity.title, + 'line_count': widget.poemEntity.linecount, + 'collecton_name': widget.collectionEntity.name, + }, + ); + + Navigator.pushNamed(context, savedPoemViewConstant, arguments: widget.poemEntity); + }, child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), From 80dc7e7d3441ecc49a9c0e8779ace3eb522eb5f9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:51 +0200 Subject: [PATCH 407/475] Add bottom sheets logging --- .../create_collection_bottom_sheet.dart | 59 +++++++++++++++++++ ...pdate_collection_bottom_sheet_content.dart | 30 ++++++++++ 2 files changed, 89 insertions(+) diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 17dfa4e..db2aa48 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -1,5 +1,8 @@ // ignore_for_file: use_build_context_synchronously +import 'dart:async'; + +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -35,6 +38,12 @@ class _CreateCollectionBottomSheetContentState extends State().createNewCollection( userId: getIt().getCurrentUser().userId!, collectionName: textController.text, @@ -195,6 +244,16 @@ class _CreateButtonWidget extends StatelessWidget { await _showPositiveToast('The collection has been successfully saved'); } else{ + unawaited( + FirebaseAnalytics.instance.logEvent( + name: 'create_collection', + parameters: { + 'success': 'false', + 'error': 'Collection exists', + }, + ), + ); + await _showNegativeToast('The collection with this name already exists'); } diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 8be8a40..a0c6cdf 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -34,6 +37,13 @@ class _UpdateCollectionBottomSheetContentState extends State().updatePoemsInCollection( userId: getIt().getCurrentUser().userId!, collectionName: collectionName, From cb0f86d7f24491764925eb93b3c1a501ec916c7c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:21:54 +0200 Subject: [PATCH 408/475] Convert numeric fields to string --- .../presentation/widgets/create_collection_bottom_sheet.dart | 2 +- .../widgets/update_collection_bottom_sheet_content.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index db2aa48..44ce520 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -229,7 +229,7 @@ class _CreateButtonWidget extends StatelessWidget { parameters: { 'success': 'true', 'collection_name': textController.text, - 'poems_count': selectController.selectedOptions.length, + 'poems_count': selectController.selectedOptions.length.toString(), }, ), ); diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index a0c6cdf..7276169 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -195,7 +195,7 @@ class _EditButtonWidget extends StatelessWidget { name: 'update_collection', parameters: { 'collection_name': collectionName, - 'updated_poems_count': selectController.selectedOptions.length, + 'updated_poems_count': selectController.selectedOptions.length.toString(), }, ), ); From 787206a84739681a38c245bf55e3a4c9319ab067 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:47:57 +0200 Subject: [PATCH 409/475] Change animation logic --- .../screens/saved_poems_screen.dart | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 2b3bcff..bf332b3 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -26,34 +26,10 @@ class SavedPoemsScreen extends StatefulWidget { class _SavedPoemsScreenState extends State { Future?>? collectionsFuture; - bool isButton1Animated = false; - bool isButton2Animated = false; - bool isTextAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 300); - @override void initState() { super.initState(); - collectionsFuture = initCollections(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isButton1Animated = val, - (val) => isButton2Animated = val, - (val) => isTextAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + collectionsFuture = initCollections();; } Future?> initCollections() async => context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); @@ -93,7 +69,7 @@ class _SavedPoemsScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ RightAnimation( - animationField: isButton2Animated, + animationField: true, positionInitialValue: MediaQuery.of(context).size.width/8, child: FilledButton( child: const Text('Create a collection'), @@ -113,7 +89,7 @@ class _SavedPoemsScreenState extends State { ), RightAnimation( - animationField: isButton1Animated, + animationField: true, positionInitialValue: MediaQuery.of(context).size.width/8, child: FilledButton.tonal( onPressed: () => Navigator.pushNamedAndRemoveUntil( @@ -129,9 +105,9 @@ class _SavedPoemsScreenState extends State { ), if (collections == null || collections!.isEmpty) TopAnimation( - animationField: isTextAnimated, + animationField: true, positionInitialValue: MediaQuery.of(context).size.width/8, - child: const Text("You haven't saved any poems yet. :(") , + child: const Text("You haven't saved any poems yet. 😔") , ), if (!(collections == null || collections!.isEmpty)) ListView.builder( From c5ec6d4b1eb42cff412e9946d435505c32a822dd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:02:23 +0200 Subject: [PATCH 410/475] Fix empty list issue --- .../saved_poems/presentation/screens/saved_poems_screen.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index bf332b3..eeca90a 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; @@ -78,7 +79,9 @@ class _SavedPoemsScreenState extends State { context: context, isScrollControlled: true, builder:(context) => CreateCollectionBottomSheetContent( - poems: collections?[0].poems, + poems: (collections == null || collections!.isEmpty) + ? [] + : collections![0].poems, ), ); From 824aeb0170869dd7234f23355cef2b41da826d76 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:03:51 +0200 Subject: [PATCH 411/475] Decrease animationDelay --- .../poems_feed/presentation/widgets/drawer/custom_drawer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index c35fe06..3d9497e 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -36,7 +36,7 @@ class _CustomDrawerState extends State { bool isResultCountAnimated = false; bool isCheckboxAnimated = false; bool isButtonAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 200); + final Duration animationDelay = const Duration(milliseconds: 125); @override void initState() { From 08102e573f7789e1c580f45fd8e5759677500d08 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:36:02 +0200 Subject: [PATCH 412/475] Decrease animationDelay --- .../presentation/widgets/app_bar/buttons/settings_button.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 297ec11..0a57250 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -95,7 +95,7 @@ class _BottomSheetContent extends StatefulWidget { class _BottomSheetContentState extends State<_BottomSheetContent> { bool isHeaderAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 200); + final Duration animationDelay = const Duration(milliseconds: 150); @override void initState() { From a46c943884b716cd97bb48a38a3098a2a5e8c36e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:38:47 +0200 Subject: [PATCH 413/475] Decrease delay even more! --- .../presentation/widgets/app_bar/buttons/settings_button.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 0a57250..f3c2505 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -95,7 +95,7 @@ class _BottomSheetContent extends StatefulWidget { class _BottomSheetContentState extends State<_BottomSheetContent> { bool isHeaderAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 150); + final Duration animationDelay = const Duration(milliseconds: 125); @override void initState() { From b11bcf9f12f1bce7669d7a3cfc847a8f60f8a1e4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:09:41 +0200 Subject: [PATCH 414/475] Fix typo --- .../poems_feed/presentation/screens/poems_feed_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index bc4bd23..5ea749c 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -13,7 +13,7 @@ class PoemsFeedScreen extends StatelessWidget { Widget build(BuildContext context) => BlocBuilder( builder: (context, state) { if(state is RemotePoemLoading){ - return const Loader(text: 'Grabbing some amazing amazing poems to read 📚'); + return const Loader(text: 'Grabbing some amazing poems to read 📚'); } if(state is RemotePoemError){ From 919a1b568fe8f293e0d860752c5f068918f7f4b8 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:13:39 +0200 Subject: [PATCH 415/475] Close Drawer on successful poems fetch --- .../widgets/drawer/custom_drawer.dart | 88 +++++++++++++------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 3d9497e..174c688 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -3,9 +3,11 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; +import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; @@ -112,32 +114,46 @@ class _CustomDrawerState extends State { ), const CustomSpacer(heightFactor: 0.04), - TopAnimation( - animationField: isButtonAnimated, - positionInitialValue: MediaQuery.of(context).size.height/14, - child: CustomSearchButton( - onPressed: () { - FirebaseAnalytics.instance.logEvent( - name: 'search_poem', - parameters: { - 'author': _authorController.text, - 'title': _titleController.text, - 'line_count': _numberOfLinesController.text, - 'poem_count': _resultCountController.text, - 'is_random': _isRandom.toString(), + BlocListener( + listener: (context, state) { + if (state is RemotePoemDone) { + Navigator.pop(context); + + _showPositiveToast('We have received wonderful poems 😉'); + } + if(state is RemotePoemError){ + _showNegativeToast('Failed to retrieve wonderful poems. An error occurred 😓'); + } + }, + child: BlocBuilder( + builder: (context, state) => TopAnimation( + animationField: isButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.height / 14, + child: CustomSearchButton( + onPressed: () { + FirebaseAnalytics.instance.logEvent( + name: 'search_poem', + parameters: { + 'author': _authorController.text, + 'title': _titleController.text, + 'line_count': _numberOfLinesController.text, + 'poem_count': _resultCountController.text, + 'is_random': _isRandom.toString(), + }, + ); + + BlocProvider.of(context).add( + GetPoemsEvent( + author: _authorController.text, + title: _titleController.text, + lineCount: _numberOfLinesController.text, + poemCount: _resultCountController.text, + isRandom: _isRandom ?? false, + ), + ); }, - ); - - BlocProvider.of(context).add( - GetPoemsEvent( - author: _authorController.text, - title: _titleController.text, - lineCount: _numberOfLinesController.text, - poemCount: _resultCountController.text, - isRandom: _isRandom ?? false, - ), - ); - }, + ), + ), ), ), ], @@ -148,4 +164,26 @@ class _CustomDrawerState extends State { ); void _toggleCheckbox(bool? value) => setState(() => _isRandom = value); + + Future _showPositiveToast(String text) async{ + await Fluttertoast.showToast( + msg: text, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + ); + } + + Future _showNegativeToast(String error) async{ + await Fluttertoast.showToast( + msg: error, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); + } } From 5eb73b8825462e883dd871fd5d1f7dd286a79c69 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:23:22 +0200 Subject: [PATCH 416/475] Increase defaultPoemsCount --- lib/core/constants/poems_constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/constants/poems_constants.dart b/lib/core/constants/poems_constants.dart index 25ff2ae..d67324a 100644 --- a/lib/core/constants/poems_constants.dart +++ b/lib/core/constants/poems_constants.dart @@ -1,2 +1,2 @@ const String poemApiBaseUrl = 'https://poetrydb.org/'; -const int defaultPoemsCount = 30; +const int defaultPoemsCount = 50; From a54c4a9cc5ea87728091d6e38fbe128f8477d75a Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:27:50 +0200 Subject: [PATCH 417/475] Remove FAB in "All saved poems" collection --- .../pages/saved_collection_view_page.dart | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 913ca1c..d34d674 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -48,27 +48,29 @@ class _SavedCollectionViewPageState extends State { return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), - floatingActionButton: FloatingActionButton( - tooltip: 'Edit a collection', - onPressed: () async { - final savedPoems = await context.read().getUserPoems( - getIt().getCurrentUser().userId!, - ); - - if (mounted){ - await showModalBottomSheet( - context: localContext, - isScrollControlled: true, - builder:(context) => UpdateCollectionBottomSheetContent( - collectionName: collectionEntity.name ?? '', - poemsInTheCollection: poemsInTheCollection, - allSavedPoems: savedPoems, - ), + floatingActionButton: collectionEntity.isAllSavedPoems + ? null + : FloatingActionButton( + tooltip: 'Edit a collection', + onPressed: () async { + final savedPoems = await context.read().getUserPoems( + getIt().getCurrentUser().userId!, ); - } - }, - child: const Icon(Icons.edit), - ), + + if (mounted){ + await showModalBottomSheet( + context: localContext, + isScrollControlled: true, + builder:(context) => UpdateCollectionBottomSheetContent( + collectionName: collectionEntity.name ?? '', + poemsInTheCollection: poemsInTheCollection, + allSavedPoems: savedPoems, + ), + ); + } + }, + child: const Icon(Icons.edit), + ), body: BlocConsumer( listener: (context, state) async { if (state.status == FirebaseDatabaseStatus.needsRefresh) { From 716d4b73c95775ba5fb0cfc0c36c0b2e6d8234be Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:51:49 +0200 Subject: [PATCH 418/475] Add collection name --- .../pages/saved_collection_view_page.dart | 136 ++++++++++++------ 1 file changed, 93 insertions(+), 43 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index d34d674..156adb3 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -5,6 +5,7 @@ import 'package:poetlum/features/application/presentation/widgets/app_bar/app_ba import 'package:poetlum/features/application/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; @@ -36,41 +37,37 @@ class _SavedCollectionViewPageState extends State { Future initPoems() async { poemsInTheCollection = await context.read().getPoemsInCollection( userId: getIt().getCurrentUser().userId!, - collectionName: collectionEntity.isAllSavedPoems - ? null - : collectionEntity.name, + collectionName: collectionEntity.isAllSavedPoems ? null : collectionEntity.name, ); } @override - Widget build(BuildContext context){ + Widget build(BuildContext context) { final localContext = context; return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), - floatingActionButton: collectionEntity.isAllSavedPoems - ? null - : FloatingActionButton( - tooltip: 'Edit a collection', - onPressed: () async { - final savedPoems = await context.read().getUserPoems( - getIt().getCurrentUser().userId!, - ); + floatingActionButton: collectionEntity.isAllSavedPoems ? null : FloatingActionButton( + tooltip: 'Edit a collection', + onPressed: () async { + final savedPoems = await context.read().getUserPoems( + getIt().getCurrentUser().userId!, + ); - if (mounted){ - await showModalBottomSheet( - context: localContext, - isScrollControlled: true, - builder:(context) => UpdateCollectionBottomSheetContent( - collectionName: collectionEntity.name ?? '', - poemsInTheCollection: poemsInTheCollection, - allSavedPoems: savedPoems, - ), - ); - } - }, - child: const Icon(Icons.edit), - ), + if (mounted){ + await showModalBottomSheet( + context: localContext, + isScrollControlled: true, + builder:(context) => UpdateCollectionBottomSheetContent( + collectionName: collectionEntity.name ?? '', + poemsInTheCollection: poemsInTheCollection, + allSavedPoems: savedPoems, + ), + ); + } + }, + child: const Icon(Icons.edit), + ), body: BlocConsumer( listener: (context, state) async { if (state.status == FirebaseDatabaseStatus.needsRefresh) { @@ -81,27 +78,80 @@ class _SavedCollectionViewPageState extends State { if (state.status == FirebaseDatabaseStatus.submitting) { return const Loader(text: 'Snatching your poems from our top-secret database 🕵️‍♂️'); } else { - return poemsInTheCollection.isEmpty - ? const Padding( - padding: EdgeInsets.symmetric(horizontal: 15), - child: Center( - child: Text( - 'Nothing to show here 😔\nTap on the "Edit" button to add amazing poems to the collection', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 18), - ), + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _CollectionName(name: collectionEntity.name), + Expanded( + child: poemsInTheCollection.isEmpty + ? const Center( + child: Text( + 'Nothing to show here 😔\nTap on the "Edit" button to add amazing poems to the collection', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ) + : ListView.builder( + itemCount: poemsInTheCollection.length, + itemBuilder: (__, index) => SavedPoemCard( + poemEntity: poemsInTheCollection[index], + collectionEntity: collectionEntity, + ), + ), ), - ) - : ListView.builder( - itemCount: poemsInTheCollection.length, - itemBuilder: (__, index) => SavedPoemCard( - poemEntity: poemsInTheCollection[index], - collectionEntity: collectionEntity, - ), - ); + ], + ); } }, ), ); } } + +class _CollectionName extends StatefulWidget { + const _CollectionName({required this.name}); + + final String? name; + + + @override + State<_CollectionName> createState() => __CollectionNameState(); +} + +class __CollectionNameState extends State<_CollectionName> { + bool isNameAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + void _startAnimations() { + final setters = [ + (val) => isNameAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + + @override + void initState() { + super.initState(); + _startAnimations(); + } + + @override + Widget build(BuildContext context) => TopAnimation( + animationField: isNameAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + child: Align( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Text( + widget.name ?? 'Collection', + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + ), + ), + ); +} From 2419c9481c91b2620811e5a1e4e65f304cb36f71 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 14:14:05 +0200 Subject: [PATCH 419/475] Fix logging --- .../saved_poems/presentation/widgets/collection_card.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index d78ef0c..03221ce 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -56,7 +56,7 @@ class _CollectionCardState extends State { parameters: { 'deleted': 'true', 'name': widget.collection.name, - 'poems_count': widget.collection.poems?.length, + 'poems_count': widget.collection.poems?.length.toString(), }, ); @@ -72,7 +72,7 @@ class _CollectionCardState extends State { name: 'collection_card', parameters: { 'name': widget.collection.name, - 'poems_count': widget.collection.poems?.length, + 'poems_count': widget.collection.poems?.length.toString(), }, ); From ddfeb7ebb5d54af966866546dc58c4b7f3354d19 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 14:50:41 +0200 Subject: [PATCH 420/475] Fix logging --- .../saved_poems/presentation/widgets/collection_card.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 03221ce..53f57df 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -56,7 +56,9 @@ class _CollectionCardState extends State { parameters: { 'deleted': 'true', 'name': widget.collection.name, - 'poems_count': widget.collection.poems?.length.toString(), + 'poems_count': widget.collection.poems == null + ? '' + : widget.collection.poems!.length.toString(), }, ); @@ -72,7 +74,9 @@ class _CollectionCardState extends State { name: 'collection_card', parameters: { 'name': widget.collection.name, - 'poems_count': widget.collection.poems?.length.toString(), + 'poems_count': widget.collection.poems == null + ? '' + : widget.collection.poems!.length.toString(), }, ); From 3455537fa4fdb8ae7062399304b9c21f2fc9e20b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:20:32 +0200 Subject: [PATCH 421/475] Add check if poem with provided name exists --- lib/core/dependency_injection.dart | 3 + .../remote/firebase_api_service.dart | 16 +++++ .../firebase_db_repository_impl.dart | 3 + .../repository/firebase_db_repository.dart | 1 + .../is_poem_exists_by_name_params.dart | 6 ++ .../is_poem_exists_by_name_usecase.dart | 20 ++++++ .../bloc/firebase_database_cubit.dart | 27 +++++++- .../presentation/pages/write_poem_page.dart | 67 +++++++++---------- 8 files changed, 106 insertions(+), 37 deletions(-) create mode 100644 lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 5f6112c..af2879a 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -31,6 +31,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collection import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; @@ -76,6 +77,7 @@ void initializeDependencies() { ..registerSingleton(SavePoemUseCase(getIt())) ..registerSingleton(DeletePoemUseCase(getIt())) ..registerSingleton(IsPoemExistsUseCase(getIt())) + ..registerSingleton(IsPoemExistsByNameUseCase(getIt())) ..registerSingleton(CreateNewCollectionUseCase(getIt())) ..registerSingleton(DeleteCollectionUseCase(getIt())) ..registerSingleton(DeletePoemFromCollectionUseCase(getIt())) @@ -107,6 +109,7 @@ void initializeDependencies() { getIt(), getIt(), getIt(), + getIt(), ), ) ..registerFactory(() => RegisterFormValidationCubit( diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index af67ad5..fedea1c 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -11,6 +11,7 @@ abstract class FirebaseDatabaseService{ Future savePoem({required String userId, required PoemEntity poemEntity}); Future deletePoem({required PoemEntity poemEntity, required String userId, required String? collectionName}); Future isPoemExists({required PoemEntity poemEntity, required String userId}); + Future isPoemExistsByName({required String poemTitle, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); @@ -359,4 +360,19 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { return false; } + + @override + Future isPoemExistsByName({required String poemTitle, required String userId}) async{ + final userRef = FirebaseDatabase.instance.ref(userId); + final poemsRef = userRef.child('poems'); + final poemQuery = poemsRef.orderByChild('title').equalTo(poemTitle); + + final snapshot = await poemQuery.get(); + + if (snapshot.exists) { + return snapshot.value != null; + } + + return false; + } } diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 18e2b0b..45d0708 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -40,4 +40,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future isCollectionExists({required String userId, required String collectionName}) => _databaseService.isCollectionExists(userId: userId, collectionName: collectionName); + + @override + Future isPoemExistsByName({required String poemTitle, required String userId}) => _databaseService.isPoemExistsByName(poemTitle: poemTitle, userId: userId); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index e41bcba..99c9b84 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -7,6 +7,7 @@ abstract class FirebaseDatabaseRepository { Future savePoem({required String userId, required PoemEntity poemEntity}); Future deletePoem({required PoemEntity poemEntity, required String userId, String? collectionName}); Future isPoemExists({required PoemEntity poemEntity, required String userId}); + Future isPoemExistsByName({required String poemTitle, required String userId}); Future createNewCollection({required String userId, required String collectionName, required List poems}); Future deleteCollection({required String userId, required String collectionName, required List poems}); Future deletePoemFromCollection({required String userId, String? collectionName, required PoemEntity poemToDelete}); diff --git a/lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_params.dart b/lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_params.dart new file mode 100644 index 0000000..922a84d --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_params.dart @@ -0,0 +1,6 @@ +class IsPoemExistsByNameParams { + IsPoemExistsByNameParams({required this.poemTitle, required this.userId}); + + final String poemTitle; + final String userId; +} diff --git a/lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart b/lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart new file mode 100644 index 0000000..f237f27 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart @@ -0,0 +1,20 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_params.dart'; + +class IsPoemExistsByNameUseCase implements UseCase{ + IsPoemExistsByNameUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({IsPoemExistsByNameParams? params}) async { + if(params != null){ + final result = await _databaseRepository.isPoemExistsByName(poemTitle: params.poemTitle, userId: params.userId); + + return result; + } + + return false; + } +} diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index bfd4bd8..f2acf66 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -18,6 +18,8 @@ import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exist import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart'; @@ -25,7 +27,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_col import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase, this._isCollectionExistsUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase, this._isCollectionExistsUseCase, this._isPoemExistsByNameUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -38,6 +40,7 @@ class FirebaseDatabaseCubit extends Cubit { final UpdatePoemsInCollectionUseCase _updatePoemsInCollectionUseCase; final GetPoemsInCollectionUseCase _getPoemsInCollectionUseCase; final IsCollectionExistsUseCase _isCollectionExistsUseCase; + final IsPoemExistsByNameUseCase _isPoemExistsByNameUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -131,6 +134,28 @@ class FirebaseDatabaseCubit extends Cubit { } } + Future isPoemExistsByName({required String poemTitle, required String userId, String? collectionName}) async{ + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + + try{ + final isExists = await _isPoemExistsByNameUseCase( + params: IsPoemExistsByNameParams(poemTitle: poemTitle, userId: userId), + ); + + if(isExists){ + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } else{ + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + } + + return isExists; + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + + return false; + } + } + Future createNewCollection({required String userId, required String collectionName, required List poems}) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 9598d24..407c124 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -1,3 +1,7 @@ +// ignore_for_file: use_build_context_synchronously + +import 'dart:async'; + import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -59,29 +63,7 @@ class _WritePoemPageState extends State { } @override - Widget build(BuildContext context) => BlocConsumer( - listener: (context, state) { - if (state.status == FirebaseDatabaseStatus.success) { - FirebaseAnalytics.instance.logEvent( - name: 'write_poem', - parameters: { - 'success': 'true', - 'username': widget._userRepository.getCurrentUser().username!, - 'title': _nameController.text, - 'text': _contentController.text, - }, - ); - _showPositiveToast('Your amazing poem has been saved! :D'); - } else if (state.status == FirebaseDatabaseStatus.error) { - FirebaseAnalytics.instance.logEvent( - name: 'write_poem', - parameters: { - 'success': 'false', - }, - ); - _showNegativeToast('An error occurred :('); - } - }, + Widget build(BuildContext context) => BlocBuilder( builder: (context, state) => Scaffold( appBar: CustomAppBar( title: 'Poetlum', @@ -117,21 +99,34 @@ class _WritePoemPageState extends State { animationField: isButtonAnimated, positionInitialValue: MediaQuery.of(context).size.width/3, child: FilledButton.tonal( - onPressed: () { - FirebaseAnalytics.instance.logEvent( - name: 'write_poem', - parameters: { - 'button_pressed': 'true', - }, + onPressed: () async { + unawaited( + FirebaseAnalytics.instance.logEvent( + name: 'write_poem', + parameters: { + 'button_pressed': 'true', + }, + ), + ); + + final isPoemExists = await context.read().isPoemExistsByName( + poemTitle: _nameController.text, + userId: widget._userRepository.getCurrentUser().userId!, ); - if (_formKey.currentState!.validate()) { - context.read().savePoem( - userId: widget._userRepository.getCurrentUser().userId!, - username: widget._userRepository.getCurrentUser().username!, - title: _nameController.text, - text: _contentController.text, - ); + if(isPoemExists == false){ + if (_formKey.currentState!.validate()) { + await context.read().savePoem( + userId: widget._userRepository.getCurrentUser().userId!, + username: widget._userRepository.getCurrentUser().username!, + title: _nameController.text, + text: _contentController.text, + ); + } + + await _showPositiveToast('Your amazing poem has been saved! :D'); + } else{ + await _showNegativeToast('A poem with the stunning name is already in your saved poems. Please try another name 📝'); } }, child: const Padding( From 85b6afef5c7256fe75d2b3dcae7a5f00b4959209 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:23:04 +0200 Subject: [PATCH 422/475] Add logging --- .../presentation/pages/write_poem_page.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 407c124..6b69ed6 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -115,6 +115,15 @@ class _WritePoemPageState extends State { ); if(isPoemExists == false){ + unawaited( + FirebaseAnalytics.instance.logEvent( + name: 'write_poem', + parameters: { + 'success': 'true', + }, + ), + ); + if (_formKey.currentState!.validate()) { await context.read().savePoem( userId: widget._userRepository.getCurrentUser().userId!, @@ -126,6 +135,15 @@ class _WritePoemPageState extends State { await _showPositiveToast('Your amazing poem has been saved! :D'); } else{ + unawaited( + FirebaseAnalytics.instance.logEvent( + name: 'write_poem', + parameters: { + 'success': 'false', + }, + ), + ); + await _showNegativeToast('A poem with the stunning name is already in your saved poems. Please try another name 📝'); } }, From 4514340bed1141a597fd831623a834bcd45b1373 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:35:36 +0200 Subject: [PATCH 423/475] Fix mounted issue --- .../widgets/create_collection_bottom_sheet.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 44ce520..0e39604 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -66,9 +66,11 @@ class _CreateCollectionBottomSheetContentState extends State setState(() => setters[i](true)), - ); + Future.delayed(animationDelay * (i + 1)).then((_) { + if (mounted) { + setState(() => setters[i](true)); + } + }); } } From 4756e5e92401938a53de3b037390fa5a607f9204 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:36:57 +0200 Subject: [PATCH 424/475] Remove unused mounted checks --- .../screens/saved_poems_screen.dart | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index eeca90a..c239a4a 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -34,6 +34,13 @@ class _SavedPoemsScreenState extends State { } Future?> initCollections() async => context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); + Future refreshCollections() async { + final newCollections = await context.read().getUserCollections(getIt().getCurrentUser().userId!); + + setState(() { + collectionsFuture = Future.value(newCollections); + }); + } @override Widget build(BuildContext context) => BlocConsumer( @@ -79,14 +86,13 @@ class _SavedPoemsScreenState extends State { context: context, isScrollControlled: true, builder:(context) => CreateCollectionBottomSheetContent( - poems: (collections == null || collections!.isEmpty) + poems: (collections == null || collections.isEmpty) ? [] - : collections![0].poems, + : collections[0].poems, ), ); - - collections = await context.read().getUserCollections(getIt().getCurrentUser().userId!); + await refreshCollections(); }, ), ), @@ -106,19 +112,19 @@ class _SavedPoemsScreenState extends State { ], ), ), - if (collections == null || collections!.isEmpty) + if (collections == null || collections.isEmpty) TopAnimation( animationField: true, positionInitialValue: MediaQuery.of(context).size.width/8, child: const Text("You haven't saved any poems yet. 😔") , ), - if (!(collections == null || collections!.isEmpty)) + if (!(collections == null || collections.isEmpty)) ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: collections!.length, + itemCount: collections.length, itemBuilder: (context, index) => CollectionCard( - collection: collections![index], + collection: collections[index], ), ), ], @@ -130,4 +136,3 @@ class _SavedPoemsScreenState extends State { }, ); } - From 7c1c6c935a6027e9b23bb9c9e20537ac155172d9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 16 Dec 2023 15:31:40 +0200 Subject: [PATCH 425/475] Remove unused class --- lib/core/dependency_injection.dart | 6 +----- .../domain/entities/database_manager.dart | 20 ------------------- .../database_manager_interface.dart | 4 ---- 3 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 lib/features/realtime_database/domain/entities/database_manager.dart delete mode 100644 lib/features/realtime_database/domain/repository/database_manager_interface.dart diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index af2879a..5b3cd6c 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -18,7 +18,6 @@ import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.da import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; -import 'package:poetlum/features/realtime_database/domain/entities/database_manager.dart'; import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; import 'package:poetlum/features/saved_poems/data/repository/firebase_db_repository_impl.dart'; import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; @@ -45,11 +44,8 @@ import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cub GetIt getIt = GetIt.instance; void initializeDependencies() { - if(!GetIt.instance.isRegistered()){ + if(!GetIt.instance.isRegistered()){ getIt - // Database - ..registerSingleton(DatabaseManager()) - // Dio ..registerSingleton(Dio()) diff --git a/lib/features/realtime_database/domain/entities/database_manager.dart b/lib/features/realtime_database/domain/entities/database_manager.dart deleted file mode 100644 index ca0d74d..0000000 --- a/lib/features/realtime_database/domain/entities/database_manager.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:poetlum/features/realtime_database/data/data_sources/database.dart'; -import 'package:poetlum/features/realtime_database/domain/repository/database_manager_interface.dart'; - -class DatabaseManager implements IDatabaseManager{ - @override - Future read(String path) async { - final databaseReference = database.ref(path); - - final snapshot = await databaseReference.get(); - - return snapshot.exists - ? snapshot.value as Map - : null; - } - - @override - void write(Map data, String path) { - database.ref(path).set(data); - } -} diff --git a/lib/features/realtime_database/domain/repository/database_manager_interface.dart b/lib/features/realtime_database/domain/repository/database_manager_interface.dart deleted file mode 100644 index e84443e..0000000 --- a/lib/features/realtime_database/domain/repository/database_manager_interface.dart +++ /dev/null @@ -1,4 +0,0 @@ -abstract class IDatabaseManager{ - void write(Map data, String path); - Future read(String path); -} From 1e374c0a0f4bdf09220d39f24f638a1bf7a569fd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 16 Dec 2023 15:32:31 +0200 Subject: [PATCH 426/475] Remove keyboard on tap --- .../widgets/drawer/custom_drawer.dart | 179 +++++++++--------- 1 file changed, 91 insertions(+), 88 deletions(-) diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 174c688..3778f67 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -66,97 +66,100 @@ class _CustomDrawerState extends State { @override Widget build(BuildContext context) => Drawer( - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: SafeArea( - child: SingleChildScrollView( - child: Column( - children: [ - const CustomSpacer(heightFactor: 0.02), - TopAnimation( - animationField: isHeaderAnimated, - positionInitialValue: MediaQuery.of(context).size.height/14, - child: CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), - ), - - TopAnimation( - animationField: isAuthorAnimated, - positionInitialValue: MediaQuery.of(context).size.height/14, - child: CustomTextField(hintText: 'Author', controller: _authorController), - ), - const CustomSpacer(heightFactor: 0.04), - - TopAnimation( - animationField: isTitleAnimated, - positionInitialValue: MediaQuery.of(context).size.height/14, - child: CustomTextField(hintText: 'Title', controller: _titleController), - ), - const CustomSpacer(heightFactor: 0.04), - - TopAnimation( - animationField: isLinesNumberAnimated, - positionInitialValue: MediaQuery.of(context).size.height/14, - child: CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), - ), - const CustomSpacer(heightFactor: 0.04), - - TopAnimation( - animationField: isResultCountAnimated, - positionInitialValue: MediaQuery.of(context).size.height/14, - child: CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), - ), - const CustomSpacer(heightFactor: 0.04), - - TopAnimation( - animationField: isCheckboxAnimated, - positionInitialValue: MediaQuery.of(context).size.height/14, - child: CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), - ), - const CustomSpacer(heightFactor: 0.04), - - BlocListener( - listener: (context, state) { - if (state is RemotePoemDone) { - Navigator.pop(context); - - _showPositiveToast('We have received wonderful poems 😉'); - } - if(state is RemotePoemError){ - _showNegativeToast('Failed to retrieve wonderful poems. An error occurred 😓'); - } - }, - child: BlocBuilder( - builder: (context, state) => TopAnimation( - animationField: isButtonAnimated, - positionInitialValue: MediaQuery.of(context).size.height / 14, - child: CustomSearchButton( - onPressed: () { - FirebaseAnalytics.instance.logEvent( - name: 'search_poem', - parameters: { - 'author': _authorController.text, - 'title': _titleController.text, - 'line_count': _numberOfLinesController.text, - 'poem_count': _resultCountController.text, - 'is_random': _isRandom.toString(), - }, - ); - - BlocProvider.of(context).add( - GetPoemsEvent( - author: _authorController.text, - title: _titleController.text, - lineCount: _numberOfLinesController.text, - poemCount: _resultCountController.text, - isRandom: _isRandom ?? false, - ), - ); - }, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: SafeArea( + child: SingleChildScrollView( + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.02), + TopAnimation( + animationField: isHeaderAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + child: CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), + ), + + TopAnimation( + animationField: isAuthorAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + child: CustomTextField(hintText: 'Author', controller: _authorController), + ), + const CustomSpacer(heightFactor: 0.04), + + TopAnimation( + animationField: isTitleAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + child: CustomTextField(hintText: 'Title', controller: _titleController), + ), + const CustomSpacer(heightFactor: 0.04), + + TopAnimation( + animationField: isLinesNumberAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + child: CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), + ), + const CustomSpacer(heightFactor: 0.04), + + TopAnimation( + animationField: isResultCountAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + child: CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), + ), + const CustomSpacer(heightFactor: 0.04), + + TopAnimation( + animationField: isCheckboxAnimated, + positionInitialValue: MediaQuery.of(context).size.height/14, + child: CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), + ), + const CustomSpacer(heightFactor: 0.04), + + BlocListener( + listener: (context, state) { + if (state is RemotePoemDone) { + Navigator.pop(context); + + _showPositiveToast('We have received wonderful poems 😉'); + } + if(state is RemotePoemError){ + _showNegativeToast('Failed to retrieve wonderful poems. An error occurred 😓'); + } + }, + child: BlocBuilder( + builder: (context, state) => TopAnimation( + animationField: isButtonAnimated, + positionInitialValue: MediaQuery.of(context).size.height / 14, + child: CustomSearchButton( + onPressed: () { + FirebaseAnalytics.instance.logEvent( + name: 'search_poem', + parameters: { + 'author': _authorController.text, + 'title': _titleController.text, + 'line_count': _numberOfLinesController.text, + 'poem_count': _resultCountController.text, + 'is_random': _isRandom.toString(), + }, + ); + + BlocProvider.of(context).add( + GetPoemsEvent( + author: _authorController.text, + title: _titleController.text, + lineCount: _numberOfLinesController.text, + poemCount: _resultCountController.text, + isRandom: _isRandom ?? false, + ), + ); + }, + ), ), ), ), - ), - ], + ], + ), ), ), ), From bcd09141b75701c7d89d85ad1555c5c3b8e366eb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sat, 16 Dec 2023 15:35:41 +0200 Subject: [PATCH 427/475] Visual code refactor --- lib/core/constants/shared_preferences_constants.dart | 2 +- lib/core/usecases/usecase.dart | 2 ++ .../widgets/app_bar/buttons/refresh_button.dart | 2 ++ .../widgets/app_bar/buttons/settings_button.dart | 2 ++ .../presentation/bloc/validation/validators.dart | 2 ++ .../authorization/presentation/pages/login/login_page.dart | 2 ++ .../presentation/pages/registration/registration_page.dart | 2 ++ lib/features/poems_feed/domain/entities/firebase_user.dart | 2 +- .../poems_feed/domain/repository/user_repository.dart | 2 ++ .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 ++ .../poems_feed/presentation/screens/poems_feed_screen.dart | 2 +- .../poems_feed/presentation/widgets/custom_spacer.dart | 2 +- .../presentation/widgets/poem_view/poem_author.dart | 2 +- .../presentation/widgets/poem_view/poem_content.dart | 2 +- .../presentation/widgets/poem_view/poem_line_count.dart | 2 +- .../data/data_sources/remote/firebase_api_service.dart | 6 +++--- .../presentation/pages/saved_collection_view_page.dart | 2 ++ .../presentation/pages/saved_poem_view_page.dart | 2 ++ .../saved_poems/presentation/pages/write_poem_page.dart | 4 ++-- .../presentation/screens/saved_poems_screen.dart | 4 ++-- .../saved_poems/presentation/widgets/collection_card.dart | 4 +++- .../widgets/create_collection_bottom_sheet.dart | 2 +- .../saved_poems/presentation/widgets/saved_poem_card.dart | 2 ++ .../widgets/update_collection_bottom_sheet_content.dart | 6 ++++-- .../presentation/widgets/color_option_button.dart | 2 +- .../presentation/widgets/init_theme_widget.dart | 2 +- 26 files changed, 46 insertions(+), 20 deletions(-) diff --git a/lib/core/constants/shared_preferences_constants.dart b/lib/core/constants/shared_preferences_constants.dart index 02dfa27..f5d51e4 100644 --- a/lib/core/constants/shared_preferences_constants.dart +++ b/lib/core/constants/shared_preferences_constants.dart @@ -1 +1 @@ -const String savedColorConstant = 'saved_color'; \ No newline at end of file +const String savedColorConstant = 'saved_color'; diff --git a/lib/core/usecases/usecase.dart b/lib/core/usecases/usecase.dart index 531d58c..a1a901e 100644 --- a/lib/core/usecases/usecase.dart +++ b/lib/core/usecases/usecase.dart @@ -1,3 +1,5 @@ +// ignore_for_file: one_member_abstracts + abstract class UseCase{ Future call({Params params}); } diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart index de344c2..1e16c94 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index f3c2505..1bfef51 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; diff --git a/lib/features/authorization/presentation/bloc/validation/validators.dart b/lib/features/authorization/presentation/bloc/validation/validators.dart index e2332bf..ce1c7b6 100644 --- a/lib/features/authorization/presentation/bloc/validation/validators.dart +++ b/lib/features/authorization/presentation/bloc/validation/validators.dart @@ -1,3 +1,5 @@ +// ignore_for_file: one_member_abstracts, avoid_positional_boolean_parameters + import 'package:email_validator/email_validator.dart'; abstract class Validator { diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 1ad933e..50b06d9 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'dart:async'; import 'package:flutter/material.dart'; diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 48caa49..0685982 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'dart:async'; import 'package:flutter/material.dart'; diff --git a/lib/features/poems_feed/domain/entities/firebase_user.dart b/lib/features/poems_feed/domain/entities/firebase_user.dart index e4c701d..67abcf9 100644 --- a/lib/features/poems_feed/domain/entities/firebase_user.dart +++ b/lib/features/poems_feed/domain/entities/firebase_user.dart @@ -11,6 +11,6 @@ class FirebaseUserEntity extends Equatable{ List get props => [ username, email, - userId + userId, ]; } diff --git a/lib/features/poems_feed/domain/repository/user_repository.dart b/lib/features/poems_feed/domain/repository/user_repository.dart index ffaac64..b65841b 100644 --- a/lib/features/poems_feed/domain/repository/user_repository.dart +++ b/lib/features/poems_feed/domain/repository/user_repository.dart @@ -1,3 +1,5 @@ +// ignore_for_file: one_member_abstracts + import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; abstract class UserRepository{ diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 624179e..4b71e82 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index 5ea749c..5fc0cdb 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -56,4 +56,4 @@ class PoemsFeedScreen extends StatelessWidget { ], ), ); -} \ No newline at end of file +} diff --git a/lib/features/poems_feed/presentation/widgets/custom_spacer.dart b/lib/features/poems_feed/presentation/widgets/custom_spacer.dart index 5adf13a..f9d05b2 100644 --- a/lib/features/poems_feed/presentation/widgets/custom_spacer.dart +++ b/lib/features/poems_feed/presentation/widgets/custom_spacer.dart @@ -7,4 +7,4 @@ class CustomSpacer extends StatelessWidget { @override Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); -} \ No newline at end of file +} diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart b/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart index 2d98a36..c0ea61e 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart @@ -13,4 +13,4 @@ class PoemAuthor extends StatelessWidget { fontStyle: FontStyle.italic, ), ); -} \ No newline at end of file +} diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart b/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart index 2c98be4..80b68dd 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart @@ -12,4 +12,4 @@ class PoemContent extends StatelessWidget { fontSize: 16, ), ); -} \ No newline at end of file +} diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart b/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart index e88edb4..6b84677 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart @@ -15,4 +15,4 @@ class PoemLineCount extends StatelessWidget { ), ), ); -} \ No newline at end of file +} diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index fedea1c..3cfb238 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid_dynamic_calls, cascade_invocations +// ignore_for_file: avoid_dynamic_calls, cascade_invocations, prefer_final_in_for_each import 'package:firebase_database/firebase_database.dart'; import 'package:poetlum/features/poems_feed/data/models/poem.dart'; @@ -288,8 +288,8 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { 'author': poem.author, 'linecount': poem.linecount, 'text': poem.text, - 'title': poem.title - }).toList(); + 'title': poem.title, + },).toList(); await collectionsRef.child('$targetCollectionKey/poems').set(updatedPoemsData); } diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 156adb3..f4b6647 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 6e31d59..bb13a11 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:flutter/material.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 6b69ed6..6bfc697 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously +// ignore_for_file: use_build_context_synchronously, avoid_positional_boolean_parameters import 'dart:async'; @@ -230,4 +230,4 @@ class _CustomSpacer extends StatelessWidget { @override Widget build(BuildContext context) => SizedBox(height: MediaQuery.of(context).size.height * heightFactor); -} \ No newline at end of file +} diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index c239a4a..a59adc2 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously +// ignore_for_file: use_build_context_synchronously, prefer_final_locals import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -30,7 +30,7 @@ class _SavedPoemsScreenState extends State { @override void initState() { super.initState(); - collectionsFuture = initCollections();; + collectionsFuture = initCollections(); } Future?> initCollections() async => context.read().getUserCollections(widget._userRepository.getCurrentUser().userId!); diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 53f57df..9cd7b2b 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -156,4 +158,4 @@ class _EmptyCollectionText extends StatelessWidget { style: TextStyle(fontSize: 17), ), ); -} \ No newline at end of file +} diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 0e39604..66d03d7 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously +// ignore_for_file: use_build_context_synchronously, avoid_positional_boolean_parameters import 'dart:async'; diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 437b2a7..ad63d39 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 7276169..4798542 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_positional_boolean_parameters + import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; @@ -40,7 +42,7 @@ class _UpdateCollectionBottomSheetContentState extends State Date: Sun, 17 Dec 2023 11:09:06 +0200 Subject: [PATCH 428/475] Remove linter rule --- analysis_options.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index ed66032..e796f70 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -61,7 +61,6 @@ linter: - no_runtimeType_toString - noop_primitive_operations - omit_local_variable_types - - one_member_abstracts - parameter_assignments - prefer_asserts_in_initializer_lists - prefer_constructors_over_static_methods From 0c16bdbf88dca0fd3bc28364a9feaa5db6f69552 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:12:50 +0200 Subject: [PATCH 429/475] Move loader to the shared folder --- .../shared}/presentation/widgets/loader.dart | 0 lib/features/application/presentation/pages/auth_wrapper.dart | 2 +- .../application/presentation/pages/screens_wrapper.dart | 2 +- .../firebase/presentation/widgets/init_firebase_widget.dart | 2 +- .../poems_feed/presentation/screens/poems_feed_screen.dart | 2 +- .../presentation/pages/saved_collection_view_page.dart | 2 +- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename lib/{features/application => core/shared}/presentation/widgets/loader.dart (100%) diff --git a/lib/features/application/presentation/widgets/loader.dart b/lib/core/shared/presentation/widgets/loader.dart similarity index 100% rename from lib/features/application/presentation/widgets/loader.dart rename to lib/core/shared/presentation/widgets/loader.dart diff --git a/lib/features/application/presentation/pages/auth_wrapper.dart b/lib/features/application/presentation/pages/auth_wrapper.dart index e6e4f00..d96eb2f 100644 --- a/lib/features/application/presentation/pages/auth_wrapper.dart +++ b/lib/features/application/presentation/pages/auth_wrapper.dart @@ -2,7 +2,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/pages/screens_wrapper.dart'; -import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/authorization/domain/repository/auth_repository.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index d20577d..ed13131 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; -import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; diff --git a/lib/features/firebase/presentation/widgets/init_firebase_widget.dart b/lib/features/firebase/presentation/widgets/init_firebase_widget.dart index abf18ea..906c925 100644 --- a/lib/features/firebase/presentation/widgets/init_firebase_widget.dart +++ b/lib/features/firebase/presentation/widgets/init_firebase_widget.dart @@ -1,6 +1,6 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; class InitFirebaseWidget extends StatefulWidget { const InitFirebaseWidget({ diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index 5fc0cdb..acd092f 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index f4b6647..9df1bbd 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; -import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index a59adc2..22db175 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/application/presentation/widgets/loader.dart'; +import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; From e424bda78d1497a141647b32f1bb0cf8713db5f8 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:14:46 +0200 Subject: [PATCH 430/475] Move appbar --- .../app_bar => core/shared/presentation/widgets}/app_bar.dart | 0 lib/core/usecases/usecase.dart | 2 -- .../application/presentation/pages/screens_wrapper.dart | 2 +- .../authorization/presentation/bloc/validation/validators.dart | 2 +- lib/features/poems_feed/domain/repository/user_repository.dart | 2 -- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 +- .../presentation/pages/saved_collection_view_page.dart | 2 +- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 2 +- .../saved_poems/presentation/pages/write_poem_page.dart | 2 +- 9 files changed, 6 insertions(+), 10 deletions(-) rename lib/{features/application/presentation/widgets/app_bar => core/shared/presentation/widgets}/app_bar.dart (100%) diff --git a/lib/features/application/presentation/widgets/app_bar/app_bar.dart b/lib/core/shared/presentation/widgets/app_bar.dart similarity index 100% rename from lib/features/application/presentation/widgets/app_bar/app_bar.dart rename to lib/core/shared/presentation/widgets/app_bar.dart diff --git a/lib/core/usecases/usecase.dart b/lib/core/usecases/usecase.dart index a1a901e..531d58c 100644 --- a/lib/core/usecases/usecase.dart +++ b/lib/core/usecases/usecase.dart @@ -1,5 +1,3 @@ -// ignore_for_file: one_member_abstracts - abstract class UseCase{ Future call({Params params}); } diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index ed13131..050c504 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -3,7 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; diff --git a/lib/features/authorization/presentation/bloc/validation/validators.dart b/lib/features/authorization/presentation/bloc/validation/validators.dart index ce1c7b6..5ad4c39 100644 --- a/lib/features/authorization/presentation/bloc/validation/validators.dart +++ b/lib/features/authorization/presentation/bloc/validation/validators.dart @@ -1,4 +1,4 @@ -// ignore_for_file: one_member_abstracts, avoid_positional_boolean_parameters +// ignore_for_file: avoid_positional_boolean_parameters import 'package:email_validator/email_validator.dart'; diff --git a/lib/features/poems_feed/domain/repository/user_repository.dart b/lib/features/poems_feed/domain/repository/user_repository.dart index b65841b..ffaac64 100644 --- a/lib/features/poems_feed/domain/repository/user_repository.dart +++ b/lib/features/poems_feed/domain/repository/user_repository.dart @@ -1,5 +1,3 @@ -// ignore_for_file: one_member_abstracts - import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; abstract class UserRepository{ diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 4b71e82..fa0affd 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 9df1bbd..43dfaa6 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index bb13a11..6a98f1a 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 6bfc697..0b33450 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; From b00c6597defa1d56f212684c16cba1f937dd7d12 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:17:59 +0200 Subject: [PATCH 431/475] Move RotatingButtonMixin --- .../shared/presentation/widgets}/rotating_button_mixin.dart | 0 .../presentation/widgets/app_bar/buttons/refresh_button.dart | 2 +- .../presentation/widgets/app_bar/buttons/settings_button.dart | 2 +- .../theme_change/presentation/widgets/color_option_button.dart | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename lib/{features/application/presentation/widgets/app_bar/buttons => core/shared/presentation/widgets}/rotating_button_mixin.dart (100%) diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart b/lib/core/shared/presentation/widgets/rotating_button_mixin.dart similarity index 100% rename from lib/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart rename to lib/core/shared/presentation/widgets/rotating_button_mixin.dart diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart index 1e16c94..02c3ceb 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -3,7 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart index 1bfef51..23008d4 100644 --- a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart @@ -2,7 +2,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/animated_color_option_button.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/color_options.dart'; diff --git a/lib/features/theme_change/presentation/widgets/color_option_button.dart b/lib/features/theme_change/presentation/widgets/color_option_button.dart index 202f9cd..02b286a 100644 --- a/lib/features/theme_change/presentation/widgets/color_option_button.dart +++ b/lib/features/theme_change/presentation/widgets/color_option_button.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/rotating_button_mixin.dart'; +import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; class ColorOptionButton extends StatefulWidget { From 900590e1566d87b1b8689f5e5310fa59c6328b1b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:18:58 +0200 Subject: [PATCH 432/475] Move buttons --- .../shared/presentation/widgets/{ => app_bar}/app_bar.dart | 4 ++-- .../presentation/widgets/app_bar/buttons/refresh_button.dart | 0 .../presentation/widgets/app_bar/buttons/settings_button.dart | 0 .../application/presentation/pages/screens_wrapper.dart | 2 +- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 +- .../presentation/pages/saved_collection_view_page.dart | 2 +- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 2 +- .../saved_poems/presentation/pages/write_poem_page.dart | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) rename lib/core/shared/presentation/widgets/{ => app_bar}/app_bar.dart (74%) rename lib/{features/application => core/shared}/presentation/widgets/app_bar/buttons/refresh_button.dart (100%) rename lib/{features/application => core/shared}/presentation/widgets/app_bar/buttons/settings_button.dart (100%) diff --git a/lib/core/shared/presentation/widgets/app_bar.dart b/lib/core/shared/presentation/widgets/app_bar/app_bar.dart similarity index 74% rename from lib/core/shared/presentation/widgets/app_bar.dart rename to lib/core/shared/presentation/widgets/app_bar/app_bar.dart index 7992839..cf95505 100644 --- a/lib/core/shared/presentation/widgets/app_bar.dart +++ b/lib/core/shared/presentation/widgets/app_bar/app_bar.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart'; -import 'package:poetlum/features/application/presentation/widgets/app_bar/buttons/settings_button.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { const CustomAppBar({super.key, required this.title, this.leading}); diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart similarity index 100% rename from lib/features/application/presentation/widgets/app_bar/buttons/refresh_button.dart rename to lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart diff --git a/lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart similarity index 100% rename from lib/features/application/presentation/widgets/app_bar/buttons/settings_button.dart rename to lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index 050c504..af634e7 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -3,7 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index fa0affd..f46c46c 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 43dfaa6..c42a721 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 6a98f1a..a0a9489 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 0b33450..201459f 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; From 2ad1d01233b8238763a3aa67eda43540b70d1138 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:30:59 +0200 Subject: [PATCH 433/475] Move PoemModel --- lib/{features/poems_feed => core/shared}/data/models/poem.dart | 0 lib/features/application/presentation/pages/auth_wrapper.dart | 2 +- .../poems_feed/data/data_sources/remote/poem_api_service.dart | 2 +- .../poems_feed/data/repository/poem_repository_impl.dart | 2 +- .../data/data_sources/remote/firebase_api_service.dart | 2 +- lib/features/saved_poems/data/models/collection.dart | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename lib/{features/poems_feed => core/shared}/data/models/poem.dart (100%) diff --git a/lib/features/poems_feed/data/models/poem.dart b/lib/core/shared/data/models/poem.dart similarity index 100% rename from lib/features/poems_feed/data/models/poem.dart rename to lib/core/shared/data/models/poem.dart diff --git a/lib/features/application/presentation/pages/auth_wrapper.dart b/lib/features/application/presentation/pages/auth_wrapper.dart index d96eb2f..b77f0cf 100644 --- a/lib/features/application/presentation/pages/auth_wrapper.dart +++ b/lib/features/application/presentation/pages/auth_wrapper.dart @@ -1,8 +1,8 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/application/presentation/pages/screens_wrapper.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; +import 'package:poetlum/features/application/presentation/pages/screens_wrapper.dart'; import 'package:poetlum/features/authorization/domain/repository/auth_repository.dart'; import 'package:poetlum/features/authorization/presentation/pages/registration/registration_page.dart'; diff --git a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart index 82e8a4e..3df6533 100644 --- a/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart +++ b/lib/features/poems_feed/data/data_sources/remote/poem_api_service.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; import 'package:poetlum/core/constants/poems_constants.dart'; -import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/core/shared/data/models/poem.dart'; import 'package:retrofit/retrofit.dart'; part 'poem_api_service.g.dart'; diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index 478abdf..a68c928 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -4,7 +4,7 @@ import 'package:dio/dio.dart'; import 'package:poetlum/core/constants/poems_constants.dart'; import 'package:poetlum/core/resources/data_state.dart'; import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; -import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/core/shared/data/models/poem.dart'; import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; class PoemRepositoryImpl implements PoemRepository{ diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 3cfb238..4792f82 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_dynamic_calls, cascade_invocations, prefer_final_in_for_each import 'package:firebase_database/firebase_database.dart'; -import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/core/shared/data/models/poem.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/data/models/collection.dart'; diff --git a/lib/features/saved_poems/data/models/collection.dart b/lib/features/saved_poems/data/models/collection.dart index 4674480..8f147f1 100644 --- a/lib/features/saved_poems/data/models/collection.dart +++ b/lib/features/saved_poems/data/models/collection.dart @@ -1,4 +1,4 @@ -import 'package:poetlum/features/poems_feed/data/models/poem.dart'; +import 'package:poetlum/core/shared/data/models/poem.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; class CollectionModel extends CollectionEntity{ From 67a1a28450d4a0767acf89ae370fb2edd239aca7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:32:39 +0200 Subject: [PATCH 434/475] Move user repository --- lib/core/dependency_injection.dart | 4 ++-- .../shared}/data/repository/user_repository_impl.dart | 2 +- .../shared}/domain/repository/user_repository.dart | 0 .../application/presentation/pages/screens_wrapper.dart | 2 +- .../poems_feed/data/repository/poem_repository_impl.dart | 2 +- .../poems_feed/presentation/widgets/drawer/custom_drawer.dart | 2 +- .../presentation/widgets/poem_view/custom_save_button.dart | 2 +- .../presentation/pages/saved_collection_view_page.dart | 2 +- .../saved_poems/presentation/pages/write_poem_page.dart | 2 +- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- .../saved_poems/presentation/widgets/collection_card.dart | 2 +- .../presentation/widgets/create_collection_bottom_sheet.dart | 2 +- .../saved_poems/presentation/widgets/saved_poem_card.dart | 2 +- .../widgets/update_collection_bottom_sheet_content.dart | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) rename lib/{features/poems_feed => core/shared}/data/repository/user_repository_impl.dart (85%) rename lib/{features/poems_feed => core/shared}/domain/repository/user_repository.dart (100%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 5b3cd6c..6d9fa7a 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -13,9 +13,9 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; import 'package:poetlum/features/poems_feed/data/repository/poem_repository_impl.dart'; -import 'package:poetlum/features/poems_feed/data/repository/user_repository_impl.dart'; +import 'package:poetlum/core/shared/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; diff --git a/lib/features/poems_feed/data/repository/user_repository_impl.dart b/lib/core/shared/data/repository/user_repository_impl.dart similarity index 85% rename from lib/features/poems_feed/data/repository/user_repository_impl.dart rename to lib/core/shared/data/repository/user_repository_impl.dart index 2f6d05c..b424fa4 100644 --- a/lib/features/poems_feed/data/repository/user_repository_impl.dart +++ b/lib/core/shared/data/repository/user_repository_impl.dart @@ -1,6 +1,6 @@ import 'package:firebase_auth/firebase_auth.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; class UserRepositoryImpl implements UserRepository{ UserRepositoryImpl(this._firebaseAuth); diff --git a/lib/features/poems_feed/domain/repository/user_repository.dart b/lib/core/shared/domain/repository/user_repository.dart similarity index 100% rename from lib/features/poems_feed/domain/repository/user_repository.dart rename to lib/core/shared/domain/repository/user_repository.dart diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index af634e7..919b0df 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -5,7 +5,7 @@ import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/saved_poems/presentation/screens/saved_poems_screen.dart'; diff --git a/lib/features/poems_feed/data/repository/poem_repository_impl.dart b/lib/features/poems_feed/data/repository/poem_repository_impl.dart index a68c928..33ec8d7 100644 --- a/lib/features/poems_feed/data/repository/poem_repository_impl.dart +++ b/lib/features/poems_feed/data/repository/poem_repository_impl.dart @@ -3,8 +3,8 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:poetlum/core/constants/poems_constants.dart'; import 'package:poetlum/core/resources/data_state.dart'; -import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; import 'package:poetlum/core/shared/data/models/poem.dart'; +import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; class PoemRepositoryImpl implements PoemRepository{ diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 3778f67..4557fc9 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -4,7 +4,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart index 4286ce7..f3fe3cb 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart @@ -6,7 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:like_button/like_button.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class CustomSaveButton extends StatefulWidget { diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index c42a721..bba068b 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -6,7 +6,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 201459f..b3b1e89 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -8,7 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 22db175..89e79c5 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -6,7 +6,7 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 9cd7b2b..78370c9 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -6,7 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 66d03d7..c635691 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -9,7 +9,7 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index ad63d39..eae38a2 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -8,7 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 4798542..fde5348 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -9,7 +9,7 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; From 7ee4fcb2cf7bc3f4e828b646b2491ac3d9f861ad Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:34:40 +0200 Subject: [PATCH 435/475] Move animations --- .../presentation/widgets/animations/right_animation.dart | 0 .../presentation/widgets/animations/top_animation.dart | 0 .../presentation/widgets/app_bar/buttons/refresh_button.dart | 2 +- .../presentation/widgets/app_bar/buttons/settings_button.dart | 2 +- .../authorization/presentation/pages/login/login_page.dart | 2 +- .../presentation/pages/registration/registration_page.dart | 2 +- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 +- .../poems_feed/presentation/widgets/drawer/custom_drawer.dart | 2 +- .../poems_feed/presentation/widgets/poems_feed/poem_card.dart | 2 +- .../presentation/pages/saved_collection_view_page.dart | 2 +- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 2 +- .../saved_poems/presentation/pages/write_poem_page.dart | 2 +- .../saved_poems/presentation/screens/saved_poems_screen.dart | 4 ++-- .../saved_poems/presentation/widgets/collection_card.dart | 2 +- .../presentation/widgets/create_collection_bottom_sheet.dart | 2 +- .../saved_poems/presentation/widgets/saved_poem_card.dart | 2 +- .../widgets/update_collection_bottom_sheet_content.dart | 2 +- 17 files changed, 16 insertions(+), 16 deletions(-) rename lib/{features/poems_feed => core/shared}/presentation/widgets/animations/right_animation.dart (100%) rename lib/{features/poems_feed => core/shared}/presentation/widgets/animations/top_animation.dart (100%) diff --git a/lib/features/poems_feed/presentation/widgets/animations/right_animation.dart b/lib/core/shared/presentation/widgets/animations/right_animation.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/animations/right_animation.dart rename to lib/core/shared/presentation/widgets/animations/right_animation.dart diff --git a/lib/features/poems_feed/presentation/widgets/animations/top_animation.dart b/lib/core/shared/presentation/widgets/animations/top_animation.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/animations/top_animation.dart rename to lib/core/shared/presentation/widgets/animations/top_animation.dart diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart index 02c3ceb..c2ee9ce 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -7,7 +7,7 @@ import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.d import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; class RefreshButton extends StatefulWidget { const RefreshButton({super.key}); diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart index 23008d4..113cf25 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart @@ -3,7 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/animated_color_option_button.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/color_options.dart'; diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 50b06d9..ea5a990 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -12,7 +12,7 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; class _Header extends StatefulWidget { const _Header(); diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 0685982..c302e58 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -13,7 +13,7 @@ import 'package:poetlum/features/authorization/presentation/widgets/auth_button. import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; class _Header extends StatefulWidget { const _Header(); diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index f46c46c..128a9d2 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart index 4557fc9..18f6ba7 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart @@ -8,7 +8,7 @@ import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart index 6987a97..81d14fa 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart @@ -4,7 +4,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; class PoemCard extends StatefulWidget { const PoemCard({super.key, required this.poemEntity}); diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index bba068b..f49465f 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -7,7 +7,7 @@ import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index a0a9489..cf75364 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index b3b1e89..93fdc61 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -9,7 +9,7 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 89e79c5..c5a5171 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -7,8 +7,8 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 78370c9..2a45a35 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -7,7 +7,7 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index c635691..d4e634a 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -10,7 +10,7 @@ import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index eae38a2..ddc0233 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -9,7 +9,7 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index fde5348..868f6f1 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -10,7 +10,7 @@ import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; From e8bba20a3938a3123502805f132454900f782278 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:36:55 +0200 Subject: [PATCH 436/475] Move custom drawer --- .../presentation/pages/screens_wrapper.dart | 2 +- .../widgets/drawer/custom_checkbox_tile.dart | 0 .../presentation/widgets/drawer/custom_drawer.dart | 10 +++++----- .../presentation/widgets/drawer/custom_header.dart | 0 .../widgets/drawer/custom_search_button.dart | 0 .../presentation/widgets/drawer/custom_textfield.dart | 0 .../widgets/create_collection_bottom_sheet.dart | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename lib/features/{poems_feed => application}/presentation/widgets/drawer/custom_checkbox_tile.dart (100%) rename lib/features/{poems_feed => application}/presentation/widgets/drawer/custom_drawer.dart (95%) rename lib/features/{poems_feed => application}/presentation/widgets/drawer/custom_header.dart (100%) rename lib/features/{poems_feed => application}/presentation/widgets/drawer/custom_search_button.dart (100%) rename lib/features/{poems_feed => application}/presentation/widgets/drawer/custom_textfield.dart (100%) diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index 919b0df..623111c 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -7,7 +7,7 @@ import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart'; +import 'package:poetlum/features/application/presentation/widgets/drawer/custom_drawer.dart'; import 'package:poetlum/features/saved_poems/presentation/screens/saved_poems_screen.dart'; class ScreensWrapper extends StatefulWidget { diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart b/lib/features/application/presentation/widgets/drawer/custom_checkbox_tile.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart rename to lib/features/application/presentation/widgets/drawer/custom_checkbox_tile.dart diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart similarity index 95% rename from lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart rename to lib/features/application/presentation/widgets/drawer/custom_drawer.dart index 18f6ba7..4cf0fe6 100644 --- a/lib/features/poems_feed/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -5,15 +5,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/features/application/presentation/widgets/drawer/custom_checkbox_tile.dart'; +import 'package:poetlum/features/application/presentation/widgets/drawer/custom_header.dart'; +import 'package:poetlum/features/application/presentation/widgets/drawer/custom_search_button.dart'; +import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_checkbox_tile.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_header.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_search_button.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; class CustomDrawer extends StatefulWidget { const CustomDrawer(this._userRepository, {super.key}); diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart b/lib/features/application/presentation/widgets/drawer/custom_header.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/drawer/custom_header.dart rename to lib/features/application/presentation/widgets/drawer/custom_header.dart diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_search_button.dart b/lib/features/application/presentation/widgets/drawer/custom_search_button.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/drawer/custom_search_button.dart rename to lib/features/application/presentation/widgets/drawer/custom_search_button.dart diff --git a/lib/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart b/lib/features/application/presentation/widgets/drawer/custom_textfield.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart rename to lib/features/application/presentation/widgets/drawer/custom_textfield.dart diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index d4e634a..4fc60ba 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -12,7 +12,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/drawer/custom_textfield.dart'; +import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; From 7eea3d3c25c83e9a689c37fedda6e1bb867a230c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:38:00 +0200 Subject: [PATCH 437/475] Move custom spacer --- .../shared}/presentation/widgets/custom_spacer.dart | 0 .../application/presentation/widgets/drawer/custom_drawer.dart | 2 +- .../application/presentation/widgets/drawer/custom_header.dart | 2 +- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 +- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 2 +- .../presentation/widgets/create_collection_bottom_sheet.dart | 2 +- .../widgets/update_collection_bottom_sheet_content.dart | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename lib/{features/poems_feed => core/shared}/presentation/widgets/custom_spacer.dart (100%) diff --git a/lib/features/poems_feed/presentation/widgets/custom_spacer.dart b/lib/core/shared/presentation/widgets/custom_spacer.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/custom_spacer.dart rename to lib/core/shared/presentation/widgets/custom_spacer.dart diff --git a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart index 4cf0fe6..8225663 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -13,7 +13,7 @@ import 'package:poetlum/features/application/presentation/widgets/drawer/custom_ import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; class CustomDrawer extends StatefulWidget { const CustomDrawer(this._userRepository, {super.key}); diff --git a/lib/features/application/presentation/widgets/drawer/custom_header.dart b/lib/features/application/presentation/widgets/drawer/custom_header.dart index b20d48c..b95e733 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_header.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_header.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; class CustomDrawerHeader extends StatelessWidget { const CustomDrawerHeader({super.key, required this.user}); diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 128a9d2..e377e2c 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index cf75364..5b0e581 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 4fc60ba..1ecf8ab 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -11,7 +11,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 868f6f1..05a8921 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -11,7 +11,7 @@ import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; From ea3a28bde13992831a4447e31c4d89e09df15173 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:40:08 +0200 Subject: [PATCH 438/475] Move poem card --- .../shared/presentation/widgets}/poem_card.dart | 2 +- .../poems_feed/presentation/screens/poems_feed_screen.dart | 2 +- .../presentation/widgets/poem_view/custom_save_button.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename lib/{features/poems_feed/presentation/widgets/poems_feed => core/shared/presentation/widgets}/poem_card.dart (100%) diff --git a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart b/lib/core/shared/presentation/widgets/poem_card.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart rename to lib/core/shared/presentation/widgets/poem_card.dart index 81d14fa..d324514 100644 --- a/lib/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart +++ b/lib/core/shared/presentation/widgets/poem_card.dart @@ -3,8 +3,8 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class PoemCard extends StatefulWidget { const PoemCard({super.key, required this.poemEntity}); diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index acd092f..33df532 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -4,7 +4,7 @@ import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poems_feed/poem_card.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card.dart'; class PoemsFeedScreen extends StatelessWidget { const PoemsFeedScreen({super.key}); diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart index f3fe3cb..d7f32d2 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart @@ -5,8 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:like_button/like_button.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; class CustomSaveButton extends StatefulWidget { From 837f5de80557d105f29217bc3a01ce49a812f5b3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:42:00 +0200 Subject: [PATCH 439/475] Move poem card related items --- .../widgets/poem_card}/custom_share_button.dart | 0 .../presentation/widgets/poem_card}/poem_author.dart | 0 .../widgets/{ => poem_card}/poem_card.dart | 0 .../presentation/widgets/poem_card}/poem_content.dart | 0 .../widgets/poem_card}/poem_line_count.dart | 0 .../presentation/widgets/poem_card}/poem_title.dart | 0 .../presentation/pages/poem_view/poem_view.dart | 10 +++++----- .../presentation/screens/poems_feed_screen.dart | 2 +- .../presentation/pages/saved_poem_view_page.dart | 10 +++++----- 9 files changed, 11 insertions(+), 11 deletions(-) rename lib/{features/poems_feed/presentation/widgets/poem_view => core/shared/presentation/widgets/poem_card}/custom_share_button.dart (100%) rename lib/{features/poems_feed/presentation/widgets/poem_view => core/shared/presentation/widgets/poem_card}/poem_author.dart (100%) rename lib/core/shared/presentation/widgets/{ => poem_card}/poem_card.dart (100%) rename lib/{features/poems_feed/presentation/widgets/poem_view => core/shared/presentation/widgets/poem_card}/poem_content.dart (100%) rename lib/{features/poems_feed/presentation/widgets/poem_view => core/shared/presentation/widgets/poem_card}/poem_line_count.dart (100%) rename lib/{features/poems_feed/presentation/widgets/poem_view => core/shared/presentation/widgets/poem_card}/poem_title.dart (100%) diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart b/lib/core/shared/presentation/widgets/poem_card/custom_share_button.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart rename to lib/core/shared/presentation/widgets/poem_card/custom_share_button.dart diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart b/lib/core/shared/presentation/widgets/poem_card/poem_author.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poem_view/poem_author.dart rename to lib/core/shared/presentation/widgets/poem_card/poem_author.dart diff --git a/lib/core/shared/presentation/widgets/poem_card.dart b/lib/core/shared/presentation/widgets/poem_card/poem_card.dart similarity index 100% rename from lib/core/shared/presentation/widgets/poem_card.dart rename to lib/core/shared/presentation/widgets/poem_card/poem_card.dart diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart b/lib/core/shared/presentation/widgets/poem_card/poem_content.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poem_view/poem_content.dart rename to lib/core/shared/presentation/widgets/poem_card/poem_content.dart diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart b/lib/core/shared/presentation/widgets/poem_card/poem_line_count.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart rename to lib/core/shared/presentation/widgets/poem_card/poem_line_count.dart diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/poem_title.dart b/lib/core/shared/presentation/widgets/poem_card/poem_title.dart similarity index 100% rename from lib/features/poems_feed/presentation/widgets/poem_view/poem_title.dart rename to lib/core/shared/presentation/widgets/poem_card/poem_title.dart diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index e377e2c..441aaf9 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -6,11 +6,11 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/custom_share_button.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_author.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_content.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_line_count.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_title.dart'; class PoemViewPage extends StatefulWidget { const PoemViewPage({super.key}); diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index 33df532..35f21fe 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -4,7 +4,7 @@ import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/core/shared/presentation/widgets/poem_card.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_card.dart'; class PoemsFeedScreen extends StatelessWidget { const PoemsFeedScreen({super.key}); diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 5b0e581..e654f24 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -5,11 +5,11 @@ import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_share_button.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_author.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_content.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_line_count.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/poem_title.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/custom_share_button.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_author.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_content.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_line_count.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_title.dart'; class SavedPoemViewPage extends StatefulWidget { const SavedPoemViewPage({super.key}); From ee65959093c3200dcd5480c384f0debc0e88f207 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:42:54 +0200 Subject: [PATCH 440/475] Remove unused file --- lib/features/realtime_database/data/data_sources/database.dart | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 lib/features/realtime_database/data/data_sources/database.dart diff --git a/lib/features/realtime_database/data/data_sources/database.dart b/lib/features/realtime_database/data/data_sources/database.dart deleted file mode 100644 index 19f0356..0000000 --- a/lib/features/realtime_database/data/data_sources/database.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:firebase_database/firebase_database.dart'; - -FirebaseDatabase database = FirebaseDatabase.instance; From 9b9a5e70748ca42b084e8942418db782be30e20b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:45:54 +0200 Subject: [PATCH 441/475] Move use cases --- lib/core/dependency_injection.dart | 4 ++-- .../get_user_collections_usecase.dart | 0 .../usecases/{ => get_user_poems}/get_user_poems_usecase.dart | 0 .../presentation/bloc/firebase_database_cubit.dart | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename lib/features/saved_poems/domain/usecases/{ => get_user_collections}/get_user_collections_usecase.dart (100%) rename lib/features/saved_poems/domain/usecases/{ => get_user_poems}/get_user_poems_usecase.dart (100%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 6d9fa7a..b0bfb5c 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -26,8 +26,8 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/d import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart'; diff --git a/lib/features/saved_poems/domain/usecases/get_user_collections_usecase.dart b/lib/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart similarity index 100% rename from lib/features/saved_poems/domain/usecases/get_user_collections_usecase.dart rename to lib/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart diff --git a/lib/features/saved_poems/domain/usecases/get_user_poems_usecase.dart b/lib/features/saved_poems/domain/usecases/get_user_poems/get_user_poems_usecase.dart similarity index 100% rename from lib/features/saved_poems/domain/usecases/get_user_poems_usecase.dart rename to lib/features/saved_poems/domain/usecases/get_user_poems/get_user_poems_usecase.dart diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart index f2acf66..0600d81 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart @@ -12,8 +12,8 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_co import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems/get_user_poems_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_collection_exists/is_collection_exists_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_poem_exists_params.dart'; From 31a8cf626e186357bf505d3341e6946ea828c854 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:46:36 +0200 Subject: [PATCH 442/475] Move firebase database cubit and state --- lib/core/dependency_injection.dart | 2 +- lib/features/multi_bloc_provider/presentation/init_blocs.dart | 2 +- .../presentation/widgets/poem_view/custom_save_button.dart | 2 +- .../bloc/{ => firebase_database}/firebase_database_cubit.dart | 2 +- .../bloc/{ => firebase_database}/firebase_database_state.dart | 0 .../presentation/pages/saved_collection_view_page.dart | 4 ++-- .../saved_poems/presentation/pages/write_poem_page.dart | 4 ++-- .../saved_poems/presentation/screens/saved_poems_screen.dart | 4 ++-- .../saved_poems/presentation/widgets/collection_card.dart | 2 +- .../presentation/widgets/create_collection_bottom_sheet.dart | 4 ++-- .../saved_poems/presentation/widgets/saved_poem_card.dart | 2 +- .../widgets/update_collection_bottom_sheet_content.dart | 4 ++-- 12 files changed, 16 insertions(+), 16 deletions(-) rename lib/features/saved_poems/presentation/bloc/{ => firebase_database}/firebase_database_cubit.dart (99%) rename lib/features/saved_poems/presentation/bloc/{ => firebase_database}/firebase_database_state.dart (100%) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index b0bfb5c..9ee6cf8 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -33,7 +33,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists/is_p import 'package:poetlum/features/saved_poems/domain/usecases/is_poem_exists_by_name/is_poem_exists_by_name_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/theme_change/data/data_sources/local/shared_preferences_service.dart'; import 'package:poetlum/features/theme_change/data/repository/shared_preferences_repository_impl.dart'; import 'package:poetlum/features/theme_change/domain/repository/shared_preferences_repository.dart'; diff --git a/lib/features/multi_bloc_provider/presentation/init_blocs.dart b/lib/features/multi_bloc_provider/presentation/init_blocs.dart index 433814d..483699d 100644 --- a/lib/features/multi_bloc_provider/presentation/init_blocs.dart +++ b/lib/features/multi_bloc_provider/presentation/init_blocs.dart @@ -5,7 +5,7 @@ import 'package:poetlum/features/authorization/presentation/bloc/authorization/a import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/theme_change/presentation/bloc/change_theme_cubit.dart'; class InitBlocs extends StatelessWidget { diff --git a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart index d7f32d2..374630f 100644 --- a/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart +++ b/lib/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart @@ -7,7 +7,7 @@ import 'package:like_button/like_button.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; class CustomSaveButton extends StatefulWidget { const CustomSaveButton({super.key, required this.poemEntity}); diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart similarity index 99% rename from lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart rename to lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart index 0600d81..06b7603 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart @@ -24,7 +24,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem import 'package:poetlum/features/saved_poems/domain/usecases/save_poem/save_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_collection/update_poems_in_collection_usecase.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase, this._isCollectionExistsUseCase, this._isPoemExistsByNameUseCase) : super(const FirebaseDatabaseState()); diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database_state.dart b/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart similarity index 100% rename from lib/features/saved_poems/presentation/bloc/firebase_database_state.dart rename to lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index f49465f..47acd2b 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -9,8 +9,8 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/saved_poem_card.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart'; diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 93fdc61..dd9e521 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -10,8 +10,8 @@ import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; class WritePoemPage extends StatefulWidget { const WritePoemPage(this._userRepository, {super.key}); diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index c5a5171..d5488ba 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -10,8 +10,8 @@ import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/collection_card.dart'; import 'package:poetlum/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 2a45a35..70ef158 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -9,7 +9,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; class CollectionCard extends StatefulWidget { const CollectionCard({super.key, required this.collection}); diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 1ecf8ab..1024cb3 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -13,8 +13,8 @@ import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; class CreateCollectionBottomSheetContent extends StatefulWidget { const CreateCollectionBottomSheetContent({super.key, required this.poems}); diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index ddc0233..8200d2a 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -11,7 +11,7 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; class SavedPoemCard extends StatefulWidget { const SavedPoemCard({super.key, required this.poemEntity, required this.collectionEntity}); diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 05a8921..0954557 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -12,8 +12,8 @@ import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_cubit.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database_state.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; class UpdateCollectionBottomSheetContent extends StatefulWidget { const UpdateCollectionBottomSheetContent({super.key, this.allSavedPoems, this.poemsInTheCollection, required this.collectionName}); From 9536ec4965415efc39f77639f692baac234204fe Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:50:09 +0200 Subject: [PATCH 443/475] Sort imports --- lib/core/dependency_injection.dart | 4 ++-- .../widgets/app_bar/buttons/refresh_button.dart | 2 +- .../widgets/app_bar/buttons/settings_button.dart | 2 +- .../application/presentation/pages/screens_wrapper.dart | 4 ++-- .../presentation/widgets/drawer/custom_drawer.dart | 2 +- .../presentation/widgets/drawer/custom_header.dart | 2 +- .../authorization/presentation/pages/login/login_page.dart | 2 +- .../presentation/pages/registration/registration_page.dart | 2 +- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 6 +++--- .../poems_feed/presentation/screens/poems_feed_screen.dart | 2 +- .../presentation/pages/saved_collection_view_page.dart | 4 ++-- .../presentation/pages/saved_poem_view_page.dart | 4 ++-- .../saved_poems/presentation/pages/write_poem_page.dart | 2 +- .../presentation/screens/saved_poems_screen.dart | 4 ++-- .../saved_poems/presentation/widgets/collection_card.dart | 2 +- .../widgets/create_collection_bottom_sheet.dart | 2 +- .../saved_poems/presentation/widgets/saved_poem_card.dart | 2 +- .../widgets/update_collection_bottom_sheet_content.dart | 2 +- 18 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 9ee6cf8..ef8926b 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -1,6 +1,8 @@ import 'package:dio/dio.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:get_it/get_it.dart'; +import 'package:poetlum/core/shared/data/repository/user_repository_impl.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/authorization/data/data_sources/remote/firebase_service.dart'; import 'package:poetlum/features/authorization/data/repository/auth_repository_impl.dart'; import 'package:poetlum/features/authorization/data/repository/firebase_repository_impl.dart'; @@ -13,9 +15,7 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/bloc/validation/validators.dart'; import 'package:poetlum/features/poems_feed/data/data_sources/remote/poem_api_service.dart'; import 'package:poetlum/features/poems_feed/data/repository/poem_repository_impl.dart'; -import 'package:poetlum/core/shared/data/repository/user_repository_impl.dart'; import 'package:poetlum/features/poems_feed/domain/repository/poem_repository.dart'; -import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/features/poems_feed/domain/usecases/get_poems_usecase.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/saved_poems/data/data_sources/remote/firebase_api_service.dart'; diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart index c2ee9ce..4b79703 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -3,11 +3,11 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; class RefreshButton extends StatefulWidget { const RefreshButton({super.key}); diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart index 113cf25..b7508a3 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart @@ -2,8 +2,8 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; -import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/animated_color_option_button.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/color_options.dart'; diff --git a/lib/features/application/presentation/pages/screens_wrapper.dart b/lib/features/application/presentation/pages/screens_wrapper.dart index 623111c..a4ffaff 100644 --- a/lib/features/application/presentation/pages/screens_wrapper.dart +++ b/lib/features/application/presentation/pages/screens_wrapper.dart @@ -3,11 +3,11 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; -import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_drawer.dart'; +import 'package:poetlum/features/poems_feed/presentation/screens/poems_feed_screen.dart'; import 'package:poetlum/features/saved_poems/presentation/screens/saved_poems_screen.dart'; class ScreensWrapper extends StatefulWidget { diff --git a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart index 8225663..1033e26 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_header.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_search_button.dart'; @@ -13,7 +14,6 @@ import 'package:poetlum/features/application/presentation/widgets/drawer/custom_ import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; class CustomDrawer extends StatefulWidget { const CustomDrawer(this._userRepository, {super.key}); diff --git a/lib/features/application/presentation/widgets/drawer/custom_header.dart b/lib/features/application/presentation/widgets/drawer/custom_header.dart index b95e733..f0527be 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_header.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_header.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/poems_feed/data/models/firebase_user.dart'; class CustomDrawerHeader extends StatelessWidget { const CustomDrawerHeader({super.key, required this.user}); diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index ea5a990..dee2a14 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; @@ -12,7 +13,6 @@ import 'package:poetlum/features/authorization/presentation/bloc/validation/vali import 'package:poetlum/features/authorization/presentation/widgets/auth_button.dart'; import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; -import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; class _Header extends StatefulWidget { const _Header(); diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index c302e58..3faff46 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; @@ -13,7 +14,6 @@ import 'package:poetlum/features/authorization/presentation/widgets/auth_button. import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; -import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; class _Header extends StatefulWidget { const _Header(); diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 441aaf9..d216ce5 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,16 +1,16 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/custom_share_button.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_author.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_content.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_line_count.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_title.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/poems_feed/presentation/widgets/poem_view/custom_save_button.dart'; class PoemViewPage extends StatefulWidget { const PoemViewPage({super.key}); diff --git a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart index 35f21fe..866f11b 100644 --- a/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart +++ b/lib/features/poems_feed/presentation/screens/poems_feed_screen.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; +import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_card.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_event.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_state.dart'; -import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_card.dart'; class PoemsFeedScreen extends StatelessWidget { const PoemsFeedScreen({super.key}); diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 47acd2b..b2a97b3 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -3,11 +3,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index e654f24..2229b88 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,15 +1,15 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/custom_share_button.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_author.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_content.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_line_count.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_title.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; class SavedPoemViewPage extends StatefulWidget { const SavedPoemViewPage({super.key}); diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index dd9e521..1be1c1a 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -7,9 +7,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; -import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index d5488ba..6627fd2 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 70ef158..bbea984 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -5,9 +5,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 1024cb3..6e947e7 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -8,11 +8,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 8200d2a..1700588 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -7,9 +7,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 0954557..a8bb241 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -8,10 +8,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; From afad8b19d038e117223a573fe59e86d46845f2af Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 12:05:04 +0200 Subject: [PATCH 444/475] Create ToastManager --- .../presentation/widgets/toast_manager.dart | 26 ++++++++++++++ .../widgets/drawer/custom_drawer.dart | 28 ++------------- .../presentation/widgets/auth_button.dart | 28 ++------------- .../presentation/pages/write_poem_page.dart | 28 ++------------- .../create_collection_bottom_sheet.dart | 34 ++++--------------- ...pdate_collection_bottom_sheet_content.dart | 30 +++------------- 6 files changed, 45 insertions(+), 129 deletions(-) create mode 100644 lib/core/shared/presentation/widgets/toast_manager.dart diff --git a/lib/core/shared/presentation/widgets/toast_manager.dart b/lib/core/shared/presentation/widgets/toast_manager.dart new file mode 100644 index 0000000..2d9b997 --- /dev/null +++ b/lib/core/shared/presentation/widgets/toast_manager.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class ToastManager{ + static Future showPositiveToast(String text) async{ + await Fluttertoast.showToast( + msg: text, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + ); + } + + static Future showNegativeToast(String error) async{ + await Fluttertoast.showToast( + msg: error, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); + } +} diff --git a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart index 1033e26..e593731 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -3,10 +3,10 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_checkbox_tile.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_header.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_search_button.dart'; @@ -121,10 +121,10 @@ class _CustomDrawerState extends State { if (state is RemotePoemDone) { Navigator.pop(context); - _showPositiveToast('We have received wonderful poems 😉'); + ToastManager.showPositiveToast('We have received wonderful poems 😉'); } if(state is RemotePoemError){ - _showNegativeToast('Failed to retrieve wonderful poems. An error occurred 😓'); + ToastManager.showNegativeToast('Failed to retrieve wonderful poems. An error occurred 😓'); } }, child: BlocBuilder( @@ -167,26 +167,4 @@ class _CustomDrawerState extends State { ); void _toggleCheckbox(bool? value) => setState(() => _isRandom = value); - - Future _showPositiveToast(String text) async{ - await Fluttertoast.showToast( - msg: text, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.green, - textColor: Colors.white, - fontSize: 16, - ); - } - - Future _showNegativeToast(String error) async{ - await Fluttertoast.showToast( - msg: error, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16, - ); - } } diff --git a/lib/features/authorization/presentation/widgets/auth_button.dart b/lib/features/authorization/presentation/widgets/auth_button.dart index 6268a52..a2db36e 100644 --- a/lib/features/authorization/presentation/widgets/auth_button.dart +++ b/lib/features/authorization/presentation/widgets/auth_button.dart @@ -1,7 +1,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttertoast/fluttertoast.dart'; +import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_state.dart'; @@ -25,7 +25,7 @@ class AuthButton< Widget build(BuildContext context)=> BlocConsumer( listener: (__, registerState) { if (registerState.status == AuthStatus.success) { - _showPositiveToast(successfulToastText); + ToastManager.showPositiveToast(successfulToastText); FirebaseAnalytics.instance.logEvent( name: 'auth', parameters: { @@ -40,7 +40,7 @@ class AuthButton< 'status': 'failed', }, ); - _showNegativeToast(registerState.errorMessage ?? 'Unknown error'); + ToastManager.showNegativeToast(registerState.errorMessage ?? 'Unknown error'); } }, builder: (context, state) => state.status == AuthStatus.submitting @@ -60,26 +60,4 @@ class AuthButton< ), ), ); - - Future _showPositiveToast(String text) async{ - await Fluttertoast.showToast( - msg: text, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.green, - textColor: Colors.white, - fontSize: 16, - ); - } - - Future _showNegativeToast(String error) async{ - await Fluttertoast.showToast( - msg: error, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16, - ); - } } diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 1be1c1a..a7b6612 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -5,11 +5,11 @@ import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; +import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; @@ -133,7 +133,7 @@ class _WritePoemPageState extends State { ); } - await _showPositiveToast('Your amazing poem has been saved! :D'); + await ToastManager.showPositiveToast('Your amazing poem has been saved! :D'); } else{ unawaited( FirebaseAnalytics.instance.logEvent( @@ -144,7 +144,7 @@ class _WritePoemPageState extends State { ), ); - await _showNegativeToast('A poem with the stunning name is already in your saved poems. Please try another name 📝'); + await ToastManager.showNegativeToast('A poem with the stunning name is already in your saved poems. Please try another name 📝'); } }, child: const Padding( @@ -160,28 +160,6 @@ class _WritePoemPageState extends State { ), ), ); - - Future _showPositiveToast(String text) async{ - await Fluttertoast.showToast( - msg: text, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.green, - textColor: Colors.white, - fontSize: 16, - ); - } - - Future _showNegativeToast(String error) async{ - await Fluttertoast.showToast( - msg: error, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16, - ); - } } class _CustomTextField extends StatelessWidget { diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 6e947e7..6f64ab6 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -5,12 +5,12 @@ import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; @@ -177,7 +177,7 @@ class _CreateButtonWidget extends StatelessWidget { Widget build(BuildContext context) => BlocConsumer( listener: (context, state) { if (state.status == FirebaseDatabaseStatus.error) { - _showNegativeToast('An error occurred :('); + ToastManager.showNegativeToast('An error occurred :('); } }, builder: (context, state) => state.status == FirebaseDatabaseStatus.submitting @@ -204,7 +204,7 @@ class _CreateButtonWidget extends StatelessWidget { ), ); - await _showNegativeToast('Please select at least one poem to add to the collection'); + await ToastManager.showNegativeToast('Please select at least one poem to add to the collection'); } else if(textController.text.isEmpty){ unawaited( FirebaseAnalytics.instance.logEvent( @@ -216,7 +216,7 @@ class _CreateButtonWidget extends StatelessWidget { ), ); - await _showNegativeToast('Please provide the name for the collection'); + await ToastManager.showNegativeToast('Please provide the name for the collection'); } else{ final isCollectionExist = await context.read().isCollectionExists( @@ -244,7 +244,7 @@ class _CreateButtonWidget extends StatelessWidget { ).toList(), ); - await _showPositiveToast('The collection has been successfully saved'); + await ToastManager.showPositiveToast('The collection has been successfully saved'); } else{ unawaited( FirebaseAnalytics.instance.logEvent( @@ -256,7 +256,7 @@ class _CreateButtonWidget extends StatelessWidget { ), ); - await _showNegativeToast('The collection with this name already exists'); + await ToastManager.showNegativeToast('The collection with this name already exists'); } } @@ -267,26 +267,4 @@ class _CreateButtonWidget extends StatelessWidget { ), ), ); - - Future _showPositiveToast(String text) async{ - await Fluttertoast.showToast( - msg: text, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.green, - textColor: Colors.white, - fontSize: 16, - ); - } - - Future _showNegativeToast(String error) async{ - await Fluttertoast.showToast( - msg: error, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16, - ); - } } diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index a8bb241..30d6cb4 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -5,12 +5,12 @@ import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; @@ -172,7 +172,7 @@ class _EditButtonWidget extends StatelessWidget { Widget build(BuildContext context) => BlocConsumer( listener: (context, state) { if (state.status == FirebaseDatabaseStatus.error) { - _showNegativeToast('An error occurred :('); + ToastManager.showNegativeToast('An error occurred :('); } }, builder: (context, state) => state.status == FirebaseDatabaseStatus.submitting @@ -190,7 +190,7 @@ class _EditButtonWidget extends StatelessWidget { ), ); - await _showNegativeToast('Please select at least one poem to add to the collection'); + await ToastManager.showNegativeToast('Please select at least one poem to add to the collection'); } else{ unawaited( FirebaseAnalytics.instance.logEvent( @@ -210,7 +210,7 @@ class _EditButtonWidget extends StatelessWidget { ).toList(), ); - await _showPositiveToast('The collection has been successfully saved'); + await ToastManager.showPositiveToast('The collection has been successfully saved'); } }, child: const Padding( @@ -219,26 +219,4 @@ class _EditButtonWidget extends StatelessWidget { ), ), ); - - Future _showPositiveToast(String text) async{ - await Fluttertoast.showToast( - msg: text, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.green, - textColor: Colors.white, - fontSize: 16, - ); - } - - Future _showNegativeToast(String error) async{ - await Fluttertoast.showToast( - msg: error, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16, - ); - } } From 2c12dafc5139385dac2150ebb8678b2bec7751ce Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 14:07:37 +0200 Subject: [PATCH 445/475] Create custom animation controller --- .../animations/animation_controller.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 lib/core/shared/presentation/widgets/animations/animation_controller.dart diff --git a/lib/core/shared/presentation/widgets/animations/animation_controller.dart b/lib/core/shared/presentation/widgets/animations/animation_controller.dart new file mode 100644 index 0000000..af4c9d7 --- /dev/null +++ b/lib/core/shared/presentation/widgets/animations/animation_controller.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class AnimationControllerWithDelays { + AnimationControllerWithDelays({ + required this.initialDelay, + required this.delayBetweenAnimations, + required this.numberOfAnimations, + }) { + animationStates = List.filled(numberOfAnimations, false); + } + + final Duration initialDelay; + final Duration delayBetweenAnimations; + final int numberOfAnimations; + late List animationStates; + + Future startAnimations(VoidCallback setStateCallback) async { + await Future.delayed(initialDelay); + for (var i = 0; i < numberOfAnimations; i++) { + await Future.delayed(delayBetweenAnimations); + animationStates[i] = true; + setStateCallback(); + } + } +} From 1833f158eb8eb74632274985dc09bac1ca4525a4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 14:07:47 +0200 Subject: [PATCH 446/475] Implement animation controller --- .../app_bar/buttons/refresh_button.dart | 26 ++---- .../app_bar/buttons/settings_button.dart | 25 ++--- .../widgets/poem_card/poem_card.dart | 36 +++----- .../widgets/drawer/custom_drawer.dart | 48 +++------- .../presentation/pages/login/login_page.dart | 90 ++++++------------ .../pages/registration/registration_page.dart | 91 ++++++------------- .../pages/poem_view/poem_view.dart | 48 +++------- .../pages/saved_collection_view_page.dart | 24 ++--- .../pages/saved_poem_view_page.dart | 44 +++------ .../presentation/pages/write_poem_page.dart | 36 +++----- .../presentation/widgets/collection_card.dart | 28 ++---- .../create_collection_bottom_sheet.dart | 37 +++----- .../presentation/widgets/saved_poem_card.dart | 28 ++---- ...pdate_collection_bottom_sheet_content.dart | 36 +++----- 14 files changed, 189 insertions(+), 408 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart index 4b79703..e8e5590 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -3,6 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; @@ -17,30 +18,23 @@ class RefreshButton extends StatefulWidget { } class _RefreshButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { - bool isButtonAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 400); + late AnimationControllerWithDelays animationController; + final Duration animationDelay = const Duration(milliseconds: 200); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override Widget build(BuildContext context) => RightAnimation( - animationField: isButtonAnimated , + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/6, child: BlocBuilder( builder: (context, state) => RotationTransition( diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart index b7508a3..fc9d6fe 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart @@ -2,6 +2,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/features/theme_change/presentation/widgets/animated_color_option_button.dart'; @@ -15,7 +16,7 @@ class SettingsButton extends StatefulWidget { } class _SettingsButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { - bool isButtonAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override @@ -27,24 +28,18 @@ class _SettingsButtonState extends State with TickerProviderStat 'opened': 'true', }, ); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } + super.initState(); + animationController = AnimationControllerWithDelays( + initialDelay: Duration.zero, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override Widget build(BuildContext context) => RightAnimation( - animationField: isButtonAnimated , + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/6, child: RotationTransition( turns: rotationAnimation, diff --git a/lib/core/shared/presentation/widgets/poem_card/poem_card.dart b/lib/core/shared/presentation/widgets/poem_card/poem_card.dart index d324514..4e784f9 100644 --- a/lib/core/shared/presentation/widgets/poem_card/poem_card.dart +++ b/lib/core/shared/presentation/widgets/poem_card/poem_card.dart @@ -3,6 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; @@ -16,33 +17,18 @@ class PoemCard extends StatefulWidget { } class _PoemCardState extends State { - bool isTitleAnimated = false; - bool isAuthorAnimated = false; - bool isTextAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isTitleAnimated = val, - (val) => isAuthorAnimated = val, - (val) => isTextAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 3, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -72,21 +58,21 @@ class _PoemCardState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ RightAnimation( - animationField: isTitleAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/8, child: _TitleText(title: widget.poemEntity.title), ), const SizedBox(height: 8), RightAnimation( - animationField: isAuthorAnimated, + animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.width/8, child: _AuthorText(author: widget.poemEntity.author), ), const SizedBox(height: 16), RightAnimation( - animationField: isTextAnimated, + animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.width/8, child: _PoemText(text: widget.poemEntity.text, maxLength: 250), ), diff --git a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart index e593731..417ae36 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -4,6 +4,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; @@ -31,37 +32,18 @@ class _CustomDrawerState extends State { final TextEditingController _resultCountController = TextEditingController(); bool? _isRandom = false; - bool isHeaderAnimated = false; - bool isAuthorAnimated = false; - bool isTitleAnimated = false; - bool isLinesNumberAnimated = false; - bool isResultCountAnimated = false; - bool isCheckboxAnimated = false; - bool isButtonAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 125); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isHeaderAnimated = val, - (val) => isAuthorAnimated = val, - (val) => isTitleAnimated = val, - (val) => isLinesNumberAnimated = val, - (val) => isResultCountAnimated = val, - (val) => isCheckboxAnimated = val, - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 7, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -76,41 +58,41 @@ class _CustomDrawerState extends State { children: [ const CustomSpacer(heightFactor: 0.02), TopAnimation( - animationField: isHeaderAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomDrawerHeader(user: widget._userRepository.getCurrentUser()), ), TopAnimation( - animationField: isAuthorAnimated, + animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomTextField(hintText: 'Author', controller: _authorController), ), const CustomSpacer(heightFactor: 0.04), TopAnimation( - animationField: isTitleAnimated, + animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomTextField(hintText: 'Title', controller: _titleController), ), const CustomSpacer(heightFactor: 0.04), TopAnimation( - animationField: isLinesNumberAnimated, + animationField: animationController.animationStates[3], positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomTextField(hintText: 'Number of lines', isNumberInput: true, controller: _numberOfLinesController), ), const CustomSpacer(heightFactor: 0.04), TopAnimation( - animationField: isResultCountAnimated, + animationField: animationController.animationStates[4], positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomTextField(hintText: 'Result count', isNumberInput: true, controller: _resultCountController), ), const CustomSpacer(heightFactor: 0.04), TopAnimation( - animationField: isCheckboxAnimated, + animationField: animationController.animationStates[5], positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomCheckboxTile(value: _isRandom, onChanged: _toggleCheckbox), ), @@ -129,7 +111,7 @@ class _CustomDrawerState extends State { }, child: BlocBuilder( builder: (context, state) => TopAnimation( - animationField: isButtonAnimated, + animationField: animationController.animationStates[6], positionInitialValue: MediaQuery.of(context).size.height / 14, child: CustomSearchButton( onPressed: () { diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index dee2a14..72bf314 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,10 +1,9 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; @@ -22,25 +21,18 @@ class _Header extends StatefulWidget { } class _HeaderState extends State<_Header> { - bool isHeaderAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); - void _startAnimations() { - final setters = [ - (val) => isHeaderAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } - } - @override void initState() { super.initState(); - _startAnimations(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -51,7 +43,7 @@ class _HeaderState extends State<_Header> { const Spacer(), RightAnimation( - animationField: isHeaderAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: const Text( 'Login', @@ -78,35 +70,18 @@ class _Form extends StatefulWidget { } class _FormState extends State<_Form> { - bool isUsernameAnimated = false; - bool isEmailAnimated = false; - bool isPasswordAnimated = false; - bool isButtonAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); - - Future _startAnimations() async { - await Future.delayed(const Duration(milliseconds: 200)); - - final setters = [ - (val) => isEmailAnimated = val, - (val) => isPasswordAnimated = val, - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - unawaited( - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ), - ); - } - } - @override void initState() { super.initState(); - _startAnimations(); + animationController = AnimationControllerWithDelays( + initialDelay: const Duration(milliseconds: 400), + delayBetweenAnimations: animationDelay, + numberOfAnimations: 3, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -116,21 +91,21 @@ class _FormState extends State<_Form> { child: Column( children: [ RightAnimation( - animationField: isEmailAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: EmailTextField(controller: widget.emailController), ), const Spacer(), RightAnimation( - animationField: isPasswordAnimated, + animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.height/14, child: PasswordTextField(controller: widget.passwordController), ), const Spacer(), RightAnimation( - animationField: isButtonAnimated, + animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.height/14, child: AuthButton< AuthCubit, @@ -164,36 +139,25 @@ class _Footer extends StatefulWidget { } class _FooterState extends State<_Footer> { - bool isFooterAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); - Future _startAnimations() async { - await Future.delayed(const Duration(milliseconds: 800)); - - final setters = [ - (val) => isFooterAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - unawaited( - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ), - ); - } - } - @override void initState() { super.initState(); - _startAnimations(); + animationController = AnimationControllerWithDelays( + initialDelay: const Duration(milliseconds: 1000), + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height/10, child: RightAnimation( - animationField: isFooterAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 3faff46..86d5caa 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_state.dart'; @@ -23,25 +24,18 @@ class _Header extends StatefulWidget { } class _HeaderState extends State<_Header> { - bool isHeaderAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); - void _startAnimations() { - final setters = [ - (val) => isHeaderAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } - } - @override void initState() { super.initState(); - _startAnimations(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -52,7 +46,7 @@ class _HeaderState extends State<_Header> { const Spacer(), TopAnimation( - animationField: isHeaderAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: const Text( 'Registration', @@ -81,36 +75,18 @@ class _Form extends StatefulWidget { } class _FormState extends State<_Form> { - bool isUsernameAnimated = false; - bool isEmailAnimated = false; - bool isPasswordAnimated = false; - bool isButtonAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); - - Future _startAnimations() async { - await Future.delayed(const Duration(milliseconds: 200)); - - final setters = [ - (val) => isUsernameAnimated = val, - (val) => isEmailAnimated = val, - (val) => isPasswordAnimated = val, - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - unawaited( - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ), - ); - } - } - @override void initState() { super.initState(); - _startAnimations(); + animationController = AnimationControllerWithDelays( + initialDelay: const Duration(milliseconds: 400), + delayBetweenAnimations: animationDelay, + numberOfAnimations: 4, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -120,28 +96,28 @@ class _FormState extends State<_Form> { child: Column( children: [ TopAnimation( - animationField: isUsernameAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: UsernameTextField(controller: widget.usernameController), ), const Spacer(), TopAnimation( - animationField: isEmailAnimated, + animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.height/14, child: EmailTextField(controller: widget.emailController), ), const Spacer(), TopAnimation( - animationField: isPasswordAnimated, + animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.height/14, child: PasswordTextField(controller: widget.passwordController), ), const Spacer(), TopAnimation( - animationField: isButtonAnimated, + animationField: animationController.animationStates[3], positionInitialValue: MediaQuery.of(context).size.height/14, child: AuthButton< AuthCubit, @@ -174,36 +150,25 @@ class _Footer extends StatefulWidget { } class _FooterState extends State<_Footer> { - bool isFooterAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); - Future _startAnimations() async { - await Future.delayed(const Duration(milliseconds: 1000)); - - final setters = [ - (val) => isFooterAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - unawaited( - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ), - ); - } - } - @override void initState() { super.initState(); - _startAnimations(); + animationController = AnimationControllerWithDelays( + initialDelay: const Duration(milliseconds: 1200), + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height/10, child: TopAnimation( - animationField: isFooterAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index d216ce5..b65f974 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,6 +1,7 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; @@ -20,39 +21,18 @@ class PoemViewPage extends StatefulWidget { } class _PoemViewPageState extends State { - bool isSaveButtonAnimated = false; - bool isTitleAnimated = false; - bool isAuthorAnimated = false; - bool isShareButtonAnimated = false; - bool isContentAnimated = false; - bool isPoemLineAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isSaveButtonAnimated = val, - (val) => isTitleAnimated = val, - (val) => isAuthorAnimated = val, - (val) => isShareButtonAnimated = val, - (val) => isContentAnimated = val, - (val) => isPoemLineAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 6, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -70,42 +50,42 @@ class _PoemViewPageState extends State { children: [ const CustomSpacer(heightFactor: 0.04), RightAnimation( - animationField: isSaveButtonAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/8, child: CustomSaveButton(poemEntity: poemEntity), ), const CustomSpacer(heightFactor: 0.02), RightAnimation( - animationField: isTitleAnimated, + animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemTitle(title: poemEntity.title ?? ''), ), const CustomSpacer(heightFactor: 0.02), RightAnimation( - animationField: isAuthorAnimated, + animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemAuthor(author: poemEntity.author ?? ''), ), const CustomSpacer(heightFactor: 0.02), RightAnimation( - animationField: isShareButtonAnimated, + animationField: animationController.animationStates[3], positionInitialValue: MediaQuery.of(context).size.width/8, child: CustomShareButton(poemEntity: poemEntity), ), const CustomSpacer(heightFactor: 0.02), RightAnimation( - animationField: isContentAnimated, + animationField: animationController.animationStates[4], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemContent(text: poemEntity.text ?? ''), ), const CustomSpacer(heightFactor: 0.02), RightAnimation( - animationField: isPoemLineAnimated, + animationField: animationController.animationStates[5], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemLineCount(lineCount: poemEntity.linecount ?? 0), ), diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index b2a97b3..8500f28 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; @@ -121,30 +122,23 @@ class _CollectionName extends StatefulWidget { } class __CollectionNameState extends State<_CollectionName> { - bool isNameAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); - void _startAnimations() { - final setters = [ - (val) => isNameAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } - } - @override void initState() { super.initState(); - _startAnimations(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override Widget build(BuildContext context) => TopAnimation( - animationField: isNameAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: Align( child: Padding( diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 2229b88..b49424d 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,6 +1,7 @@ // ignore_for_file: avoid_positional_boolean_parameters import 'package:flutter/material.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; @@ -19,37 +20,18 @@ class SavedPoemViewPage extends StatefulWidget { } class _SavedPoemViewPageState extends State { - bool isTitleAnimated = false; - bool isAuthorAnimated = false; - bool isShareButtonAnimated = false; - bool isContentAnimated = false; - bool isPoemLineAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isTitleAnimated = val, - (val) => isAuthorAnimated = val, - (val) => isShareButtonAnimated = val, - (val) => isContentAnimated = val, - (val) => isPoemLineAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 5, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -68,35 +50,35 @@ class _SavedPoemViewPageState extends State { const CustomSpacer(heightFactor: 0.04), TopAnimation( - animationField: isTitleAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemTitle(title: poemEntity.title ?? ''), ), const CustomSpacer(heightFactor: 0.02), TopAnimation( - animationField: isAuthorAnimated, + animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemAuthor(author: poemEntity.author ?? ''), ), const CustomSpacer(heightFactor: 0.02), TopAnimation( - animationField: isShareButtonAnimated, + animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.width/8, child: CustomShareButton(poemEntity: poemEntity), ), const CustomSpacer(heightFactor: 0.02), TopAnimation( - animationField: isContentAnimated, + animationField: animationController.animationStates[3], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemContent(text: poemEntity.text ?? ''), ), const CustomSpacer(heightFactor: 0.02), TopAnimation( - animationField: isPoemLineAnimated, + animationField: animationController.animationStates[4], positionInitialValue: MediaQuery.of(context).size.width/8, child: PoemLineCount(lineCount: poemEntity.linecount ?? 0), ), diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index a7b6612..78eeefd 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; @@ -27,9 +28,7 @@ class _WritePoemPageState extends State { final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); - bool isNameTextFieldAnimated = false; - bool isPoemTextFieldAnimated = false; - bool isButtonAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override @@ -41,25 +40,12 @@ class _WritePoemPageState extends State { 'opened': 'true', }, ); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isNameTextFieldAnimated = val, - (val) => isPoemTextFieldAnimated = val, - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: Duration.zero, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 3, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -80,14 +66,14 @@ class _WritePoemPageState extends State { children: [ const _CustomSpacer(heightFactor: 0.03), RightAnimation( - animationField: isNameTextFieldAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/3, child: _CustomTextField(hintText: 'Poem name', controller: _nameController, isLarge: false), ), const _CustomSpacer(heightFactor: 0.03), RightAnimation( - animationField: isPoemTextFieldAnimated, + animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.width/3, child: _CustomTextField(hintText: 'Your amazing poem :D', controller: _contentController, isLarge: true), ), @@ -96,7 +82,7 @@ class _WritePoemPageState extends State { if (state.status == FirebaseDatabaseStatus.submitting) const CircularProgressIndicator() else RightAnimation( - animationField: isButtonAnimated, + animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.width/3, child: FilledButton.tonal( onPressed: () async { diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index bbea984..1ad38e0 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; import 'package:poetlum/features/saved_poems/domain/entities/collection.dart'; @@ -21,29 +22,18 @@ class CollectionCard extends StatefulWidget { } class _CollectionCardState extends State { - bool isAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -96,7 +86,7 @@ class _CollectionCardState extends State { padding: const EdgeInsets.all(16), child: SingleChildScrollView( child: TopAnimation( - animationField: isAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.height/14, child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 6f64ab6..dbaca0f 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -8,6 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; @@ -29,10 +30,7 @@ class _CreateCollectionBottomSheetContentState extends State _selectController; - bool isHeaderAnimated = false; - bool isTextFieldAnimated = false; - bool isSelectAnimated = false; - bool isButtonAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override @@ -46,7 +44,12 @@ class _CreateCollectionBottomSheetContentState extends State setState(() {})); } @override @@ -57,22 +60,6 @@ class _CreateCollectionBottomSheetContentState extends State[ - (val) => isHeaderAnimated = val, - (val) => isTextFieldAnimated = val, - (val) => isSelectAnimated = val, - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then((_) { - if (mounted) { - setState(() => setters[i](true)); - } - }); - } - } @override Widget build(BuildContext context) => SizedBox( @@ -83,28 +70,28 @@ class _CreateCollectionBottomSheetContentState extends State { - bool isAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: Duration.zero, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() => setState(() {})); } @override @@ -102,7 +92,7 @@ class _SavedPoemCardState extends State { child: Padding( padding: const EdgeInsets.all(16), child: RightAnimation( - animationField: isAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/8, child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 30d6cb4..6de3996 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -8,6 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; @@ -31,9 +32,7 @@ class _UpdateCollectionBottomSheetContentState extends State> selectedValues = >[]; late List> allValues = >[]; - bool isTitleAnimated = false; - bool isSelectorAnimated = false; - bool isButtonAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 200); @override @@ -50,25 +49,12 @@ class _UpdateCollectionBottomSheetContentState extends State[ - (val) => isTitleAnimated = val, - (val) => isSelectorAnimated = val, - (val) => isButtonAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_){ - if (mounted) { - setState(() => setters[i](true)); - } - } - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 3, + ); + animationController.startAnimations(() => setState(() {})); } void _initPoemsInTheCollectionValues(){ @@ -102,21 +88,21 @@ class _UpdateCollectionBottomSheetContentState extends State Date: Sun, 17 Dec 2023 14:18:29 +0200 Subject: [PATCH 447/475] Check if the widgets are mounted before playing an animation. --- .../app_bar/buttons/refresh_button.dart | 6 +++++- .../app_bar/buttons/settings_button.dart | 6 +++++- .../widgets/poem_card/poem_card.dart | 6 +++++- .../widgets/drawer/custom_drawer.dart | 6 +++++- .../presentation/pages/login/login_page.dart | 18 +++++++++++++++--- .../pages/registration/registration_page.dart | 18 +++++++++++++++--- .../pages/poem_view/poem_view.dart | 6 +++++- .../pages/saved_collection_view_page.dart | 6 +++++- .../pages/saved_poem_view_page.dart | 6 +++++- .../presentation/pages/write_poem_page.dart | 6 +++++- .../presentation/widgets/collection_card.dart | 6 +++++- .../create_collection_bottom_sheet.dart | 6 +++++- .../presentation/widgets/saved_poem_card.dart | 6 +++++- ...update_collection_bottom_sheet_content.dart | 6 +++++- 14 files changed, 90 insertions(+), 18 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart index e8e5590..08ecd3b 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -29,7 +29,11 @@ class _RefreshButtonState extends State with TickerProviderStateM delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart index fc9d6fe..cd8a83a 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart @@ -34,7 +34,11 @@ class _SettingsButtonState extends State with TickerProviderStat delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/core/shared/presentation/widgets/poem_card/poem_card.dart b/lib/core/shared/presentation/widgets/poem_card/poem_card.dart index 4e784f9..55742f0 100644 --- a/lib/core/shared/presentation/widgets/poem_card/poem_card.dart +++ b/lib/core/shared/presentation/widgets/poem_card/poem_card.dart @@ -28,7 +28,11 @@ class _PoemCardState extends State { delayBetweenAnimations: animationDelay, numberOfAnimations: 3, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart index 417ae36..c5c4fed 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -43,7 +43,11 @@ class _CustomDrawerState extends State { delayBetweenAnimations: animationDelay, numberOfAnimations: 7, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 72bf314..bcf5109 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -32,7 +32,11 @@ class _HeaderState extends State<_Header> { delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override @@ -81,7 +85,11 @@ class _FormState extends State<_Form> { delayBetweenAnimations: animationDelay, numberOfAnimations: 3, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override @@ -150,7 +158,11 @@ class _FooterState extends State<_Footer> { delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 86d5caa..f7de64a 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -35,7 +35,11 @@ class _HeaderState extends State<_Header> { delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override @@ -86,7 +90,11 @@ class _FormState extends State<_Form> { delayBetweenAnimations: animationDelay, numberOfAnimations: 4, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override @@ -161,7 +169,11 @@ class _FooterState extends State<_Footer> { delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index b65f974..4f99a9f 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -32,7 +32,11 @@ class _PoemViewPageState extends State { delayBetweenAnimations: animationDelay, numberOfAnimations: 6, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 8500f28..020ce8c 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -133,7 +133,11 @@ class __CollectionNameState extends State<_CollectionName> { delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index b49424d..1702d7f 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -31,7 +31,11 @@ class _SavedPoemViewPageState extends State { delayBetweenAnimations: animationDelay, numberOfAnimations: 5, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index 78eeefd..cab349e 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -45,7 +45,11 @@ class _WritePoemPageState extends State { delayBetweenAnimations: animationDelay, numberOfAnimations: 3, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 1ad38e0..1f89039 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -33,7 +33,11 @@ class _CollectionCardState extends State { delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index dbaca0f..70b3b81 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -49,7 +49,11 @@ class _CreateCollectionBottomSheetContentState extends State setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 66f3bb6..55b1e0d 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -36,7 +36,11 @@ class _SavedPoemCardState extends State { delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); - animationController.startAnimations(() => setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 6de3996..5119bfa 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -54,7 +54,11 @@ class _UpdateCollectionBottomSheetContentState extends State setState(() {})); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } void _initPoemsInTheCollectionValues(){ From a41bdaa8f436ae109e5f9c0c274366147ece64da Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 14:23:17 +0200 Subject: [PATCH 448/475] Trim search queries --- .../widgets/drawer/custom_drawer.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart index c5c4fed..f225264 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -122,20 +122,20 @@ class _CustomDrawerState extends State { FirebaseAnalytics.instance.logEvent( name: 'search_poem', parameters: { - 'author': _authorController.text, - 'title': _titleController.text, - 'line_count': _numberOfLinesController.text, - 'poem_count': _resultCountController.text, + 'author': _authorController.text.trim(), + 'title': _titleController.text.trim(), + 'line_count': _numberOfLinesController.text.trim(), + 'poem_count': _resultCountController.text.trim(), 'is_random': _isRandom.toString(), }, ); BlocProvider.of(context).add( GetPoemsEvent( - author: _authorController.text, - title: _titleController.text, - lineCount: _numberOfLinesController.text, - poemCount: _resultCountController.text, + author: _authorController.text.trim(), + title: _titleController.text.trim(), + lineCount: _numberOfLinesController.text.trim(), + poemCount: _resultCountController.text.trim(), isRandom: _isRandom ?? false, ), ); From ce56e170276101fd4dc500db43bdad5adaf19019 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 14:35:27 +0200 Subject: [PATCH 449/475] Move widgets to the top of the files --- .../presentation/pages/login/login_page.dart | 92 ++++++------ .../pages/registration/registration_page.dart | 142 +++++++++--------- 2 files changed, 116 insertions(+), 118 deletions(-) diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index bcf5109..158e8a7 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -13,6 +13,52 @@ import 'package:poetlum/features/authorization/presentation/widgets/auth_button. import 'package:poetlum/features/authorization/presentation/widgets/email_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + + @override + void initState() { + super.initState(); + final formCubit = context.read(); + + _emailController.addListener(() { + formCubit.emailChanged(_emailController.text); + }); + + _passwordController.addListener(() { + formCubit.passwordChanged(_passwordController.text); + }); + } + + @override + Widget build(BuildContext context) => Scaffold( + body: SafeArea( + child: BlocBuilder( + builder: (context, state) => Column( + children: [ + const _Header(), + + _Form( + _emailController, + _passwordController, + ), + + const _Footer(), + ], + ), + ), + ), + ); +} + class _Header extends StatefulWidget { const _Header(); @@ -189,49 +235,3 @@ class _FooterState extends State<_Footer> { ), ); } - -class LoginPage extends StatefulWidget { - const LoginPage({super.key}); - - @override - State createState() => _LoginPageState(); -} - -class _LoginPageState extends State { - final TextEditingController _emailController = TextEditingController(); - final TextEditingController _passwordController = TextEditingController(); - - @override - void initState() { - super.initState(); - final formCubit = context.read(); - - _emailController.addListener(() { - formCubit.emailChanged(_emailController.text); - }); - - _passwordController.addListener(() { - formCubit.passwordChanged(_passwordController.text); - }); - } - - @override - Widget build(BuildContext context) => Scaffold( - body: SafeArea( - child: BlocBuilder( - builder: (context, state) => Column( - children: [ - const _Header(), - - _Form( - _emailController, - _passwordController, - ), - - const _Footer(), - ], - ), - ), - ), - ); -} diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index f7de64a..1ae74d2 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -16,6 +16,76 @@ import 'package:poetlum/features/authorization/presentation/widgets/email_field. import 'package:poetlum/features/authorization/presentation/widgets/password_field.dart'; import 'package:poetlum/features/authorization/presentation/widgets/username_field.dart'; +class RegistrationPage extends StatefulWidget { + const RegistrationPage({super.key}); + + @override + State createState() => _RegistrationPageState(); +} + +class _RegistrationPageState extends State { + final TextEditingController _usernameController = TextEditingController(); + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + + bool isTextAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 200); + + void _startAnimations() { + final setters = [ + (val) => isTextAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + + @override + void initState() { + super.initState(); + + final formCubit = context.read(); + + _usernameController.addListener(() { + formCubit.usernameChanged(_usernameController.text); + }); + + _emailController.addListener(() { + formCubit.emailChanged(_emailController.text); + }); + + _passwordController.addListener(() { + formCubit.passwordChanged(_passwordController.text); + }); + + _startAnimations(); + } + + @override + Widget build(BuildContext context) => Scaffold( + body: SafeArea( + child: BlocBuilder( + builder: (context, state) => Column( + children: [ + const _Header(), + + _Form( + _usernameController, + _emailController, + _passwordController, + ), + + const _Footer(), + ], + ), + ), + ), + ); +} + class _Header extends StatefulWidget { const _Header(); @@ -200,75 +270,3 @@ class _FooterState extends State<_Footer> { ), ); } - -class RegistrationPage extends StatefulWidget { - const RegistrationPage({super.key}); - - @override - State createState() => _RegistrationPageState(); -} - -class _RegistrationPageState extends State { - final TextEditingController _usernameController = TextEditingController(); - final TextEditingController _emailController = TextEditingController(); - final TextEditingController _passwordController = TextEditingController(); - - - - bool isTextAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 200); - - void _startAnimations() { - final setters = [ - (val) => isTextAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } - } - - @override - void initState() { - super.initState(); - - final formCubit = context.read(); - - _usernameController.addListener(() { - formCubit.usernameChanged(_usernameController.text); - }); - - _emailController.addListener(() { - formCubit.emailChanged(_emailController.text); - }); - - _passwordController.addListener(() { - formCubit.passwordChanged(_passwordController.text); - }); - - _startAnimations(); - } - - @override - Widget build(BuildContext context) => Scaffold( - body: SafeArea( - child: BlocBuilder( - builder: (context, state) => Column( - children: [ - const _Header(), - - _Form( - _usernameController, - _emailController, - _passwordController, - ), - - const _Footer(), - ], - ), - ), - ), - ); -} From a28631007ff90e5580421453cfe66da3dd8d24cb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 14:46:51 +0200 Subject: [PATCH 450/475] Decompoze widget --- .../pages/saved_collection_view_page.dart | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 020ce8c..55e1669 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/app_bar.dart'; import 'package:poetlum/core/shared/presentation/widgets/loader.dart'; @@ -87,13 +88,7 @@ class _SavedCollectionViewPageState extends State { _CollectionName(name: collectionEntity.name), Expanded( child: poemsInTheCollection.isEmpty - ? const Center( - child: Text( - 'Nothing to show here 😔\nTap on the "Edit" button to add amazing poems to the collection', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 18), - ), - ) + ? const EmptyCollectionText() : ListView.builder( itemCount: poemsInTheCollection.length, itemBuilder: (__, index) => SavedPoemCard( @@ -111,6 +106,49 @@ class _SavedCollectionViewPageState extends State { } } +class EmptyCollectionText extends StatefulWidget { + const EmptyCollectionText({super.key}); + + @override + State createState() => _EmptyCollectionTextState(); +} + +class _EmptyCollectionTextState extends State { + late AnimationControllerWithDelays animationController; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); + } + + @override + Widget build(BuildContext context) => RightAnimation( + animationField: animationController.animationStates[0], + positionInitialValue: MediaQuery.of(context).size.height/14, + child: const Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 15), + child: Text( + 'Nothing to show here 😔\nTap on the "Edit" button to add amazing poems to the collection', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ), + ), + ); +} + class _CollectionName extends StatefulWidget { const _CollectionName({required this.name}); From b9306b88f4339e7e7343bf523c48b134a632c2eb Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Sun, 17 Dec 2023 17:56:41 +0200 Subject: [PATCH 451/475] Create edit functionality (now working) --- lib/core/dependency_injection.dart | 3 + .../remote/firebase_api_service.dart | 67 +++++++ .../firebase_db_repository_impl.dart | 3 + .../repository/firebase_db_repository.dart | 1 + .../usecases/edit_poem/edit_poem_params.dart | 10 ++ .../usecases/edit_poem/edit_poem_usecase.dart | 28 +++ .../firebase_database_cubit.dart | 48 ++++- .../pages/saved_poem_view_page.dart | 47 ++++- .../widgets/edit_poem_bottom_sheet.dart | 167 ++++++++++++++++++ .../presentation/widgets/saved_poem_card.dart | 11 +- 10 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart create mode 100644 lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart create mode 100644 lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index ef8926b..29c4758 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -25,6 +25,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collecti import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems/get_user_poems_usecase.dart'; @@ -82,6 +83,7 @@ void initializeDependencies() { ..registerSingleton(IsCollectionExistsUseCase(getIt())) ..registerSingleton(SaveColorUseCase(getIt())) ..registerSingleton(GetColorUseCase(getIt())) + ..registerSingleton(EditPoemUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -106,6 +108,7 @@ void initializeDependencies() { getIt(), getIt(), getIt(), + getIt(), ), ) ..registerFactory(() => RegisterFormValidationCubit( diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 4792f82..1d1df2c 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -18,6 +18,7 @@ abstract class FirebaseDatabaseService{ Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); Future> getPoemsInCollection({required String userId, String? collectionName}); Future isCollectionExists({required String userId, required String collectionName}); + Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -375,4 +376,70 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { return false; } + + @override + Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}) async { + final userRef = FirebaseDatabase.instance.ref(userId); + + if (collectionName == null) { + // Editing a poem in the "poems" key + final poemsRef = userRef.child('poems'); + final snapshot = await poemsRef.get(); + + if (snapshot.exists) { + final poems = snapshot.value as Map; + for (final key in poems.keys) { + final poemData = Map.from(poems[key] as Map); + if (poemData['title'] == oldTitle && poemData['author'] == oldAuthor &&poemData['text'] == oldText && poemData['linecount'] == oldLineCount) { + // Update the poem + await poemsRef.child(key).set({ + 'title': newTitle, + 'author': newAuthor, + 'text': newText, + 'linecount': newLineCount, + }); + break; + } + } + } + } else { + // Editing a poem in a specific collection + final collectionsRef = userRef.child('collections'); + final collectionsSnapshot = await collectionsRef.get(); + + if (collectionsSnapshot.exists) { + print('1'); + final collections = Map.from(collectionsSnapshot.value as Map); + for (final collectionKey in collections.keys) { + final collection = collections[collectionKey]; + if (collection['name'] == collectionName && collection['poems'] != null) { + print('2'); + final collectionPoemsRef = collectionsRef.child('$collectionKey/poems'); + final collectionPoemsSnapshot = await collectionPoemsRef.get(); + + if (collectionPoemsSnapshot.exists) { + print('3'); + final collectionPoems = collectionPoemsSnapshot.value as Map; + //final collectionPoems = Map.from(collectionPoemsSnapshot.value as Map); + for (final poemKey in collectionPoems.keys) { + final poemData = Map.from(collectionPoems[poemKey] as Map); + print('4'); + if (poemData['title'] == oldTitle && poemData['author'] == oldAuthor && + poemData['text'] == oldText && poemData['linecount'] == oldLineCount) { + // Update the poem in the collection + await collectionPoemsRef.child(poemKey).set({ + 'title': newTitle, + 'author': newAuthor, + 'text': newText, + 'linecount': newLineCount, + }); + break; + } + } + } + } + } + } + } + } } diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 45d0708..6b302b2 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -43,4 +43,7 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future isPoemExistsByName({required String poemTitle, required String userId}) => _databaseService.isPoemExistsByName(poemTitle: poemTitle, userId: userId); + + @override + Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName})=> _databaseService.editPoem(userId: userId, oldTitle: oldTitle, oldAuthor: oldAuthor, oldText: oldText, oldLineCount: oldLineCount, newTitle: newTitle, newAuthor: newAuthor, newText: newText, newLineCount: newLineCount, collectionName: collectionName); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 99c9b84..09f72d3 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -14,4 +14,5 @@ abstract class FirebaseDatabaseRepository { Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); Future> getPoemsInCollection({required String userId, String? collectionName}); Future isCollectionExists({required String userId, required String collectionName}); + Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}); } diff --git a/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart b/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart new file mode 100644 index 0000000..a66762e --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart @@ -0,0 +1,10 @@ +import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; + +class EditPoemParams { + EditPoemParams({required this.newPoemEntity, required this.oldPoemEntity, required this.userId, this.collectionName}); + + final PoemEntity newPoemEntity; + final PoemEntity oldPoemEntity; + final String userId; + final String? collectionName; +} diff --git a/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart b/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart new file mode 100644 index 0000000..67684f4 --- /dev/null +++ b/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart @@ -0,0 +1,28 @@ +import 'package:poetlum/core/usecases/usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart'; + +class EditPoemUseCase implements UseCase{ + EditPoemUseCase(this._databaseRepository); + + final FirebaseDatabaseRepository _databaseRepository; + + @override + Future call({EditPoemParams? params}) async { + if(params != null){ + print('params ${params.collectionName}'); + await _databaseRepository.editPoem( + oldAuthor: params.oldPoemEntity.author ?? '', + oldTitle: params.oldPoemEntity.title ?? '', + oldText: params.oldPoemEntity.text ?? '', + oldLineCount: params.oldPoemEntity.linecount ?? 0, + newAuthor: params.newPoemEntity.author ?? '', + newTitle: params.newPoemEntity.title ?? '', + newText: params.newPoemEntity.text ?? '', + newLineCount: params.newPoemEntity.linecount ?? 0, + collectionName: params.collectionName, + userId: params.userId, + ); + } + } +} diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart index 06b7603..e3f0715 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart @@ -10,6 +10,8 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart'; +import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart'; @@ -27,7 +29,21 @@ import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_col import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase, this._isCollectionExistsUseCase, this._isPoemExistsByNameUseCase) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit( + this._getUserPoemsUseCase, + this._getUserCollectionsUseCase, + this._savePoemUseCase, + this._deletePoemUseCase, + this._isPoemExistsUseCase, + this._createNewCollectionUseCase, + this._deleteCollectionUseCase, + this._deletePoemFromCollectionUseCase, + this._updatePoemsInCollectionUseCase, + this._getPoemsInCollectionUseCase, + this._isCollectionExistsUseCase, + this._isPoemExistsByNameUseCase, + this._editPoemUseCase, + ) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -41,6 +57,7 @@ class FirebaseDatabaseCubit extends Cubit { final GetPoemsInCollectionUseCase _getPoemsInCollectionUseCase; final IsCollectionExistsUseCase _isCollectionExistsUseCase; final IsPoemExistsByNameUseCase _isPoemExistsByNameUseCase; + final EditPoemUseCase _editPoemUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -264,4 +281,33 @@ class FirebaseDatabaseCubit extends Cubit { return false; } } + + Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}) async { + emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); + print(collectionName); + try{ + await _editPoemUseCase( + params: EditPoemParams( + userId: userId, + newPoemEntity: PoemEntity( + author: newAuthor, + linecount: newLineCount, + text: newText, + title: newTitle, + ), + oldPoemEntity: PoemEntity( + author: oldAuthor, + linecount: oldLineCount, + text: oldText, + title: oldTitle, + ), + collectionName: collectionName, + ), + ); + + emit(state.copyWith(status: FirebaseDatabaseStatus.success)); + } catch(e) { + emit(state.copyWith(status: FirebaseDatabaseStatus.error)); + } + } } diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 1702d7f..c916c33 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -11,6 +11,7 @@ import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_content. import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_line_count.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_title.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; +import 'package:poetlum/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart'; class SavedPoemViewPage extends StatefulWidget { const SavedPoemViewPage({super.key}); @@ -40,10 +41,19 @@ class _SavedPoemViewPageState extends State { @override Widget build(BuildContext context) { - final poemEntity = ModalRoute.of(context)?.settings.arguments as PoemEntity; + final poemEntity = (ModalRoute.of(context)?.settings.arguments! as Map)['poem'] as PoemEntity; + final collectionName = (ModalRoute.of(context)?.settings.arguments! as Map)['collectionName'] as String?; return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), + floatingActionButton: _CustomFloatingActionButton( + author: poemEntity.author ?? '', + collectionName: collectionName, + context: context, + lineCount: poemEntity.linecount ?? 0, + text: poemEntity.text ?? '', + title: poemEntity.title ?? '', + ), body: SizedBox( width: MediaQuery.of(context).size.width, child: SingleChildScrollView( @@ -95,3 +105,38 @@ class _SavedPoemViewPageState extends State { ); } } + +class _CustomFloatingActionButton extends StatelessWidget { + const _CustomFloatingActionButton({ + required this.context, + required this.title, + required this.author, + required this.text, + required this.lineCount, + required this.collectionName, + }); + + final BuildContext context; + final String title; + final String author; + final String text; + final int lineCount; + final String? collectionName; + + @override + Widget build(BuildContext context) => FloatingActionButton( + onPressed: () => showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => EditPoemBottomSheetContent( + collectionName: collectionName, + author: author, + lineCount: lineCount, + text: text, + title: title, + ), + ), + tooltip: 'Edit this poem', + child: const Icon(Icons.edit), + ); +} diff --git a/lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart new file mode 100644 index 0000000..a7785b8 --- /dev/null +++ b/lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart @@ -0,0 +1,167 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; + +class EditPoemBottomSheetContent extends StatefulWidget { + const EditPoemBottomSheetContent({super.key, required this.title, required this.author, required this.text, required this.lineCount, this.collectionName}); + + final String title; + final String author; + final String text; + final int lineCount; + final String? collectionName; + + @override + State createState() => _EditPoemBottomSheetContentState(); +} + +class _EditPoemBottomSheetContentState extends State { + final TextEditingController _authorController = TextEditingController(); + final TextEditingController _titleController = TextEditingController(); + final TextEditingController _textController = TextEditingController(); + + @override + void initState() { + super.initState(); + _authorController.text = widget.author; + _titleController.text = widget.title; + _textController.text = widget.text; + } + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height / 1.15, + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.04), + const _CustomHeader(), + + const CustomSpacer(heightFactor: 0.04), + const _CustomSubHeader('Title'), + const CustomSpacer(heightFactor: 0.01), + _CustomTextField(hintText: 'Title', controller: _titleController, isMultiline: false), + + const CustomSpacer(heightFactor: 0.04), + const _CustomSubHeader('Author'), + const CustomSpacer(heightFactor: 0.01), + _CustomTextField(hintText: 'Author', controller: _authorController, isMultiline: false), + + const CustomSpacer(heightFactor: 0.04), + const _CustomSubHeader('Text'), + const CustomSpacer(heightFactor: 0.01), + _CustomTextField(hintText: 'Text', controller: _textController, isMultiline: true), + + const CustomSpacer(heightFactor: 0.04), + _CustomEditButton( + authorController: _authorController, + titleController: _titleController, + textController: _textController, + collectionName: widget.collectionName, + oldAuthor: widget.author, + oldLineCount: widget.lineCount, + oldText: widget.text, + oldTitle: widget.title, + ), + + const CustomSpacer(heightFactor: 0.04), + ], + ), + ), + ); +} + +class _CustomEditButton extends StatelessWidget { + const _CustomEditButton({required this.authorController, required this.titleController, required this.textController, required this.collectionName, required this.oldTitle, required this.oldAuthor, required this.oldText, required this.oldLineCount}); + + final TextEditingController authorController; + final TextEditingController titleController; + final TextEditingController textController; + final String? collectionName; + final String oldTitle; + final String oldAuthor; + final String oldText; + final int oldLineCount; + + @override + Widget build(BuildContext context) => FilledButton( + onPressed: () => context.read().editPoem( + userId: getIt().getCurrentUser().userId!, + newTitle: titleController.text.trim(), + newAuthor: authorController.text.trim(), + newText: textController.text.trim(), + oldAuthor: oldAuthor, + oldTitle: oldTitle, + oldLineCount: oldLineCount, + oldText: oldText, + newLineCount: textController.text.trim().split('\n').length, + collectionName: collectionName, + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), + child: Text('Edit'), + ), + ); +} + +class _CustomTextField extends StatelessWidget { + const _CustomTextField({ + required this.hintText, + required this.controller, + required this.isMultiline, + }); + + final String hintText; + final TextEditingController controller; + final bool isMultiline; + + @override + Widget build(BuildContext context) => LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth / 1.35; + + return Align( + child: SizedBox( + width: width, + child: TextField( + maxLines: isMultiline + ? 8 + : 1, + controller: controller, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: hintText, + hintStyle: const TextStyle(color: Colors.grey), + ), + ), + ), + ); + }, + ); +} + +class _CustomHeader extends StatelessWidget { + const _CustomHeader(); + + @override + Widget build(BuildContext context) => const Text( + 'Edit this remarkable poem 🖊️', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + ); +} + +class _CustomSubHeader extends StatelessWidget { + const _CustomSubHeader(this.text); + + final String text; + + @override + Widget build(BuildContext context) => Text( + text, + style: const TextStyle(fontSize: 20), + ); +} diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 55b1e0d..94c2d62 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -85,7 +85,16 @@ class _SavedPoemCardState extends State { }, ); - Navigator.pushNamed(context, savedPoemViewConstant, arguments: widget.poemEntity); + Navigator.pushNamed( + context, + savedPoemViewConstant, + arguments: { + 'poem': widget.poemEntity, + 'collectionName': widget.collectionEntity.isAllSavedPoems + ? null + : widget.collectionEntity.name, + }, + ); }, child: Card( shape: const RoundedRectangleBorder( From dd39c07db64ba26987ffa086fe19d5bc973166be Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:40:18 +0200 Subject: [PATCH 452/475] Revert "Create edit functionality (now working)" This reverts commit b9306b88f4339e7e7343bf523c48b134a632c2eb. --- lib/core/dependency_injection.dart | 3 - .../remote/firebase_api_service.dart | 67 ------- .../firebase_db_repository_impl.dart | 3 - .../repository/firebase_db_repository.dart | 1 - .../usecases/edit_poem/edit_poem_params.dart | 10 -- .../usecases/edit_poem/edit_poem_usecase.dart | 28 --- .../firebase_database_cubit.dart | 48 +---- .../pages/saved_poem_view_page.dart | 47 +---- .../widgets/edit_poem_bottom_sheet.dart | 167 ------------------ .../presentation/widgets/saved_poem_card.dart | 11 +- 10 files changed, 3 insertions(+), 382 deletions(-) delete mode 100644 lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart delete mode 100644 lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart delete mode 100644 lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 29c4758..ef8926b 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -25,7 +25,6 @@ import 'package:poetlum/features/saved_poems/domain/usecases/create_new_collecti import 'package:poetlum/features/saved_poems/domain/usecases/delete_collection/delete_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_poems/get_user_poems_usecase.dart'; @@ -83,7 +82,6 @@ void initializeDependencies() { ..registerSingleton(IsCollectionExistsUseCase(getIt())) ..registerSingleton(SaveColorUseCase(getIt())) ..registerSingleton(GetColorUseCase(getIt())) - ..registerSingleton(EditPoemUseCase(getIt())) // Validators ..registerLazySingleton(() => UsernameValidator()) @@ -108,7 +106,6 @@ void initializeDependencies() { getIt(), getIt(), getIt(), - getIt(), ), ) ..registerFactory(() => RegisterFormValidationCubit( diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 1d1df2c..4792f82 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -18,7 +18,6 @@ abstract class FirebaseDatabaseService{ Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); Future> getPoemsInCollection({required String userId, String? collectionName}); Future isCollectionExists({required String userId, required String collectionName}); - Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}); } class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { @@ -376,70 +375,4 @@ class FirebaseDatabaseServiceImpl implements FirebaseDatabaseService { return false; } - - @override - Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}) async { - final userRef = FirebaseDatabase.instance.ref(userId); - - if (collectionName == null) { - // Editing a poem in the "poems" key - final poemsRef = userRef.child('poems'); - final snapshot = await poemsRef.get(); - - if (snapshot.exists) { - final poems = snapshot.value as Map; - for (final key in poems.keys) { - final poemData = Map.from(poems[key] as Map); - if (poemData['title'] == oldTitle && poemData['author'] == oldAuthor &&poemData['text'] == oldText && poemData['linecount'] == oldLineCount) { - // Update the poem - await poemsRef.child(key).set({ - 'title': newTitle, - 'author': newAuthor, - 'text': newText, - 'linecount': newLineCount, - }); - break; - } - } - } - } else { - // Editing a poem in a specific collection - final collectionsRef = userRef.child('collections'); - final collectionsSnapshot = await collectionsRef.get(); - - if (collectionsSnapshot.exists) { - print('1'); - final collections = Map.from(collectionsSnapshot.value as Map); - for (final collectionKey in collections.keys) { - final collection = collections[collectionKey]; - if (collection['name'] == collectionName && collection['poems'] != null) { - print('2'); - final collectionPoemsRef = collectionsRef.child('$collectionKey/poems'); - final collectionPoemsSnapshot = await collectionPoemsRef.get(); - - if (collectionPoemsSnapshot.exists) { - print('3'); - final collectionPoems = collectionPoemsSnapshot.value as Map; - //final collectionPoems = Map.from(collectionPoemsSnapshot.value as Map); - for (final poemKey in collectionPoems.keys) { - final poemData = Map.from(collectionPoems[poemKey] as Map); - print('4'); - if (poemData['title'] == oldTitle && poemData['author'] == oldAuthor && - poemData['text'] == oldText && poemData['linecount'] == oldLineCount) { - // Update the poem in the collection - await collectionPoemsRef.child(poemKey).set({ - 'title': newTitle, - 'author': newAuthor, - 'text': newText, - 'linecount': newLineCount, - }); - break; - } - } - } - } - } - } - } - } } diff --git a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart index 6b302b2..45d0708 100644 --- a/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart +++ b/lib/features/saved_poems/data/repository/firebase_db_repository_impl.dart @@ -43,7 +43,4 @@ class FirebaseDatabaseRepositoryImpl implements FirebaseDatabaseRepository{ @override Future isPoemExistsByName({required String poemTitle, required String userId}) => _databaseService.isPoemExistsByName(poemTitle: poemTitle, userId: userId); - - @override - Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName})=> _databaseService.editPoem(userId: userId, oldTitle: oldTitle, oldAuthor: oldAuthor, oldText: oldText, oldLineCount: oldLineCount, newTitle: newTitle, newAuthor: newAuthor, newText: newText, newLineCount: newLineCount, collectionName: collectionName); } diff --git a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart index 09f72d3..99c9b84 100644 --- a/lib/features/saved_poems/domain/repository/firebase_db_repository.dart +++ b/lib/features/saved_poems/domain/repository/firebase_db_repository.dart @@ -14,5 +14,4 @@ abstract class FirebaseDatabaseRepository { Future updatePoemsInCollection({required String userId, required String collectionName, required List updatedPoems}); Future> getPoemsInCollection({required String userId, String? collectionName}); Future isCollectionExists({required String userId, required String collectionName}); - Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}); } diff --git a/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart b/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart deleted file mode 100644 index a66762e..0000000 --- a/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; - -class EditPoemParams { - EditPoemParams({required this.newPoemEntity, required this.oldPoemEntity, required this.userId, this.collectionName}); - - final PoemEntity newPoemEntity; - final PoemEntity oldPoemEntity; - final String userId; - final String? collectionName; -} diff --git a/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart b/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart deleted file mode 100644 index 67684f4..0000000 --- a/lib/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:poetlum/core/usecases/usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/repository/firebase_db_repository.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart'; - -class EditPoemUseCase implements UseCase{ - EditPoemUseCase(this._databaseRepository); - - final FirebaseDatabaseRepository _databaseRepository; - - @override - Future call({EditPoemParams? params}) async { - if(params != null){ - print('params ${params.collectionName}'); - await _databaseRepository.editPoem( - oldAuthor: params.oldPoemEntity.author ?? '', - oldTitle: params.oldPoemEntity.title ?? '', - oldText: params.oldPoemEntity.text ?? '', - oldLineCount: params.oldPoemEntity.linecount ?? 0, - newAuthor: params.newPoemEntity.author ?? '', - newTitle: params.newPoemEntity.title ?? '', - newText: params.newPoemEntity.text ?? '', - newLineCount: params.newPoemEntity.linecount ?? 0, - collectionName: params.collectionName, - userId: params.userId, - ); - } - } -} diff --git a/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart b/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart index e3f0715..06b7603 100644 --- a/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart +++ b/lib/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart @@ -10,8 +10,6 @@ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_ import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem/delete_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/delete_poem_from_collection/delete_poem_from_collection_usecase.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_params.dart'; -import 'package:poetlum/features/saved_poems/domain/usecases/edit_poem/edit_poem_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_params.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_poems_in_collection/get_poems_in_collection_usecase.dart'; import 'package:poetlum/features/saved_poems/domain/usecases/get_user_collections/get_user_collections_usecase.dart'; @@ -29,21 +27,7 @@ import 'package:poetlum/features/saved_poems/domain/usecases/update_poems_in_col import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_state.dart'; class FirebaseDatabaseCubit extends Cubit { - FirebaseDatabaseCubit( - this._getUserPoemsUseCase, - this._getUserCollectionsUseCase, - this._savePoemUseCase, - this._deletePoemUseCase, - this._isPoemExistsUseCase, - this._createNewCollectionUseCase, - this._deleteCollectionUseCase, - this._deletePoemFromCollectionUseCase, - this._updatePoemsInCollectionUseCase, - this._getPoemsInCollectionUseCase, - this._isCollectionExistsUseCase, - this._isPoemExistsByNameUseCase, - this._editPoemUseCase, - ) : super(const FirebaseDatabaseState()); + FirebaseDatabaseCubit(this._getUserPoemsUseCase, this._getUserCollectionsUseCase, this._savePoemUseCase, this._deletePoemUseCase, this._isPoemExistsUseCase, this._createNewCollectionUseCase, this._deleteCollectionUseCase, this._deletePoemFromCollectionUseCase, this._updatePoemsInCollectionUseCase, this._getPoemsInCollectionUseCase, this._isCollectionExistsUseCase, this._isPoemExistsByNameUseCase) : super(const FirebaseDatabaseState()); final GetUserPoemsUseCase _getUserPoemsUseCase; final GetUserCollectionsUseCase _getUserCollectionsUseCase; @@ -57,7 +41,6 @@ class FirebaseDatabaseCubit extends Cubit { final GetPoemsInCollectionUseCase _getPoemsInCollectionUseCase; final IsCollectionExistsUseCase _isCollectionExistsUseCase; final IsPoemExistsByNameUseCase _isPoemExistsByNameUseCase; - final EditPoemUseCase _editPoemUseCase; Future?> getUserPoems(String userId) async{ emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); @@ -281,33 +264,4 @@ class FirebaseDatabaseCubit extends Cubit { return false; } } - - Future editPoem({required String userId, required String oldTitle, required String oldAuthor, required String oldText, required int oldLineCount, required String newTitle, required String newAuthor, required String newText, required int newLineCount, String? collectionName}) async { - emit(state.copyWith(status: FirebaseDatabaseStatus.submitting)); - print(collectionName); - try{ - await _editPoemUseCase( - params: EditPoemParams( - userId: userId, - newPoemEntity: PoemEntity( - author: newAuthor, - linecount: newLineCount, - text: newText, - title: newTitle, - ), - oldPoemEntity: PoemEntity( - author: oldAuthor, - linecount: oldLineCount, - text: oldText, - title: oldTitle, - ), - collectionName: collectionName, - ), - ); - - emit(state.copyWith(status: FirebaseDatabaseStatus.success)); - } catch(e) { - emit(state.copyWith(status: FirebaseDatabaseStatus.error)); - } - } } diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index c916c33..1702d7f 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -11,7 +11,6 @@ import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_content. import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_line_count.dart'; import 'package:poetlum/core/shared/presentation/widgets/poem_card/poem_title.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; -import 'package:poetlum/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart'; class SavedPoemViewPage extends StatefulWidget { const SavedPoemViewPage({super.key}); @@ -41,19 +40,10 @@ class _SavedPoemViewPageState extends State { @override Widget build(BuildContext context) { - final poemEntity = (ModalRoute.of(context)?.settings.arguments! as Map)['poem'] as PoemEntity; - final collectionName = (ModalRoute.of(context)?.settings.arguments! as Map)['collectionName'] as String?; + final poemEntity = ModalRoute.of(context)?.settings.arguments as PoemEntity; return Scaffold( appBar: const CustomAppBar(title: 'Poetlum'), - floatingActionButton: _CustomFloatingActionButton( - author: poemEntity.author ?? '', - collectionName: collectionName, - context: context, - lineCount: poemEntity.linecount ?? 0, - text: poemEntity.text ?? '', - title: poemEntity.title ?? '', - ), body: SizedBox( width: MediaQuery.of(context).size.width, child: SingleChildScrollView( @@ -105,38 +95,3 @@ class _SavedPoemViewPageState extends State { ); } } - -class _CustomFloatingActionButton extends StatelessWidget { - const _CustomFloatingActionButton({ - required this.context, - required this.title, - required this.author, - required this.text, - required this.lineCount, - required this.collectionName, - }); - - final BuildContext context; - final String title; - final String author; - final String text; - final int lineCount; - final String? collectionName; - - @override - Widget build(BuildContext context) => FloatingActionButton( - onPressed: () => showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: (context) => EditPoemBottomSheetContent( - collectionName: collectionName, - author: author, - lineCount: lineCount, - text: text, - title: title, - ), - ), - tooltip: 'Edit this poem', - child: const Icon(Icons.edit), - ); -} diff --git a/lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart deleted file mode 100644 index a7785b8..0000000 --- a/lib/features/saved_poems/presentation/widgets/edit_poem_bottom_sheet.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; -import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; -import 'package:poetlum/features/saved_poems/presentation/bloc/firebase_database/firebase_database_cubit.dart'; - -class EditPoemBottomSheetContent extends StatefulWidget { - const EditPoemBottomSheetContent({super.key, required this.title, required this.author, required this.text, required this.lineCount, this.collectionName}); - - final String title; - final String author; - final String text; - final int lineCount; - final String? collectionName; - - @override - State createState() => _EditPoemBottomSheetContentState(); -} - -class _EditPoemBottomSheetContentState extends State { - final TextEditingController _authorController = TextEditingController(); - final TextEditingController _titleController = TextEditingController(); - final TextEditingController _textController = TextEditingController(); - - @override - void initState() { - super.initState(); - _authorController.text = widget.author; - _titleController.text = widget.title; - _textController.text = widget.text; - } - - @override - Widget build(BuildContext context) => SizedBox( - height: MediaQuery.of(context).size.height / 1.15, - width: MediaQuery.of(context).size.width, - child: SingleChildScrollView( - child: Column( - children: [ - const CustomSpacer(heightFactor: 0.04), - const _CustomHeader(), - - const CustomSpacer(heightFactor: 0.04), - const _CustomSubHeader('Title'), - const CustomSpacer(heightFactor: 0.01), - _CustomTextField(hintText: 'Title', controller: _titleController, isMultiline: false), - - const CustomSpacer(heightFactor: 0.04), - const _CustomSubHeader('Author'), - const CustomSpacer(heightFactor: 0.01), - _CustomTextField(hintText: 'Author', controller: _authorController, isMultiline: false), - - const CustomSpacer(heightFactor: 0.04), - const _CustomSubHeader('Text'), - const CustomSpacer(heightFactor: 0.01), - _CustomTextField(hintText: 'Text', controller: _textController, isMultiline: true), - - const CustomSpacer(heightFactor: 0.04), - _CustomEditButton( - authorController: _authorController, - titleController: _titleController, - textController: _textController, - collectionName: widget.collectionName, - oldAuthor: widget.author, - oldLineCount: widget.lineCount, - oldText: widget.text, - oldTitle: widget.title, - ), - - const CustomSpacer(heightFactor: 0.04), - ], - ), - ), - ); -} - -class _CustomEditButton extends StatelessWidget { - const _CustomEditButton({required this.authorController, required this.titleController, required this.textController, required this.collectionName, required this.oldTitle, required this.oldAuthor, required this.oldText, required this.oldLineCount}); - - final TextEditingController authorController; - final TextEditingController titleController; - final TextEditingController textController; - final String? collectionName; - final String oldTitle; - final String oldAuthor; - final String oldText; - final int oldLineCount; - - @override - Widget build(BuildContext context) => FilledButton( - onPressed: () => context.read().editPoem( - userId: getIt().getCurrentUser().userId!, - newTitle: titleController.text.trim(), - newAuthor: authorController.text.trim(), - newText: textController.text.trim(), - oldAuthor: oldAuthor, - oldTitle: oldTitle, - oldLineCount: oldLineCount, - oldText: oldText, - newLineCount: textController.text.trim().split('\n').length, - collectionName: collectionName, - ), - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), - child: Text('Edit'), - ), - ); -} - -class _CustomTextField extends StatelessWidget { - const _CustomTextField({ - required this.hintText, - required this.controller, - required this.isMultiline, - }); - - final String hintText; - final TextEditingController controller; - final bool isMultiline; - - @override - Widget build(BuildContext context) => LayoutBuilder( - builder: (context, constraints) { - final width = constraints.maxWidth / 1.35; - - return Align( - child: SizedBox( - width: width, - child: TextField( - maxLines: isMultiline - ? 8 - : 1, - controller: controller, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: hintText, - hintStyle: const TextStyle(color: Colors.grey), - ), - ), - ), - ); - }, - ); -} - -class _CustomHeader extends StatelessWidget { - const _CustomHeader(); - - @override - Widget build(BuildContext context) => const Text( - 'Edit this remarkable poem 🖊️', - style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), - ); -} - -class _CustomSubHeader extends StatelessWidget { - const _CustomSubHeader(this.text); - - final String text; - - @override - Widget build(BuildContext context) => Text( - text, - style: const TextStyle(fontSize: 20), - ); -} diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 94c2d62..55b1e0d 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -85,16 +85,7 @@ class _SavedPoemCardState extends State { }, ); - Navigator.pushNamed( - context, - savedPoemViewConstant, - arguments: { - 'poem': widget.poemEntity, - 'collectionName': widget.collectionEntity.isAllSavedPoems - ? null - : widget.collectionEntity.name, - }, - ); + Navigator.pushNamed(context, savedPoemViewConstant, arguments: widget.poemEntity); }, child: Card( shape: const RoundedRectangleBorder( From 6c8cd404b02c1521c07dc41289fd1c703d0100f9 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:12:18 +0200 Subject: [PATCH 453/475] Add username and password change --- .../presentation/widgets/app_bar/app_bar.dart | 2 + .../app_bar/buttons/credentials_button.dart | 217 ++++++++++++++++++ .../app_bar/buttons/refresh_button.dart | 2 +- .../app_bar/buttons/settings_button.dart | 2 +- 4 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart diff --git a/lib/core/shared/presentation/widgets/app_bar/app_bar.dart b/lib/core/shared/presentation/widgets/app_bar/app_bar.dart index cf95505..4b8feda 100644 --- a/lib/core/shared/presentation/widgets/app_bar/app_bar.dart +++ b/lib/core/shared/presentation/widgets/app_bar/app_bar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:poetlum/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart'; import 'package:poetlum/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart'; @@ -13,6 +14,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { actions: const [ RefreshButton(), SettingsButton(), + CredentialButton(), ], leading: leading, title: Text( diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart new file mode 100644 index 0000000..0e51a98 --- /dev/null +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -0,0 +1,217 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; +import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; + +class CredentialButton extends StatefulWidget { + const CredentialButton({super.key}); + + @override + State createState() => _CredentialButtonState(); +} + +class _CredentialButtonState extends State with TickerProviderStateMixin, RotatingButtonMixin { + late AnimationControllerWithDelays animationController; + final Duration animationDelay = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + FirebaseAnalytics.instance.logEvent( + name: 'credentials', + parameters: { + 'opened': 'true', + }, + ); + super.initState(); + animationController = AnimationControllerWithDelays( + initialDelay: Duration.zero, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 1, + ); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); + } + + @override + Widget build(BuildContext context) => RightAnimation( + animationField: animationController.animationStates[0], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: RotationTransition( + turns: rotationAnimation, + child: IconButton( + tooltip: 'Edit credentials ', + onPressed: () async{ + playAnimation(); + + await showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _BottomSheetContent(), + ); + }, + icon: const Icon(Icons.lock), + ), + ), + ); + + @override + void dispose() { + rotationController.dispose(); + super.dispose(); + } +} + +class _BottomSheetContent extends StatefulWidget { + const _BottomSheetContent(); + + @override + State<_BottomSheetContent> createState() => _BottomSheetContentState(); +} + +class _BottomSheetContentState extends State<_BottomSheetContent> { + bool isHeaderAnimated = false; + final Duration animationDelay = const Duration(milliseconds: 125); + + final TextEditingController _oldEmailController = TextEditingController(); + final TextEditingController _oldPasswordController = TextEditingController(); + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + final TextEditingController _usernameController = TextEditingController(); + + @override + void initState() { + super.initState(); + _startAnimations(); + _initCredentials(); + } + + void _initCredentials(){ + _emailController.text = getIt().getCurrentUser().email ?? ''; + _oldEmailController.text = getIt().getCurrentUser().email ?? ''; + _usernameController.text = getIt().getCurrentUser().username ?? ''; + } + + void _startAnimations() { + final setters = [ + (val) => isHeaderAnimated = val, + ]; + + for (var i = 0; i < setters.length; i++) { + Future.delayed(animationDelay * (i + 1)).then( + (_) => setState(() => setters[i](true)), + ); + } + } + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/1.25, + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.04), + const _Title(text: 'Edit credentials 🐸'), + + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'Old Email'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'Email', controller: _oldEmailController), + + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'Old Password'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'Password', controller: _oldPasswordController), + + const Divider(), + + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'Username'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'Username', controller: _usernameController), + + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'Email'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'Email', controller: _emailController), + + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'Password'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'Password', controller: _passwordController), + + const CustomSpacer(heightFactor: 0.04), + FilledButton( + onPressed: () async { + final user = FirebaseAuth.instance.currentUser; + + if (user != null) { + /*if(_usernameController.text.isNotEmpty){ + await user.updateDisplayName(_usernameController.text.trim()); + }*/ + if(_emailController.text.isNotEmpty){ + await user.reauthenticateWithCredential( + EmailAuthProvider.credential(email: _oldEmailController.text.trim(), password: _oldPasswordController.text.trim()) + ); + await user.updateEmail(_emailController.text.trim()); + await user.sendEmailVerification(); + } + /*if(_passwordController.text.isNotEmpty){ + await user.reauthenticateWithCredential( + EmailAuthProvider.credential(email: _oldEmailController.text.trim(), password: _oldPasswordController.text.trim()) + ); + await user.updatePassword(_passwordController.text.trim()); + }*/ + + //await user.reload(); + } + }, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: Text('Change'), + ), + ), + ], + ), + ), + ); +} + +class _Title extends StatelessWidget { + const _Title({required this.text}); + + final String text; + + @override + Widget build(BuildContext context) => Text( + text, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ); +} + +class _SubTitle extends StatelessWidget { + const _SubTitle({required this.text}); + + final String text; + + @override + Widget build(BuildContext context) => Text( + text, + style: const TextStyle( + fontSize: 22, + ), + ); +} diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart index 08ecd3b..6e2cfad 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -25,7 +25,7 @@ class _RefreshButtonState extends State with TickerProviderStateM void initState() { super.initState(); animationController = AnimationControllerWithDelays( - initialDelay: animationDelay, + initialDelay: const Duration(milliseconds: 400), delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart index cd8a83a..3438915 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart @@ -30,7 +30,7 @@ class _SettingsButtonState extends State with TickerProviderStat ); super.initState(); animationController = AnimationControllerWithDelays( - initialDelay: Duration.zero, + initialDelay: const Duration(milliseconds: 200), delayBetweenAnimations: animationDelay, numberOfAnimations: 1, ); From b7bb3009201bfc3f3436d59687ed6078905ced46 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:35:36 +0200 Subject: [PATCH 454/475] Create credentials bloc --- .../bloc/credentials/credentials_bloc.dart | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart diff --git a/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart new file mode 100644 index 0000000..8544c55 --- /dev/null +++ b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart @@ -0,0 +1,35 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_state.dart'; + +class CredentialsCubit extends Cubit { + CredentialsCubit() : super(const CredentialsState()); + + Future changeEmail({required String newEmail, required String oldPassword}) async{ + emit(state.copyWith(status: CredentialsStatus.submitting)); + + if(newEmail.isEmpty || oldPassword.isEmpty){ + emit(state.copyWith(status: CredentialsStatus.error, error: 'One of the fields is empty. Please fill in all fields')); + } + + try{ + final user = FirebaseAuth.instance.currentUser; + + if (user != null) { + await user.reauthenticateWithCredential( + EmailAuthProvider.credential( + email: getIt().getCurrentUser().email!, + password: oldPassword, + ), + ); + await user.updateEmail(newEmail); + } + + emit(state.copyWith(status: CredentialsStatus.success)); + } catch(e){ + emit(state.copyWith(status: CredentialsStatus.error, error: e.toString())); + } + } +} From 277bf5487b2d82c5f1e4b123f7d0318baf57a550 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:35:42 +0200 Subject: [PATCH 455/475] Create credentials state --- .../bloc/credentials/credentials_state.dart | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 lib/core/shared/presentation/bloc/credentials/credentials_state.dart diff --git a/lib/core/shared/presentation/bloc/credentials/credentials_state.dart b/lib/core/shared/presentation/bloc/credentials/credentials_state.dart new file mode 100644 index 0000000..80952a7 --- /dev/null +++ b/lib/core/shared/presentation/bloc/credentials/credentials_state.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; + +enum CredentialsStatus{initial, submitting, success, error} + +class CredentialsState extends Equatable{ + const CredentialsState({ + this.error, + this.status = CredentialsStatus.initial, + }); + + final CredentialsStatus status; + final String? error; + + CredentialsState copyWith({ + CredentialsStatus? status, + String? error, + }) => CredentialsState( + status: status ?? this.status, + error: error ?? this.error, + ); + + @override + List get props => [ + status, + ]; +} From e33ce0253909d0ac13e08232dacd10b9194a2e59 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:35:49 +0200 Subject: [PATCH 456/475] Init new bloc --- lib/core/dependency_injection.dart | 2 ++ lib/features/multi_bloc_provider/presentation/init_blocs.dart | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index ef8926b..60a8616 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -3,6 +3,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:get_it/get_it.dart'; import 'package:poetlum/core/shared/data/repository/user_repository_impl.dart'; import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_bloc.dart'; import 'package:poetlum/features/authorization/data/data_sources/remote/firebase_service.dart'; import 'package:poetlum/features/authorization/data/repository/auth_repository_impl.dart'; import 'package:poetlum/features/authorization/data/repository/firebase_repository_impl.dart'; @@ -91,6 +92,7 @@ void initializeDependencies() { // Bloc ..registerFactory(() => RemotePoemBloc(getIt(), getIt())) ..registerFactory(() => AuthCubit(getIt(), getIt())) + ..registerFactory(() => CredentialsCubit()) ..registerFactory(() => ThemeCubit(getIt(), getIt())) ..registerFactory( () => FirebaseDatabaseCubit( diff --git a/lib/features/multi_bloc_provider/presentation/init_blocs.dart b/lib/features/multi_bloc_provider/presentation/init_blocs.dart index 483699d..d3f573f 100644 --- a/lib/features/multi_bloc_provider/presentation/init_blocs.dart +++ b/lib/features/multi_bloc_provider/presentation/init_blocs.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; +import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_bloc.dart'; import 'package:poetlum/features/authorization/presentation/bloc/authorization/auth_cubit.dart'; import 'package:poetlum/features/authorization/presentation/bloc/validation/validation_cubit.dart'; import 'package:poetlum/features/poems_feed/presentation/bloc/poem/remote/remote_poem_bloc.dart'; @@ -22,6 +23,7 @@ class InitBlocs extends StatelessWidget { BlocProvider(create:(context) => getIt()), BlocProvider(create: (context) => getIt()), BlocProvider(create: (context) => getIt()), + BlocProvider(create: (context) => getIt()), ], child: child, ); From 99a13ec555d5c35e616755fe7d6ab58760563583 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:35:56 +0200 Subject: [PATCH 457/475] Implement email change --- .../app_bar/buttons/credentials_button.dart | 155 ++++++++++++++++-- 1 file changed, 138 insertions(+), 17 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index 0e51a98..4d9c971 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -1,12 +1,13 @@ import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; -import 'package:poetlum/core/dependency_injection.dart'; -import 'package:poetlum/core/shared/domain/repository/user_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_bloc.dart'; +import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_state.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; +import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; class CredentialButton extends StatefulWidget { @@ -56,7 +57,7 @@ class _CredentialButtonState extends State with TickerProvider await showModalBottomSheet( isScrollControlled: true, context: context, - builder: (context) => const _BottomSheetContent(), + builder: (context) => const _SelectBottomSheetContent(), ); }, icon: const Icon(Icons.lock), @@ -71,7 +72,123 @@ class _CredentialButtonState extends State with TickerProvider } } -class _BottomSheetContent extends StatefulWidget { +class _SelectBottomSheetContent extends StatefulWidget { + const _SelectBottomSheetContent(); + + @override + State<_SelectBottomSheetContent> createState() => __SelectBottomSheetContentState(); +} + +class __SelectBottomSheetContentState extends State<_SelectBottomSheetContent> { + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/2, + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.04), + const _Title(text: 'Choose a credential you want to edit 🐸'), + + const CustomSpacer(heightFactor: 0.04), + FilledButton.tonal( + onPressed: (){ + + }, + child: const Text('Username'), + ), + + const CustomSpacer(heightFactor: 0.04), + FilledButton.tonal( + onPressed: (){ + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _EmailBottomSheetContent(), + ); + }, + child: const Text('Email'), + ), + + const CustomSpacer(heightFactor: 0.04), + FilledButton.tonal(onPressed: (){}, child: const Text('Password')), + + const CustomSpacer(heightFactor: 0.04), + ], + ), + ), + ); +} + +class _EmailBottomSheetContent extends StatefulWidget { + const _EmailBottomSheetContent(); + + @override + State<_EmailBottomSheetContent> createState() => __EmailBottomSheetContentState(); +} + +class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { + final TextEditingController _oldPasswordController = TextEditingController(); + final TextEditingController _newEmailController = TextEditingController(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/1.25, + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'Confirm password'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'Password', controller: _oldPasswordController), + + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'New Email'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'New Email', controller: _newEmailController), + + const CustomSpacer(heightFactor: 0.04), + BlocConsumer( + listener: (context, state) { + if(state.status == CredentialsStatus.success){ + ToastManager.showPositiveToast('Your email has been successfully changed'); + } + if(state.status == CredentialsStatus.error){ + ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); + } + }, + builder: (context, state) { + if(state.status == CredentialsStatus.submitting){ + return const Center(child: CircularProgressIndicator()); + } else{ + return FilledButton( + onPressed: () => context.read().changeEmail( + newEmail: _newEmailController.text.trim(), + oldPassword: _oldPasswordController.text, + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: Text('Change'), + ), + ); + } + }, + ), + + const CustomSpacer(heightFactor: 0.04), + ], + ), + ), + ); +} + +/*class _BottomSheetContent extends StatefulWidget { const _BottomSheetContent(); @override @@ -126,29 +243,29 @@ class _BottomSheetContentState extends State<_BottomSheetContent> { const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'Old Email'), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'Email', controller: _oldEmailController), + EmailTextField(controller: _oldEmailController), const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'Old Password'), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'Password', controller: _oldPasswordController), + PasswordTextField(controller: _oldPasswordController), const Divider(), const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'Username'), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'Username', controller: _usernameController), + UsernameTextField(controller: _usernameController), const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'Email'), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'Email', controller: _emailController), + EmailTextField(controller: _emailController), const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'Password'), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'Password', controller: _passwordController), + PasswordTextField(controller: _passwordController), const CustomSpacer(heightFactor: 0.04), FilledButton( @@ -159,12 +276,13 @@ class _BottomSheetContentState extends State<_BottomSheetContent> { /*if(_usernameController.text.isNotEmpty){ await user.updateDisplayName(_usernameController.text.trim()); }*/ + print(user.emailVerified); if(_emailController.text.isNotEmpty){ await user.reauthenticateWithCredential( EmailAuthProvider.credential(email: _oldEmailController.text.trim(), password: _oldPasswordController.text.trim()) ); await user.updateEmail(_emailController.text.trim()); - await user.sendEmailVerification(); + //await user.sendEmailVerification(); } /*if(_passwordController.text.isNotEmpty){ await user.reauthenticateWithCredential( @@ -185,7 +303,7 @@ class _BottomSheetContentState extends State<_BottomSheetContent> { ), ), ); -} +}*/ class _Title extends StatelessWidget { const _Title({required this.text}); @@ -193,11 +311,14 @@ class _Title extends StatelessWidget { final String text; @override - Widget build(BuildContext context) => Text( - text, - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Text( + text, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), ), ); } From 0824cbac01d04cd203c15bf344b29d69bd33c7d1 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:42:44 +0200 Subject: [PATCH 458/475] Dispose controllers --- .../widgets/app_bar/buttons/credentials_button.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index 4d9c971..d0b9343 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -132,8 +132,10 @@ class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { final TextEditingController _newEmailController = TextEditingController(); @override - void initState() { - super.initState(); + void dispose(){ + super.dispose(); + _oldPasswordController.dispose(); + _newEmailController.dispose(); } @override From fc04aadd8c91e569b3ca708b3ec2f95971956830 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:45:33 +0200 Subject: [PATCH 459/475] Add title --- .../widgets/app_bar/buttons/credentials_button.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index d0b9343..c7a1c57 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -145,6 +145,9 @@ class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { child: SingleChildScrollView( child: Column( children: [ + const CustomSpacer(heightFactor: 0.04), + const _Title(text: 'Change your email'), + const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'Confirm password'), const CustomSpacer(heightFactor: 0.01), From 2198e45e9d8f93f06aa292c03fb9b13377a92704 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:01:26 +0200 Subject: [PATCH 460/475] Add an ability to change username --- .../bloc/credentials/credentials_bloc.dart | 30 ++++++- .../app_bar/buttons/credentials_button.dart | 85 ++++++++++++++++--- 2 files changed, 104 insertions(+), 11 deletions(-) diff --git a/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart index 8544c55..1ffb017 100644 --- a/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart +++ b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart @@ -12,6 +12,8 @@ class CredentialsCubit extends Cubit { if(newEmail.isEmpty || oldPassword.isEmpty){ emit(state.copyWith(status: CredentialsStatus.error, error: 'One of the fields is empty. Please fill in all fields')); + + return; } try{ @@ -25,9 +27,35 @@ class CredentialsCubit extends Cubit { ), ); await user.updateEmail(newEmail); + + emit(state.copyWith(status: CredentialsStatus.success)); + } else{ + emit(state.copyWith(status: CredentialsStatus.error, error: 'No user found')); } + } catch(e){ + emit(state.copyWith(status: CredentialsStatus.error, error: e.toString())); + } + } + + Future changeUsername({required String newUsername}) async{ + emit(state.copyWith(status: CredentialsStatus.submitting)); + + if(newUsername.isEmpty){ + emit(state.copyWith(status: CredentialsStatus.error, error: 'The username field is empty. Please fill it')); - emit(state.copyWith(status: CredentialsStatus.success)); + return; + } + + try{ + final user = FirebaseAuth.instance.currentUser; + + if (user != null) { + await user.updateDisplayName(newUsername); + + emit(state.copyWith(status: CredentialsStatus.success)); + } else{ + emit(state.copyWith(status: CredentialsStatus.error, error: 'No user found')); + } } catch(e){ emit(state.copyWith(status: CredentialsStatus.error, error: e.toString())); } diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index c7a1c57..6dc1472 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -92,21 +92,21 @@ class __SelectBottomSheetContentState extends State<_SelectBottomSheetContent> { const CustomSpacer(heightFactor: 0.04), FilledButton.tonal( - onPressed: (){ - - }, + onPressed: () => showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _UsernameBottomSheetContent(), + ), child: const Text('Username'), ), const CustomSpacer(heightFactor: 0.04), FilledButton.tonal( - onPressed: (){ - showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: (context) => const _EmailBottomSheetContent(), - ); - }, + onPressed: () => showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _EmailBottomSheetContent(), + ), child: const Text('Email'), ), @@ -120,6 +120,71 @@ class __SelectBottomSheetContentState extends State<_SelectBottomSheetContent> { ); } +class _UsernameBottomSheetContent extends StatefulWidget { + const _UsernameBottomSheetContent(); + + @override + State<_UsernameBottomSheetContent> createState() => __UsernameBottomSheetContentState(); +} + +class __UsernameBottomSheetContentState extends State<_UsernameBottomSheetContent> { + final TextEditingController _newUsernameController = TextEditingController(); + + @override + void dispose(){ + super.dispose(); + _newUsernameController.dispose(); + } + + @override + Widget build(BuildContext context) => SizedBox( + height: MediaQuery.of(context).size.height/1.25, + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Column( + children: [ + const CustomSpacer(heightFactor: 0.04), + const _Title(text: 'Change your username'), + + const CustomSpacer(heightFactor: 0.04), + const _SubTitle(text: 'New Username'), + const CustomSpacer(heightFactor: 0.01), + CustomTextField(hintText: 'New Username', controller: _newUsernameController), + + const CustomSpacer(heightFactor: 0.04), + BlocConsumer( + listener: (context, state) { + if(state.status == CredentialsStatus.success){ + ToastManager.showPositiveToast('Your email has been successfully changed'); + } + if(state.status == CredentialsStatus.error){ + ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); + } + }, + builder: (context, state) { + if(state.status == CredentialsStatus.submitting){ + return const Center(child: CircularProgressIndicator()); + } else{ + return FilledButton( + onPressed: () => context.read().changeUsername( + newUsername: _newUsernameController.text.trim(), + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: Text('Change'), + ), + ); + } + }, + ), + + const CustomSpacer(heightFactor: 0.04), + ], + ), + ), + ); +} + class _EmailBottomSheetContent extends StatefulWidget { const _EmailBottomSheetContent(); From 1d73aefd3b6a202e6e39e0bca9842bf5b44ecf0e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:54:24 +0200 Subject: [PATCH 461/475] Create Custom PasswordTextField widget --- .../widgets/password_textfield.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/core/shared/presentation/widgets/password_textfield.dart diff --git a/lib/core/shared/presentation/widgets/password_textfield.dart b/lib/core/shared/presentation/widgets/password_textfield.dart new file mode 100644 index 0000000..0b489c1 --- /dev/null +++ b/lib/core/shared/presentation/widgets/password_textfield.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class CustomPasswordTextField extends StatefulWidget { + const CustomPasswordTextField({super.key, required this.controller, required this.widthFactor, required this.hintText}); + + final TextEditingController controller; + final double widthFactor; + final String hintText; + + @override + State createState() => _CustomPasswordTextFieldState(); +} + +class _CustomPasswordTextFieldState extends State { + bool _isPasswordVisible = false; + + @override + Widget build(BuildContext context) => SizedBox( + width: MediaQuery.of(context).size.width/widget.widthFactor, + child: TextField( + controller: widget.controller, + obscureText: !_isPasswordVisible, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + errorMaxLines: 3, + border: const OutlineInputBorder(), + hintText: widget.hintText, + hintStyle: const TextStyle( + color: Colors.grey, + ), + suffixIcon: IconButton( + icon: Icon( + _isPasswordVisible + ? Icons.visibility + : Icons.visibility_off, + ), + onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), + ), + ), + ), + ); +} \ No newline at end of file From d17a7d00cf85b090d179fb46071aa697984f276c Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:54:40 +0200 Subject: [PATCH 462/475] Create changePassword function --- .../bloc/credentials/credentials_bloc.dart | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart index 1ffb017..ae530d5 100644 --- a/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart +++ b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart @@ -60,4 +60,34 @@ class CredentialsCubit extends Cubit { emit(state.copyWith(status: CredentialsStatus.error, error: e.toString())); } } + + Future changePassword({required String oldPassword, required String newPassword}) async{ + emit(state.copyWith(status: CredentialsStatus.submitting)); + + if(oldPassword.isEmpty || newPassword.isEmpty){ + emit(state.copyWith(status: CredentialsStatus.error, error: 'One of the fields is empty. Please fill in all fields')); + + return; + } + + try{ + final user = FirebaseAuth.instance.currentUser; + + if (user != null) { + await user.reauthenticateWithCredential( + EmailAuthProvider.credential( + email: getIt().getCurrentUser().email!, + password: oldPassword + ), + ); + await user.updatePassword(newPassword); + + emit(state.copyWith(status: CredentialsStatus.success)); + } else{ + emit(state.copyWith(status: CredentialsStatus.error, error: 'No user found')); + } + } catch(e){ + emit(state.copyWith(status: CredentialsStatus.error, error: e.toString())); + } + } } From 2d200129d57a7484c116b2878f01c7c74af6ca52 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:54:56 +0200 Subject: [PATCH 463/475] Add an ability to change the password --- .../app_bar/buttons/credentials_button.dart | 138 +++++++----------- 1 file changed, 51 insertions(+), 87 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index 6dc1472..80c1e02 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -6,6 +6,7 @@ import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_st import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; +import 'package:poetlum/core/shared/presentation/widgets/password_textfield.dart'; import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; import 'package:poetlum/core/shared/presentation/widgets/toast_manager.dart'; import 'package:poetlum/features/application/presentation/widgets/drawer/custom_textfield.dart'; @@ -111,7 +112,14 @@ class __SelectBottomSheetContentState extends State<_SelectBottomSheetContent> { ), const CustomSpacer(heightFactor: 0.04), - FilledButton.tonal(onPressed: (){}, child: const Text('Password')), + FilledButton.tonal( + onPressed: () => showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _PasswordBottomSheetContent(), + ), + child: const Text('Password'), + ), const CustomSpacer(heightFactor: 0.04), ], @@ -216,7 +224,7 @@ class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'Confirm password'), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'Password', controller: _oldPasswordController), + CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Password'), const CustomSpacer(heightFactor: 0.04), const _SubTitle(text: 'New Email'), @@ -258,46 +266,22 @@ class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { ); } -/*class _BottomSheetContent extends StatefulWidget { - const _BottomSheetContent(); +class _PasswordBottomSheetContent extends StatefulWidget { + const _PasswordBottomSheetContent(); @override - State<_BottomSheetContent> createState() => _BottomSheetContentState(); + State<_PasswordBottomSheetContent> createState() => __PasswordBottomSheetContentState(); } -class _BottomSheetContentState extends State<_BottomSheetContent> { - bool isHeaderAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 125); - - final TextEditingController _oldEmailController = TextEditingController(); +class __PasswordBottomSheetContentState extends State<_PasswordBottomSheetContent> { final TextEditingController _oldPasswordController = TextEditingController(); - final TextEditingController _emailController = TextEditingController(); - final TextEditingController _passwordController = TextEditingController(); - final TextEditingController _usernameController = TextEditingController(); + final TextEditingController _newPasswordController = TextEditingController(); @override - void initState() { - super.initState(); - _startAnimations(); - _initCredentials(); - } - - void _initCredentials(){ - _emailController.text = getIt().getCurrentUser().email ?? ''; - _oldEmailController.text = getIt().getCurrentUser().email ?? ''; - _usernameController.text = getIt().getCurrentUser().username ?? ''; - } - - void _startAnimations() { - final setters = [ - (val) => isHeaderAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } + void dispose(){ + super.dispose(); + _oldPasswordController.dispose(); + _newPasswordController.dispose(); } @override @@ -308,72 +292,52 @@ class _BottomSheetContentState extends State<_BottomSheetContent> { child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - const _Title(text: 'Edit credentials 🐸'), - - const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'Old Email'), - const CustomSpacer(heightFactor: 0.01), - EmailTextField(controller: _oldEmailController), - - const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'Old Password'), - const CustomSpacer(heightFactor: 0.01), - PasswordTextField(controller: _oldPasswordController), - - const Divider(), - - const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'Username'), - const CustomSpacer(heightFactor: 0.01), - UsernameTextField(controller: _usernameController), + const _Title(text: 'Change your password'), const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'Email'), + const _SubTitle(text: 'Confirm password'), const CustomSpacer(heightFactor: 0.01), - EmailTextField(controller: _emailController), + CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Old Password'), const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'Password'), + const _SubTitle(text: 'New Password'), const CustomSpacer(heightFactor: 0.01), - PasswordTextField(controller: _passwordController), + CustomPasswordTextField(controller: _newPasswordController, widthFactor: 1.35, hintText: 'New Password'), const CustomSpacer(heightFactor: 0.04), - FilledButton( - onPressed: () async { - final user = FirebaseAuth.instance.currentUser; - - if (user != null) { - /*if(_usernameController.text.isNotEmpty){ - await user.updateDisplayName(_usernameController.text.trim()); - }*/ - print(user.emailVerified); - if(_emailController.text.isNotEmpty){ - await user.reauthenticateWithCredential( - EmailAuthProvider.credential(email: _oldEmailController.text.trim(), password: _oldPasswordController.text.trim()) - ); - await user.updateEmail(_emailController.text.trim()); - //await user.sendEmailVerification(); - } - /*if(_passwordController.text.isNotEmpty){ - await user.reauthenticateWithCredential( - EmailAuthProvider.credential(email: _oldEmailController.text.trim(), password: _oldPasswordController.text.trim()) - ); - await user.updatePassword(_passwordController.text.trim()); - }*/ - - //await user.reload(); + BlocConsumer( + listener: (context, state) { + if(state.status == CredentialsStatus.success){ + ToastManager.showPositiveToast('Your password has been successfully changed'); } - }, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), - child: Text('Change'), - ), + if(state.status == CredentialsStatus.error){ + ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); + } + }, + builder: (context, state) { + if(state.status == CredentialsStatus.submitting){ + return const Center(child: CircularProgressIndicator()); + } else{ + return FilledButton( + onPressed: () => context.read().changePassword( + newPassword: _newPasswordController.text, + oldPassword: _oldPasswordController.text, + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: Text('Change'), + ), + ); + } + }, ), + + const CustomSpacer(heightFactor: 0.04), ], ), ), ); -}*/ +} class _Title extends StatelessWidget { const _Title({required this.text}); From 72f7c26fc1596dbe0c7714341fc9ef91d78a5366 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:02:49 +0200 Subject: [PATCH 464/475] Update linter rules --- analysis_options.yaml | 5 ----- .../presentation/widgets/app_bar/buttons/refresh_button.dart | 2 -- .../widgets/app_bar/buttons/settings_button.dart | 2 -- .../shared/presentation/widgets/poem_card/poem_card.dart | 2 -- .../presentation/widgets/drawer/custom_drawer.dart | 2 -- .../presentation/bloc/validation/validators.dart | 2 -- .../authorization/presentation/pages/login/login_page.dart | 2 -- .../presentation/pages/registration/registration_page.dart | 2 -- .../poems_feed/presentation/pages/poem_view/poem_view.dart | 2 -- .../data/data_sources/remote/firebase_api_service.dart | 2 -- .../presentation/pages/saved_collection_view_page.dart | 2 -- .../saved_poems/presentation/pages/saved_poem_view_page.dart | 2 -- .../saved_poems/presentation/pages/write_poem_page.dart | 2 +- .../saved_poems/presentation/screens/saved_poems_screen.dart | 2 +- .../saved_poems/presentation/widgets/collection_card.dart | 2 -- .../presentation/widgets/create_collection_bottom_sheet.dart | 2 -- .../saved_poems/presentation/widgets/saved_poem_card.dart | 2 -- .../widgets/update_collection_bottom_sheet_content.dart | 2 -- 18 files changed, 2 insertions(+), 37 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index e796f70..63d773f 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -19,7 +19,6 @@ linter: - unnecessary_late - null_check_on_nullable_type_parameter - use_super_parameters - - avoid_dynamic_calls - always_use_package_imports - avoid_returning_null_for_future - avoid_slow_async_io @@ -38,7 +37,6 @@ linter: - avoid_field_initializers_in_const_classes - avoid_final_parameters - avoid_multiple_declarations_per_line - - avoid_positional_boolean_parameters - avoid_private_typedef_functions - avoid_redundant_argument_values - avoid_returning_null @@ -47,7 +45,6 @@ linter: - avoid_types_on_closure_parameters - avoid_unused_constructor_parameters - avoid_void_async - - cascade_invocations - deprecated_consistency - directives_ordering - do_not_use_environment @@ -65,8 +62,6 @@ linter: - prefer_asserts_in_initializer_lists - prefer_constructors_over_static_methods - prefer_expression_function_bodies - - prefer_final_in_for_each - - prefer_final_locals - prefer_if_elements_to_conditional_expressions - prefer_interpolation_to_compose_strings - prefer_null_aware_method_calls diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart index 6e2cfad..271cee7 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/refresh_button.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart index 3438915..7ab8269 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; diff --git a/lib/core/shared/presentation/widgets/poem_card/poem_card.dart b/lib/core/shared/presentation/widgets/poem_card/poem_card.dart index 55742f0..9fdae72 100644 --- a/lib/core/shared/presentation/widgets/poem_card/poem_card.dart +++ b/lib/core/shared/presentation/widgets/poem_card/poem_card.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; diff --git a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart index f225264..2f78c4a 100644 --- a/lib/features/application/presentation/widgets/drawer/custom_drawer.dart +++ b/lib/features/application/presentation/widgets/drawer/custom_drawer.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/authorization/presentation/bloc/validation/validators.dart b/lib/features/authorization/presentation/bloc/validation/validators.dart index 5ad4c39..e2332bf 100644 --- a/lib/features/authorization/presentation/bloc/validation/validators.dart +++ b/lib/features/authorization/presentation/bloc/validation/validators.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:email_validator/email_validator.dart'; abstract class Validator { diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 158e8a7..95fbb58 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 1ae74d2..36d8246 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'dart:async'; import 'package:flutter/material.dart'; diff --git a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart index 4f99a9f..541d6ac 100644 --- a/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart +++ b/lib/features/poems_feed/presentation/pages/poem_view/poem_view.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; diff --git a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart index 4792f82..32fef06 100644 --- a/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart +++ b/lib/features/saved_poems/data/data_sources/remote/firebase_api_service.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_dynamic_calls, cascade_invocations, prefer_final_in_for_each - import 'package:firebase_database/firebase_database.dart'; import 'package:poetlum/core/shared/data/models/poem.dart'; import 'package:poetlum/features/poems_feed/domain/entities/poem.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart index 55e1669..fc3478f 100644 --- a/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_collection_view_page.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/dependency_injection.dart'; diff --git a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart index 1702d7f..3a7fd50 100644 --- a/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart +++ b/lib/features/saved_poems/presentation/pages/saved_poem_view_page.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:flutter/material.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; diff --git a/lib/features/saved_poems/presentation/pages/write_poem_page.dart b/lib/features/saved_poems/presentation/pages/write_poem_page.dart index cab349e..9216d53 100644 --- a/lib/features/saved_poems/presentation/pages/write_poem_page.dart +++ b/lib/features/saved_poems/presentation/pages/write_poem_page.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously, avoid_positional_boolean_parameters +// ignore_for_file: use_build_context_synchronously import 'dart:async'; diff --git a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart index 6627fd2..63f16d1 100644 --- a/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart +++ b/lib/features/saved_poems/presentation/screens/saved_poems_screen.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously, prefer_final_locals +// ignore_for_file: use_build_context_synchronously import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/collection_card.dart b/lib/features/saved_poems/presentation/widgets/collection_card.dart index 1f89039..f2948bd 100644 --- a/lib/features/saved_poems/presentation/widgets/collection_card.dart +++ b/lib/features/saved_poems/presentation/widgets/collection_card.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index 70b3b81..f488abe 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -1,5 +1,3 @@ -// ignore_for_file: use_build_context_synchronously, avoid_positional_boolean_parameters - import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart index 55b1e0d..ff319db 100644 --- a/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart +++ b/lib/features/saved_poems/presentation/widgets/saved_poem_card.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; diff --git a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart index 5119bfa..144c682 100644 --- a/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart +++ b/lib/features/saved_poems/presentation/widgets/update_collection_bottom_sheet_content.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_positional_boolean_parameters - import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; From ce71f73bd362d1d294cd9cc654304aa38d2a80fd Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:32:38 +0200 Subject: [PATCH 465/475] Add animations --- .../app_bar/buttons/credentials_button.dart | 368 +++++++++++++----- 1 file changed, 260 insertions(+), 108 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index 80c1e02..b631599 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -81,6 +81,24 @@ class _SelectBottomSheetContent extends StatefulWidget { } class __SelectBottomSheetContentState extends State<_SelectBottomSheetContent> { + late AnimationControllerWithDelays animationController; + final Duration animationDelay = const Duration(milliseconds: 150); + + @override + void initState() { + super.initState(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 4, + ); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); + } + @override Widget build(BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height/2, @@ -89,36 +107,52 @@ class __SelectBottomSheetContentState extends State<_SelectBottomSheetContent> { child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - const _Title(text: 'Choose a credential you want to edit 🐸'), + RightAnimation( + animationField: animationController.animationStates[0], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _Title(text: 'Choose a credential you want to edit 🐸') + ), const CustomSpacer(heightFactor: 0.04), - FilledButton.tonal( - onPressed: () => showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: (context) => const _UsernameBottomSheetContent(), + RightAnimation( + animationField: animationController.animationStates[1], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: FilledButton.tonal( + onPressed: () => showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _UsernameBottomSheetContent(), + ), + child: const Text('Username'), ), - child: const Text('Username'), ), const CustomSpacer(heightFactor: 0.04), - FilledButton.tonal( - onPressed: () => showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: (context) => const _EmailBottomSheetContent(), - ), - child: const Text('Email'), + RightAnimation( + animationField: animationController.animationStates[2], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: FilledButton.tonal( + onPressed: () => showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _EmailBottomSheetContent(), + ), + child: const Text('Email'), + ), ), const CustomSpacer(heightFactor: 0.04), - FilledButton.tonal( - onPressed: () => showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: (context) => const _PasswordBottomSheetContent(), - ), - child: const Text('Password'), + RightAnimation( + animationField: animationController.animationStates[3], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: FilledButton.tonal( + onPressed: () => showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => const _PasswordBottomSheetContent(), + ), + child: const Text('Password'), + ), ), const CustomSpacer(heightFactor: 0.04), @@ -138,6 +172,24 @@ class _UsernameBottomSheetContent extends StatefulWidget { class __UsernameBottomSheetContentState extends State<_UsernameBottomSheetContent> { final TextEditingController _newUsernameController = TextEditingController(); + late AnimationControllerWithDelays animationController; + final Duration animationDelay = const Duration(milliseconds: 150); + + @override + void initState() { + super.initState(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 4, + ); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); + } + @override void dispose(){ super.dispose(); @@ -152,38 +204,54 @@ class __UsernameBottomSheetContentState extends State<_UsernameBottomSheetConten child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - const _Title(text: 'Change your username'), + RightAnimation( + animationField: animationController.animationStates[0], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _Title(text: 'Change your username'), + ), const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'New Username'), + RightAnimation( + animationField: animationController.animationStates[1], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _SubTitle(text: 'New Username'), + ), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'New Username', controller: _newUsernameController), + RightAnimation( + animationField: animationController.animationStates[2], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: CustomTextField(hintText: 'New Username', controller: _newUsernameController), + ), const CustomSpacer(heightFactor: 0.04), - BlocConsumer( - listener: (context, state) { - if(state.status == CredentialsStatus.success){ - ToastManager.showPositiveToast('Your email has been successfully changed'); - } - if(state.status == CredentialsStatus.error){ - ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); - } - }, - builder: (context, state) { - if(state.status == CredentialsStatus.submitting){ - return const Center(child: CircularProgressIndicator()); - } else{ - return FilledButton( - onPressed: () => context.read().changeUsername( - newUsername: _newUsernameController.text.trim(), - ), - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), - child: Text('Change'), - ), - ); - } - }, + RightAnimation( + animationField: animationController.animationStates[3], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: BlocConsumer( + listener: (context, state) { + if(state.status == CredentialsStatus.success){ + ToastManager.showPositiveToast('Your email has been successfully changed'); + } + if(state.status == CredentialsStatus.error){ + ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); + } + }, + builder: (context, state) { + if(state.status == CredentialsStatus.submitting){ + return const Center(child: CircularProgressIndicator()); + } else{ + return FilledButton( + onPressed: () => context.read().changeUsername( + newUsername: _newUsernameController.text.trim(), + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: Text('Change'), + ), + ); + } + }, + ), ), const CustomSpacer(heightFactor: 0.04), @@ -204,6 +272,24 @@ class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { final TextEditingController _oldPasswordController = TextEditingController(); final TextEditingController _newEmailController = TextEditingController(); + late AnimationControllerWithDelays animationController; + final Duration animationDelay = const Duration(milliseconds: 150); + + @override + void initState() { + super.initState(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 6, + ); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); + } + @override void dispose(){ super.dispose(); @@ -219,44 +305,68 @@ class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - const _Title(text: 'Change your email'), + RightAnimation( + animationField: animationController.animationStates[0], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _Title(text: 'Change your email'), + ), const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'Confirm password'), + RightAnimation( + animationField: animationController.animationStates[1], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _SubTitle(text: 'Confirm password'), + ), const CustomSpacer(heightFactor: 0.01), - CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Password'), + RightAnimation( + animationField: animationController.animationStates[2], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Password'), + ), const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'New Email'), + RightAnimation( + animationField: animationController.animationStates[3], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _SubTitle(text: 'New Email'), + ), const CustomSpacer(heightFactor: 0.01), - CustomTextField(hintText: 'New Email', controller: _newEmailController), + RightAnimation( + animationField: animationController.animationStates[4], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: CustomTextField(hintText: 'New Email', controller: _newEmailController), + ), const CustomSpacer(heightFactor: 0.04), - BlocConsumer( - listener: (context, state) { - if(state.status == CredentialsStatus.success){ - ToastManager.showPositiveToast('Your email has been successfully changed'); - } - if(state.status == CredentialsStatus.error){ - ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); - } - }, - builder: (context, state) { - if(state.status == CredentialsStatus.submitting){ - return const Center(child: CircularProgressIndicator()); - } else{ - return FilledButton( - onPressed: () => context.read().changeEmail( - newEmail: _newEmailController.text.trim(), - oldPassword: _oldPasswordController.text, - ), - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), - child: Text('Change'), - ), - ); - } - }, + RightAnimation( + animationField: animationController.animationStates[5], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: BlocConsumer( + listener: (context, state) { + if(state.status == CredentialsStatus.success){ + ToastManager.showPositiveToast('Your email has been successfully changed'); + } + if(state.status == CredentialsStatus.error){ + ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); + } + }, + builder: (context, state) { + if(state.status == CredentialsStatus.submitting){ + return const Center(child: CircularProgressIndicator()); + } else{ + return FilledButton( + onPressed: () => context.read().changeEmail( + newEmail: _newEmailController.text.trim(), + oldPassword: _oldPasswordController.text, + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: Text('Change'), + ), + ); + } + }, + ), ), const CustomSpacer(heightFactor: 0.04), @@ -277,6 +387,24 @@ class __PasswordBottomSheetContentState extends State<_PasswordBottomSheetConten final TextEditingController _oldPasswordController = TextEditingController(); final TextEditingController _newPasswordController = TextEditingController(); + late AnimationControllerWithDelays animationController; + final Duration animationDelay = const Duration(milliseconds: 150); + + @override + void initState() { + super.initState(); + animationController = AnimationControllerWithDelays( + initialDelay: animationDelay, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 6, + ); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); + } + @override void dispose(){ super.dispose(); @@ -292,44 +420,68 @@ class __PasswordBottomSheetContentState extends State<_PasswordBottomSheetConten child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - const _Title(text: 'Change your password'), + RightAnimation( + animationField: animationController.animationStates[0], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _Title(text: 'Change your password') + ), const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'Confirm password'), + RightAnimation( + animationField: animationController.animationStates[1], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _SubTitle(text: 'Confirm password') + ), const CustomSpacer(heightFactor: 0.01), - CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Old Password'), + RightAnimation( + animationField: animationController.animationStates[2], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Old Password') + ), const CustomSpacer(heightFactor: 0.04), - const _SubTitle(text: 'New Password'), + RightAnimation( + animationField: animationController.animationStates[3], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: const _SubTitle(text: 'New Password') + ), const CustomSpacer(heightFactor: 0.01), - CustomPasswordTextField(controller: _newPasswordController, widthFactor: 1.35, hintText: 'New Password'), + RightAnimation( + animationField: animationController.animationStates[4], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: CustomPasswordTextField(controller: _newPasswordController, widthFactor: 1.35, hintText: 'New Password') + ), const CustomSpacer(heightFactor: 0.04), - BlocConsumer( - listener: (context, state) { - if(state.status == CredentialsStatus.success){ - ToastManager.showPositiveToast('Your password has been successfully changed'); - } - if(state.status == CredentialsStatus.error){ - ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); - } - }, - builder: (context, state) { - if(state.status == CredentialsStatus.submitting){ - return const Center(child: CircularProgressIndicator()); - } else{ - return FilledButton( - onPressed: () => context.read().changePassword( - newPassword: _newPasswordController.text, - oldPassword: _oldPasswordController.text, - ), - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), - child: Text('Change'), - ), - ); - } - }, + RightAnimation( + animationField: animationController.animationStates[5], + positionInitialValue: MediaQuery.of(context).size.width/6, + child: BlocConsumer( + listener: (context, state) { + if(state.status == CredentialsStatus.success){ + ToastManager.showPositiveToast('Your password has been successfully changed'); + } + if(state.status == CredentialsStatus.error){ + ToastManager.showNegativeToast(state.error ?? 'An error occured 😥'); + } + }, + builder: (context, state) { + if(state.status == CredentialsStatus.submitting){ + return const Center(child: CircularProgressIndicator()); + } else{ + return FilledButton( + onPressed: () => context.read().changePassword( + newPassword: _newPasswordController.text, + oldPassword: _oldPasswordController.text, + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: Text('Change'), + ), + ); + } + }, + ), ), const CustomSpacer(heightFactor: 0.04), From b410b8c036bdf5ecb39f9cad54806b60eb89c60b Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:33:26 +0200 Subject: [PATCH 466/475] Code enhancement --- .../shared/presentation/bloc/credentials/credentials_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart index ae530d5..1e30bbf 100644 --- a/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart +++ b/lib/core/shared/presentation/bloc/credentials/credentials_bloc.dart @@ -77,7 +77,7 @@ class CredentialsCubit extends Cubit { await user.reauthenticateWithCredential( EmailAuthProvider.credential( email: getIt().getCurrentUser().email!, - password: oldPassword + password: oldPassword, ), ); await user.updatePassword(newPassword); From 2fae9ac798edf1e7a1953e9a176ef4c155a77c7e Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:33:31 +0200 Subject: [PATCH 467/475] Code enhancement --- lib/core/shared/presentation/widgets/password_textfield.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/shared/presentation/widgets/password_textfield.dart b/lib/core/shared/presentation/widgets/password_textfield.dart index 0b489c1..6b66e45 100644 --- a/lib/core/shared/presentation/widgets/password_textfield.dart +++ b/lib/core/shared/presentation/widgets/password_textfield.dart @@ -40,4 +40,4 @@ class _CustomPasswordTextFieldState extends State { ), ), ); -} \ No newline at end of file +} From 36b850aac8b53ceb8c4d0652759f617b41afa6fc Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:33:36 +0200 Subject: [PATCH 468/475] Code enhancement --- .../widgets/app_bar/buttons/credentials_button.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index b631599..2a713f1 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -110,7 +110,7 @@ class __SelectBottomSheetContentState extends State<_SelectBottomSheetContent> { RightAnimation( animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/6, - child: const _Title(text: 'Choose a credential you want to edit 🐸') + child: const _Title(text: 'Choose a credential you want to edit 🐸'), ), const CustomSpacer(heightFactor: 0.04), @@ -423,33 +423,33 @@ class __PasswordBottomSheetContentState extends State<_PasswordBottomSheetConten RightAnimation( animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/6, - child: const _Title(text: 'Change your password') + child: const _Title(text: 'Change your password'), ), const CustomSpacer(heightFactor: 0.04), RightAnimation( animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.width/6, - child: const _SubTitle(text: 'Confirm password') + child: const _SubTitle(text: 'Confirm password'), ), const CustomSpacer(heightFactor: 0.01), RightAnimation( animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.width/6, - child: CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Old Password') + child: CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Old Password'), ), const CustomSpacer(heightFactor: 0.04), RightAnimation( animationField: animationController.animationStates[3], positionInitialValue: MediaQuery.of(context).size.width/6, - child: const _SubTitle(text: 'New Password') + child: const _SubTitle(text: 'New Password'), ), const CustomSpacer(heightFactor: 0.01), RightAnimation( animationField: animationController.animationStates[4], positionInitialValue: MediaQuery.of(context).size.width/6, - child: CustomPasswordTextField(controller: _newPasswordController, widthFactor: 1.35, hintText: 'New Password') + child: CustomPasswordTextField(controller: _newPasswordController, widthFactor: 1.35, hintText: 'New Password'), ), const CustomSpacer(heightFactor: 0.04), From 4f8e8dcc8bf0ac4a7615da9ab876f06c6b444686 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:33:47 +0200 Subject: [PATCH 469/475] Ignore linter rule --- .../presentation/widgets/create_collection_bottom_sheet.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart index f488abe..f222685 100644 --- a/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart +++ b/lib/features/saved_poems/presentation/widgets/create_collection_bottom_sheet.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'dart:async'; import 'package:firebase_analytics/firebase_analytics.dart'; From 64330bb85efb1a8f4e3b0dc0150eb3f4c91ec736 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:38:48 +0200 Subject: [PATCH 470/475] Change animations --- .../app_bar/buttons/credentials_button.dart | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart index 2a713f1..b647de7 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/credentials_button.dart @@ -5,6 +5,7 @@ import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_bl import 'package:poetlum/core/shared/presentation/bloc/credentials/credentials_state.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/animation_controller.dart'; import 'package:poetlum/core/shared/presentation/widgets/animations/right_animation.dart'; +import 'package:poetlum/core/shared/presentation/widgets/animations/top_animation.dart'; import 'package:poetlum/core/shared/presentation/widgets/custom_spacer.dart'; import 'package:poetlum/core/shared/presentation/widgets/password_textfield.dart'; import 'package:poetlum/core/shared/presentation/widgets/rotating_button_mixin.dart'; @@ -204,29 +205,29 @@ class __UsernameBottomSheetContentState extends State<_UsernameBottomSheetConten child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[0], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _Title(text: 'Change your username'), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[1], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _SubTitle(text: 'New Username'), ), const CustomSpacer(heightFactor: 0.01), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[2], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomTextField(hintText: 'New Username', controller: _newUsernameController), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[3], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: BlocConsumer( listener: (context, state) { if(state.status == CredentialsStatus.success){ @@ -305,42 +306,42 @@ class __EmailBottomSheetContentState extends State<_EmailBottomSheetContent> { child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[0], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _Title(text: 'Change your email'), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[1], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _SubTitle(text: 'Confirm password'), ), const CustomSpacer(heightFactor: 0.01), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[2], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Password'), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[3], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _SubTitle(text: 'New Email'), ), const CustomSpacer(heightFactor: 0.01), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[4], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomTextField(hintText: 'New Email', controller: _newEmailController), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[5], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: BlocConsumer( listener: (context, state) { if(state.status == CredentialsStatus.success){ @@ -420,42 +421,42 @@ class __PasswordBottomSheetContentState extends State<_PasswordBottomSheetConten child: Column( children: [ const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[0], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _Title(text: 'Change your password'), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[1], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _SubTitle(text: 'Confirm password'), ), const CustomSpacer(heightFactor: 0.01), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[2], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomPasswordTextField(controller: _oldPasswordController, widthFactor: 1.35, hintText: 'Old Password'), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[3], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: const _SubTitle(text: 'New Password'), ), const CustomSpacer(heightFactor: 0.01), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[4], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: CustomPasswordTextField(controller: _newPasswordController, widthFactor: 1.35, hintText: 'New Password'), ), const CustomSpacer(heightFactor: 0.04), - RightAnimation( + TopAnimation( animationField: animationController.animationStates[5], - positionInitialValue: MediaQuery.of(context).size.width/6, + positionInitialValue: MediaQuery.of(context).size.height/14, child: BlocConsumer( listener: (context, state) { if(state.status == CredentialsStatus.success){ From aca305193435c4c8d909b05d53a0f6db848445a3 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:46:14 +0200 Subject: [PATCH 471/475] Change animation logic --- .../app_bar/buttons/settings_button.dart | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart index 7ab8269..d1b2acb 100644 --- a/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart +++ b/lib/core/shared/presentation/widgets/app_bar/buttons/settings_button.dart @@ -93,25 +93,22 @@ class _BottomSheetContent extends StatefulWidget { } class _BottomSheetContentState extends State<_BottomSheetContent> { - bool isHeaderAnimated = false; + late AnimationControllerWithDelays animationController; final Duration animationDelay = const Duration(milliseconds: 125); @override void initState() { super.initState(); - _startAnimations(); - } - - void _startAnimations() { - final setters = [ - (val) => isHeaderAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } + animationController = AnimationControllerWithDelays( + initialDelay: Duration.zero, + delayBetweenAnimations: animationDelay, + numberOfAnimations: 4, + ); + animationController.startAnimations(() { + if (mounted) { + setState(() {}); + } + }); } @override @@ -119,7 +116,7 @@ class _BottomSheetContentState extends State<_BottomSheetContent> { child: Column( children: [ RightAnimation( - animationField: isHeaderAnimated, + animationField: animationController.animationStates[0], positionInitialValue: MediaQuery.of(context).size.width/6, child: const _Title(text: 'Choose your theme'), ), From 8759351a7bdf34bcb21c881cfe951f2bec25e8c7 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:46:22 +0200 Subject: [PATCH 472/475] remove unused field --- .../pages/registration/registration_page.dart | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index 36d8246..ce725c8 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:poetlum/core/constants/navigator_constants.dart'; @@ -26,21 +24,6 @@ class _RegistrationPageState extends State { final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); - bool isTextAnimated = false; - final Duration animationDelay = const Duration(milliseconds: 200); - - void _startAnimations() { - final setters = [ - (val) => isTextAnimated = val, - ]; - - for (var i = 0; i < setters.length; i++) { - Future.delayed(animationDelay * (i + 1)).then( - (_) => setState(() => setters[i](true)), - ); - } - } - @override void initState() { super.initState(); @@ -58,8 +41,6 @@ class _RegistrationPageState extends State { _passwordController.addListener(() { formCubit.passwordChanged(_passwordController.text); }); - - _startAnimations(); } @override From 8d8b3abca18abd86152862eb4a2e9c4e9d7722e4 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:15:57 +0200 Subject: [PATCH 473/475] Fix overflow issue --- .../pages/registration/registration_page.dart | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/features/authorization/presentation/pages/registration/registration_page.dart b/lib/features/authorization/presentation/pages/registration/registration_page.dart index ce725c8..437bff0 100644 --- a/lib/features/authorization/presentation/pages/registration/registration_page.dart +++ b/lib/features/authorization/presentation/pages/registration/registration_page.dart @@ -47,18 +47,20 @@ class _RegistrationPageState extends State { Widget build(BuildContext context) => Scaffold( body: SafeArea( child: BlocBuilder( - builder: (context, state) => Column( - children: [ - const _Header(), - - _Form( - _usernameController, - _emailController, - _passwordController, - ), - - const _Footer(), - ], + builder: (context, state) => SingleChildScrollView( + child: Column( + children: [ + const _Header(), + + _Form( + _usernameController, + _emailController, + _passwordController, + ), + + const _Footer(), + ], + ), ), ), ), @@ -148,8 +150,7 @@ class _FormState extends State<_Form> { @override Widget build(BuildContext context) => BlocBuilder( - builder: (context, state) => SizedBox( - height: MediaQuery.of(context).size.height/2.75, + builder: (context, state) => SingleChildScrollView( child: Column( children: [ TopAnimation( @@ -157,22 +158,22 @@ class _FormState extends State<_Form> { positionInitialValue: MediaQuery.of(context).size.height/14, child: UsernameTextField(controller: widget.usernameController), ), - const Spacer(), + const SizedBox(height: 20), TopAnimation( animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.height/14, child: EmailTextField(controller: widget.emailController), ), - const Spacer(), - + const SizedBox(height: 20), + TopAnimation( animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.height/14, child: PasswordTextField(controller: widget.passwordController), ), - const Spacer(), - + const SizedBox(height: 20), + TopAnimation( animationField: animationController.animationStates[3], positionInitialValue: MediaQuery.of(context).size.height/14, From 59e8980bf58cc7104689b30fc76be9df389ba676 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:22:12 +0200 Subject: [PATCH 474/475] Fix overflow issue --- .../presentation/pages/login/login_page.dart | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/features/authorization/presentation/pages/login/login_page.dart b/lib/features/authorization/presentation/pages/login/login_page.dart index 95fbb58..84b13b4 100644 --- a/lib/features/authorization/presentation/pages/login/login_page.dart +++ b/lib/features/authorization/presentation/pages/login/login_page.dart @@ -40,17 +40,19 @@ class _LoginPageState extends State { Widget build(BuildContext context) => Scaffold( body: SafeArea( child: BlocBuilder( - builder: (context, state) => Column( - children: [ - const _Header(), - - _Form( - _emailController, - _passwordController, - ), - - const _Footer(), - ], + builder: (context, state) => SingleChildScrollView( + child: Column( + children: [ + const _Header(), + + _Form( + _emailController, + _passwordController, + ), + + const _Footer(), + ], + ), ), ), ), @@ -138,8 +140,7 @@ class _FormState extends State<_Form> { @override Widget build(BuildContext context) => BlocBuilder( - builder: (context, state) => SizedBox( - height: MediaQuery.of(context).size.height/3.5, + builder: (context, state) => SingleChildScrollView( child: Column( children: [ RightAnimation( @@ -147,15 +148,15 @@ class _FormState extends State<_Form> { positionInitialValue: MediaQuery.of(context).size.height/14, child: EmailTextField(controller: widget.emailController), ), - const Spacer(), - + const SizedBox(height: 20), + RightAnimation( animationField: animationController.animationStates[1], positionInitialValue: MediaQuery.of(context).size.height/14, child: PasswordTextField(controller: widget.passwordController), ), - const Spacer(), - + const SizedBox(height: 20), + RightAnimation( animationField: animationController.animationStates[2], positionInitialValue: MediaQuery.of(context).size.height/14, From 51e62756306ab2ff9c8874560a9c83e208c6d8b2 Mon Sep 17 00:00:00 2001 From: tortamque <90132962+tortamque@users.noreply.github.com> Date: Wed, 20 Dec 2023 11:35:37 +0200 Subject: [PATCH 475/475] Disable screen rotation --- lib/main.dart | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 4e3629d..2130c4e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:poetlum/features/application/poetlum_app.dart'; import 'package:poetlum/features/dependency_injection/presentation/widgets/init_dependencies.dart'; import 'package:poetlum/features/firebase/presentation/widgets/init_crashlytics_widget.dart'; @@ -11,20 +12,24 @@ import 'package:poetlum/firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - runApp( - InitNetworkController( - child: InitFirebaseWidget( - options: DefaultFirebaseOptions.currentPlatform, - child: const InitDependencies( - child: InitCrashlyticsWidget( - child: InitBlocs( - child: InitTheme( - child: PoetlumApp(), + await SystemChrome.setPreferredOrientations( + [DeviceOrientation.portraitUp], + ).then( + (_) => runApp( + InitNetworkController( + child: InitFirebaseWidget( + options: DefaultFirebaseOptions.currentPlatform, + child: const InitDependencies( + child: InitCrashlyticsWidget( + child: InitBlocs( + child: InitTheme( + child: PoetlumApp(), + ), ), ), ), ), ), - ), + ) ); }