diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96a357f..a09532d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,9 +28,6 @@ jobs: - name: Analyze run: flutter analyze - - name: Test - run: flutter test - - name: Check Pana run: pana --no-warning --exit-code-threshold 10 diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f917e..f0766c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0 +* New parameter `navigatorKey` +* Ability to delete the comment for owner and admin +* Delete `flutter_bloc` dependency + ## 0.3.1 * Use AppBarTheme instead of SuggestionsAppBar [#65](https://github.com/What-the-Flutter/Suggest-a-Feature/pull/65) * Get rid of flutter-localizations [#58](https://github.com/What-the-Flutter/Suggest-a-Feature/pull/58) diff --git a/example/lib/main.dart b/example/lib/main.dart index eb4c705..621983c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -23,6 +23,7 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'Suggest a Feature', theme: ThemeData(useMaterial3: true), + navigatorKey: navigatorKey, home: Scaffold( body: SuggestionsPage( onGetUserById: (id) => Future( @@ -33,12 +34,15 @@ class MyApp extends StatelessWidget { userId: '1', isAdmin: true, adminSettings: _adminSettings, + navigatorKey: navigatorKey, ), ), ); } } +final navigatorKey = GlobalKey(); + const SuggestionAuthor _suggestionAuthor = SuggestionAuthor( id: '1', username: 'Author', @@ -50,7 +54,7 @@ const AdminSettings _adminSettings = AdminSettings( class MySuggestionDataSource implements SuggestionsDataSource { final Map _suggestions = { - 'mockDataSuggestion1': Suggestion( + '1': Suggestion( id: '1', title: 'Hashtags', authorId: '1', @@ -59,7 +63,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { description: 'Ability to add and filter events with #hashtags', status: SuggestionStatus.requests, ), - 'mockDataSuggestion2': Suggestion( + '2': Suggestion( id: '2', title: 'Import/Export as pdf', authorId: '2', @@ -69,7 +73,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { description: 'Feature to import and export events', status: SuggestionStatus.requests, ), - 'mockDataSuggestion3': Suggestion( + '3': Suggestion( id: '3', title: 'Notification', authorId: '3', @@ -78,7 +82,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { description: 'Implement notification date change', status: SuggestionStatus.inProgress, ), - 'mockDataSuggestion4': Suggestion( + '4': Suggestion( id: '4', title: 'Video', authorId: '4', @@ -88,7 +92,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { description: 'Аbility to save video', status: SuggestionStatus.inProgress, ), - 'mockDataSuggestion5': Suggestion( + '5': Suggestion( id: '5', title: 'Image', authorId: '5', @@ -98,7 +102,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { description: 'Poor image quality', status: SuggestionStatus.completed, ), - 'mockDataSuggestion6': Suggestion( + '6': Suggestion( id: '6', title: 'Offline authorization', authorId: '6', @@ -108,7 +112,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { ), }; final Map _comments = { - 'mockDataComment1': Comment( + '1': Comment( author: _suggestionAuthor, id: '1', suggestionId: '1', @@ -117,7 +121,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { creationTime: DateTime.now(), isFromAdmin: true, ), - 'mockDataComment2': Comment( + '2': Comment( author: _suggestionAuthor, id: '2', suggestionId: '2', @@ -126,7 +130,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { creationTime: DateTime.now(), isFromAdmin: false, ), - 'mockDataComment3': Comment( + '3': Comment( author: _suggestionAuthor, id: '3', suggestionId: '3', @@ -135,7 +139,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { creationTime: DateTime.now(), isFromAdmin: true, ), - 'mockDataComment4': Comment( + '4': Comment( author: _suggestionAuthor, id: '4', suggestionId: '4', @@ -144,7 +148,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { creationTime: DateTime.now(), isFromAdmin: true, ), - 'mockDataComment5': Comment( + '5': Comment( author: _suggestionAuthor, id: '5', suggestionId: '5', @@ -153,7 +157,7 @@ class MySuggestionDataSource implements SuggestionsDataSource { creationTime: DateTime.now(), isFromAdmin: false, ), - 'mockDataComment6': Comment( + '6': Comment( author: _suggestionAuthor, id: '6', suggestionId: '6', @@ -231,8 +235,10 @@ class MySuggestionDataSource implements SuggestionsDataSource { : []; @override - Future deleteCommentById(String commentId) async => - _comments.remove(commentId); + Future deleteCommentById(String commentId) async { + _comments.removeWhere((_, comment) => comment.id == commentId); + } + @override Future addNotifyToUpdateUser(String suggestionId) async { @@ -261,7 +267,6 @@ class MySuggestionDataSource implements SuggestionsDataSource { final modifiedSet = { ..._suggestions[suggestionId]!.votedUserIds, }..add(userId); - _suggestions[suggestionId] = _suggestions[suggestionId]!.copyWith( votedUserIds: modifiedSet, ); diff --git a/example/pubspec.lock b/example/pubspec.lock index ae92822..1a4fa16 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" - bloc: - dependency: transitive - description: - name: bloc - sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" - url: "https://pub.dev" - source: hosted - version: "8.1.1" boolean_selector: dependency: transitive description: @@ -53,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -86,14 +78,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_bloc: - dependency: transitive - description: - name: flutter_bloc - sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae - url: "https://pub.dev" - source: hosted - version: "8.1.3" flutter_lints: dependency: "direct dev" description: @@ -123,14 +107,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.0" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" lints: dependency: transitive description: @@ -143,18 +119,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -163,14 +139,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" path: dependency: transitive description: @@ -195,14 +163,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.0" - provider: - dependency: transitive - description: - name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f - url: "https://pub.dev" - source: hosted - version: "6.0.5" sky_engine: dependency: transitive description: flutter @@ -212,10 +172,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -246,7 +206,7 @@ packages: path: ".." relative: true source: path - version: "0.3.0" + version: "0.3.1" term_glyph: dependency: transitive description: @@ -259,10 +219,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" vector_graphics: dependency: transitive description: @@ -295,6 +255,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" wtf_sliding_sheet: dependency: transitive description: @@ -312,5 +280,5 @@ packages: source: hosted version: "6.3.0" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.7.0-0" diff --git a/lib/src/data/repositories/suggestion_repository.dart b/lib/src/data/repositories/suggestion_repository.dart index 711ac06..343db63 100644 --- a/lib/src/data/repositories/suggestion_repository.dart +++ b/lib/src/data/repositories/suggestion_repository.dart @@ -114,4 +114,8 @@ class SuggestionRepositoryImpl implements SuggestionRepository { @override Future createComment(CreateCommentModel comment) => _suggestionsDataSource.createComment(comment); + + @override + Future deleteCommentById(String commentId) => + _suggestionsDataSource.deleteCommentById(commentId); } diff --git a/lib/src/domain/data_interfaces/suggestion_repository.dart b/lib/src/domain/data_interfaces/suggestion_repository.dart index 7e9a850..603a5a2 100644 --- a/lib/src/domain/data_interfaces/suggestion_repository.dart +++ b/lib/src/domain/data_interfaces/suggestion_repository.dart @@ -22,4 +22,5 @@ abstract class SuggestionRepository { Future> getAllComments(String suggestionId); Future createComment(CreateCommentModel comment); + Future deleteCommentById(String commentId); } diff --git a/lib/src/presentation/di/injector.dart b/lib/src/presentation/di/injector.dart index 6547d71..137f2b9 100644 --- a/lib/src/presentation/di/injector.dart +++ b/lib/src/presentation/di/injector.dart @@ -1,3 +1,4 @@ +import 'package:flutter/widgets.dart'; import 'package:suggest_a_feature/src/data/cache_data_source.dart'; import 'package:suggest_a_feature/src/data/interfaces/cache_data_source.dart'; import 'package:suggest_a_feature/src/data/interfaces/suggestions_data_source.dart'; @@ -25,6 +26,7 @@ class _Injector { required String userId, required SuggestionsDataSource suggestionsDataSource, required String locale, + GlobalKey? navigatorKey, AdminSettings? adminSettings, bool isAdmin = false, Map? imageHeaders, @@ -45,6 +47,7 @@ class _Injector { _adminSettings = adminSettings; _isAdmin = isAdmin; _localization = locale.localizationOptions; + _navigatorKey = navigatorKey; } AdminSettings? _adminSettings; @@ -80,4 +83,8 @@ class _Injector { late LocalizationOptions _localization; LocalizationOptions get localizations => _localization; + + late GlobalKey? _navigatorKey; + + GlobalKey? get navigatorKey => _navigatorKey; } diff --git a/lib/src/presentation/localization/localization_options.dart b/lib/src/presentation/localization/localization_options.dart index 3c420cf..62ebbbb 100644 --- a/lib/src/presentation/localization/localization_options.dart +++ b/lib/src/presentation/localization/localization_options.dart @@ -15,6 +15,7 @@ class LocalizationOptions { final String delete; final String deletionQuestion; final String deletionPhotoQuestion; + final String deletionCommentQuestion; final String title; final String description; final String postAnonymously; @@ -71,6 +72,8 @@ class LocalizationOptions { this.delete = 'Delete suggestion', this.deletionQuestion = 'Are you sure you want to delete the suggestion?', this.deletionPhotoQuestion = 'Are you sure you want to delete this photo?', + this.deletionCommentQuestion = + 'Are you sure you want to delete this comment?', this.title = 'Briefly describe your suggestion', this.description = 'Describe your suggestion in details', this.postAnonymously = 'Post anonymously', @@ -130,6 +133,8 @@ class LocalizationOptions { delete: 'Удалить предложение', deletionQuestion: 'Вы действительно хотите удалить это предложение?', deletionPhotoQuestion: 'Вы действительно хотите удалить это фото?', + deletionCommentQuestion: + 'Вы действительно хотите удалить этот комментарий?', title: 'Кратко опишите ваше предложение', description: 'Опишите ваше предложение более подробно', postAnonymously: 'Опубликовать анонимно', @@ -187,6 +192,7 @@ class LocalizationOptions { delete: 'Видалити пропозицію', deletionQuestion: 'Ви дійсно бажаєте видалити цю пропозицію?', deletionPhotoQuestion: 'Ви дійсно бажаєте видалити це фото?', + deletionCommentQuestion: 'Ви дійсно бажаєте видалити цей коментар?', title: 'Коротко опишіть вашу пропозицію', description: 'Опишіть вашу пропозицію більш детально', postAnonymously: 'Опублікувати анонімно', diff --git a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart b/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart index 3bd37fd..4afebf6 100644 --- a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart +++ b/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart @@ -1,12 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:suggest_a_feature/src/domain/entities/suggestion.dart'; import 'package:suggest_a_feature/src/presentation/di/injector.dart'; import 'package:suggest_a_feature/src/presentation/localization/localization_extensions.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit_scope.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_state.dart'; +import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_state_manager.dart'; import 'package:suggest_a_feature/src/presentation/pages/theme/theme_extension.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/add_event_photo_button.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/bottom_sheets/base_bottom_sheet.dart'; @@ -16,6 +14,7 @@ import 'package:suggest_a_feature/src/presentation/pages/widgets/clickable_list_ import 'package:suggest_a_feature/src/presentation/pages/widgets/network_image.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/photo_view.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/small_photo_preview.dart'; +import 'package:suggest_a_feature/src/presentation/pages/widgets/state_listener.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/suggestions_labels.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/suggestions_switch.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/suggestions_text_field.dart'; @@ -91,7 +90,7 @@ class _CreateEditSuggestionBottomSheetState } void _listener(BuildContext context, CreateEditSuggestionState state) { - final cubit = context.read(); + final stateManager = CreateEditSuggestionManager.of(context); if (state.savingImageResultMessageType != SavingResultMessageType.none) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -106,31 +105,33 @@ class _CreateEditSuggestionBottomSheetState } else if (state.isSubmitted) { widget.onClose(); } else if (state.isPhotoViewOpen) { - _openPhotoView(state, cubit); + _openPhotoView(state, stateManager); } - cubit.reset(); + stateManager.reset(); } @override Widget build(BuildContext context) { - return CreateEditSuggestionCubitScope( + return CreateEditSuggestionManager( suggestion: widget.suggestion, - child: BlocConsumer( - listenWhen: _listenWhen, - listener: _listener, - builder: (_, state) { + child: Builder( + builder: (context) { + final stateManager = CreateEditSuggestionManager.of(context); + final state = stateManager.state; + + late final Widget child; if (state.isLabelsBottomSheetOpen) { - return _LabelsBottomSheet( + child = _LabelsBottomSheet( suggestionLabels: state.suggestion.labels, labelsSheetController: _labelsSheetController, ); } else if (state.isStatusBottomSheetOpen && i.isAdmin) { - return _StatusesBottomSheet( + child = _StatusesBottomSheet( suggestionStatus: state.suggestion.status, statusesSheetController: _statusesSheetController, ); } else if (!state.isPhotoViewOpen) { - return _CreateEditSuggestionBottomSheet( + child = _CreateEditSuggestionBottomSheet( titleFocusNode: _titleFocusNode, controller: widget.controller, onUploadMultiplePhotos: widget.onUploadMultiplePhotos, @@ -139,9 +140,15 @@ class _CreateEditSuggestionBottomSheetState titleController: _titleController, onClose: widget.onClose, ); + } else { + child = const SizedBox.shrink(); } - - return const SizedBox.shrink(); + return StateListener( + state: state, + listenWhen: _listenWhen, + listener: (state) => _listener(context, state), + child: child, + ); }, ), ); @@ -149,7 +156,7 @@ class _CreateEditSuggestionBottomSheetState Future _openPhotoView( CreateEditSuggestionState state, - CreateEditSuggestionCubit cubit, + CreateEditSuggestionStateManager stateManager, ) async { await showDialog( useSafeArea: false, @@ -159,17 +166,17 @@ class _CreateEditSuggestionBottomSheetState builder: (BuildContext context) { return PhotoView( initialIndex: state.openPhotoIndex!, - onDeleteClick: cubit.removePhoto, + onDeleteClick: stateManager.removePhoto, onDownloadClick: widget.onSaveToGallery != null - ? (String path) => - cubit.showSavingResultMessage(widget.onSaveToGallery!(path)) + ? (String path) => stateManager + .showSavingResultMessage(widget.onSaveToGallery!(path)) : null, photos: state.suggestion.images, previousNavBarColor: context.theme.colorScheme.surface, ); }, ); - cubit.changePhotoViewStatus(isPhotoViewOpen: false); + stateManager.changePhotoViewStatus(isPhotoViewOpen: false); } } @@ -194,7 +201,7 @@ class _CreateEditSuggestionBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - final cubit = context.read(); + final stateManager = CreateEditSuggestionManager.of(context); return BaseBottomSheet( controller: controller, onOpen: titleFocusNode.requestFocus, @@ -211,18 +218,19 @@ class _CreateEditSuggestionBottomSheet extends StatelessWidget { titleFocusNode: titleFocusNode, descriptionFocusNode: descriptionFocusNode, onUploadMultiplePhotos: onUploadMultiplePhotos, - onTitleChanged: cubit.changeSuggestionTitle, - onDescriptionChanged: cubit.changeSuggestionDescription, - onLabelChanged: (value) => cubit.changeLabelsBottomSheetStatus( + onTitleChanged: stateManager.changeSuggestionTitle, + onDescriptionChanged: stateManager.changeSuggestionDescription, + onLabelChanged: (value) => stateManager.changeLabelsBottomSheetStatus( isLabelsBottomSheetOpen: value, ), - onAnonymityChanged: (value) => cubit.changeSuggestionAnonymity( + onAnonymityChanged: (value) => stateManager.changeSuggestionAnonymity( isAnonymous: value, ), - onStatusChanged: (value) => cubit.changeStatusBottomSheetStatus( + onStatusChanged: (value) => + stateManager.changeStatusBottomSheetStatus( isStatusBottomSheetOpen: value, ), - onSave: cubit.saveSuggestion, + onSave: stateManager.saveSuggestion, ); }, ); @@ -368,72 +376,65 @@ class _PhotoPickerItem extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.isLoading != current.isLoading || - previous.suggestion.images != current.suggestion.images, - builder: (context, state) { - final cubit = context.read(); - - if (state.suggestion.images.isNotEmpty) { - final tileWidth = state.suggestion.images.length == 1 - ? (MediaQuery.of(context).size.width - Dimensions.margin3x) / 2 - : (MediaQuery.of(context).size.width - Dimensions.margin4x) / 3; - - return SizedBox( - height: - MediaQuery.of(context).size.height * Dimensions.smallSize / 100, - child: Padding( - padding: const EdgeInsets.only(bottom: Dimensions.marginMiddle), - child: ListView.builder( - physics: const BouncingScrollPhysics(), - scrollDirection: Axis.horizontal, - itemCount: state.suggestion.images.length + 1, - itemBuilder: (BuildContext context, int i) { - return _PhotoItem( - isAddButtonShown: i == 0, - isLoading: state.isLoading, - imageUrl: i != 0 ? state.suggestion.images[i - 1] : '', - tileWidth: state.suggestion.images.length > 2 - ? tileWidth * 0.9 - : tileWidth, - onUploadPhotos: () { - final availableNumOfPhotos = maxPhotosForOneSuggestion - - state.suggestion.images.length; - if (availableNumOfPhotos > 0) { - cubit.addUploadedPhotos( - onUploadMultiplePhotos!( - availableNumOfPhotos: availableNumOfPhotos, - ), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - localization.eventPhotosRestriction, - ), - ), - ); - } - }, - onPhotoClick: () => cubit.onPhotoClick(i - 1), - ); + final stateManager = CreateEditSuggestionManager.of(context); + final state = stateManager.state; + + if (state.suggestion.images.isNotEmpty) { + final tileWidth = state.suggestion.images.length == 1 + ? (MediaQuery.of(context).size.width - Dimensions.margin3x) / 2 + : (MediaQuery.of(context).size.width - Dimensions.margin4x) / 3; + + return SizedBox( + height: MediaQuery.of(context).size.height * Dimensions.smallSize / 100, + child: Padding( + padding: const EdgeInsets.only(bottom: Dimensions.marginMiddle), + child: ListView.builder( + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemCount: state.suggestion.images.length + 1, + itemBuilder: (BuildContext context, int i) { + return _PhotoItem( + isAddButtonShown: i == 0, + isLoading: state.isLoading, + imageUrl: i != 0 ? state.suggestion.images[i - 1] : '', + tileWidth: state.suggestion.images.length > 2 + ? tileWidth * 0.9 + : tileWidth, + onUploadPhotos: () { + final availableNumOfPhotos = maxPhotosForOneSuggestion - + state.suggestion.images.length; + if (availableNumOfPhotos > 0) { + stateManager.addUploadedPhotos( + onUploadMultiplePhotos!( + availableNumOfPhotos: availableNumOfPhotos, + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + localization.eventPhotosRestriction, + ), + ), + ); + } }, - ), - ), - ); - } - - return _AddButton( - isLoading: state.isLoading, - isSmall: state.suggestion.images.isNotEmpty, - onUploadPhotos: () => cubit.addUploadedPhotos( - onUploadMultiplePhotos!( - availableNumOfPhotos: maxPhotosForOneSuggestion, - ), + onPhotoClick: () => stateManager.onPhotoClick(i - 1), + ); + }, ), - ); - }, + ), + ); + } + + return _AddButton( + isLoading: state.isLoading, + isSmall: state.suggestion.images.isNotEmpty, + onUploadPhotos: () => stateManager.addUploadedPhotos( + onUploadMultiplePhotos!( + availableNumOfPhotos: maxPhotosForOneSuggestion, + ), + ), ); } } @@ -625,74 +626,72 @@ class _EditSuggestionBottomSheetListView extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return ListView( - padding: const EdgeInsets.symmetric( - vertical: Dimensions.marginSmall, + final state = CreateEditSuggestionManager.of(context).state; + + return ListView( + padding: const EdgeInsets.symmetric( + vertical: Dimensions.marginSmall, + ), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + children: [ + SuggestionsTextField( + controller: titleController, + focusNode: titleFocusNode, + hintText: localization.title, + padding: const EdgeInsets.fromLTRB( + Dimensions.marginDefault, + Dimensions.marginDefault, + Dimensions.marginSmall, + Dimensions.marginDefault, ), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - children: [ - SuggestionsTextField( - controller: titleController, - focusNode: titleFocusNode, - hintText: localization.title, - padding: const EdgeInsets.fromLTRB( - Dimensions.marginDefault, - Dimensions.marginDefault, - Dimensions.marginSmall, - Dimensions.marginDefault, - ), - onChanged: (text) { - if (state.suggestion.title != text) { - onTitleChanged(text); - } - }, - isShowError: state.isShowTitleError, - ), - const SizedBox(height: Dimensions.marginDefault), - SuggestionsTextField( - controller: descriptionController, - focusNode: descriptionFocusNode, - hintText: localization.description, - padding: const EdgeInsets.fromLTRB( - Dimensions.marginDefault, - Dimensions.marginDefault, - Dimensions.marginSmall, - Dimensions.marginDefault, - ), - onChanged: (String text) { - if (state.suggestion.description != text) { - onDescriptionChanged(text); - } - }, - ), - const SizedBox(height: Dimensions.marginBig), - const Divider(thickness: 0.5, height: 1.5), - _LabelItems( - labels: state.suggestion.labels, - changeLabelsBottomSheetStatus: onLabelChanged, - ), - ..._suggestionStatus( - isEditing: state.isEditing, - status: state.suggestion.status, - ), - ..._multiplePicker( - isImagesEmpty: state.suggestion.images.isEmpty, - ), - ..._anonymitySwitch( - isEditing: state.isEditing, - isAnonymous: state.suggestion.isAnonymous, - ), - _SaveSubmitButton( - isEditing: state.isEditing, - isLoading: state.isLoading, - saveSuggestion: onSave, - ), - ], - ); - }, + onChanged: (text) { + if (state.suggestion.title != text) { + onTitleChanged(text); + } + }, + isShowError: state.isShowTitleError, + ), + const SizedBox(height: Dimensions.marginDefault), + SuggestionsTextField( + controller: descriptionController, + focusNode: descriptionFocusNode, + hintText: localization.description, + padding: const EdgeInsets.fromLTRB( + Dimensions.marginDefault, + Dimensions.marginDefault, + Dimensions.marginSmall, + Dimensions.marginDefault, + ), + onChanged: (String text) { + if (state.suggestion.description != text) { + onDescriptionChanged(text); + } + }, + ), + const SizedBox(height: Dimensions.marginBig), + const Divider(thickness: 0.5, height: 1.5), + _LabelItems( + labels: state.suggestion.labels, + changeLabelsBottomSheetStatus: onLabelChanged, + ), + ..._suggestionStatus( + isEditing: state.isEditing, + status: state.suggestion.status, + ), + ..._multiplePicker( + isImagesEmpty: state.suggestion.images.isEmpty, + ), + ..._anonymitySwitch( + isEditing: state.isEditing, + isAnonymous: state.suggestion.isAnonymous, + ), + _SaveSubmitButton( + isEditing: state.isEditing, + isLoading: state.isLoading, + saveSuggestion: onSave, + ), + ], ); } @@ -757,20 +756,20 @@ class _StatusesBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - final cubit = context.read(); + final stateManager = CreateEditSuggestionManager.of(context); return StatusBottomSheet( controller: statusesSheetController, selectedStatus: suggestionStatus, onCancel: ([_]) async { await statusesSheetController.collapse(); - cubit.changeStatusBottomSheetStatus( + stateManager.changeStatusBottomSheetStatus( isStatusBottomSheetOpen: false, ); }, onDone: (status) async { - cubit.changeStatus(status); + stateManager.changeStatus(status); await statusesSheetController.collapse(); - cubit.changeStatusBottomSheetStatus( + stateManager.changeStatusBottomSheetStatus( isStatusBottomSheetOpen: false, ); }, @@ -789,20 +788,20 @@ class _LabelsBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - final cubit = context.read(); + final stateManager = CreateEditSuggestionManager.of(context); return LabelBottomSheet( controller: labelsSheetController, selectedLabels: suggestionLabels, onCancel: ([_]) async { await labelsSheetController.collapse(); - cubit.changeLabelsBottomSheetStatus( + stateManager.changeLabelsBottomSheetStatus( isLabelsBottomSheetOpen: false, ); }, onDone: (labels) async { - cubit.selectLabels(labels); + stateManager.selectLabels(labels); await labelsSheetController.collapse(); - cubit.changeLabelsBottomSheetStatus( + stateManager.changeLabelsBottomSheetStatus( isLabelsBottomSheetOpen: false, ); }, diff --git a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit_scope.dart b/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit_scope.dart deleted file mode 100644 index 5745959..0000000 --- a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit_scope.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:suggest_a_feature/src/presentation/di/injector.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit.dart'; -import 'package:suggest_a_feature/suggest_a_feature.dart'; - -class CreateEditSuggestionCubitScope extends StatelessWidget { - final Widget child; - final Suggestion? suggestion; - - const CreateEditSuggestionCubitScope({ - required this.child, - this.suggestion, - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => CreateEditSuggestionCubit( - suggestionRepository: i.suggestionRepository, - suggestion: suggestion, - ), - child: child, - ); - } -} diff --git a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit.dart b/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_state_manager.dart similarity index 56% rename from lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit.dart rename to lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_state_manager.dart index 551abd9..582b524 100644 --- a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_cubit.dart +++ b/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_state_manager.dart @@ -1,33 +1,65 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'dart:async'; + +import 'package:flutter/material.dart'; import 'package:suggest_a_feature/src/domain/data_interfaces/suggestion_repository.dart'; import 'package:suggest_a_feature/src/domain/entities/suggestion.dart'; import 'package:suggest_a_feature/src/presentation/di/injector.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_state.dart'; import 'package:suggest_a_feature/src/presentation/utils/image_utils.dart'; -class CreateEditSuggestionCubit extends Cubit { - final SuggestionRepository _suggestionRepository; - - CreateEditSuggestionCubit({ - required SuggestionRepository suggestionRepository, - Suggestion? suggestion, - }) : _suggestionRepository = suggestionRepository, - super( - CreateEditSuggestionState( - suggestion: suggestion ?? Suggestion.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - isShowTitleError: false, - isEditing: suggestion != null, - isSubmitted: false, - isLoading: false, - isLabelsBottomSheetOpen: false, - isStatusBottomSheetOpen: false, - isPhotoViewOpen: false, - ), - ); +class CreateEditSuggestionManager extends StatefulWidget { + final Suggestion? suggestion; + final Widget child; + + const CreateEditSuggestionManager({ + required this.child, + this.suggestion, + super.key, + }); + + static CreateEditSuggestionStateManager of(BuildContext context) { + return (context.dependOnInheritedWidgetOfExactType< + _InheritedCreateEditSuggestion>()!) + .suggestionManager; + } + + @override + CreateEditSuggestionStateManager createState() => + CreateEditSuggestionStateManager(); +} + +class CreateEditSuggestionStateManager + extends State { + late CreateEditSuggestionState state; + late final SuggestionRepository _suggestionRepository; + + @override + void initState() { + super.initState(); + _suggestionRepository = i.suggestionRepository; + state = CreateEditSuggestionState( + suggestion: widget.suggestion ?? Suggestion.empty(), + savingImageResultMessageType: SavingResultMessageType.none, + isShowTitleError: false, + isEditing: widget.suggestion != null, + isSubmitted: false, + isLoading: false, + isLabelsBottomSheetOpen: false, + isStatusBottomSheetOpen: false, + isPhotoViewOpen: false, + ); + } + + void _update(CreateEditSuggestionState newState) { + if (newState != state) { + setState(() { + state = newState; + }); + } + } void reset() { - emit( + _update( state.newState( savingImageResultMessageType: SavingResultMessageType.none, ), @@ -35,10 +67,10 @@ class CreateEditSuggestionCubit extends Cubit { } Future addUploadedPhotos(Future?> urls) async { - emit(state.newState(isLoading: true)); + _update(state.newState(isLoading: true)); final photos = await urls; if (photos != null) { - emit( + _update( state.newState( suggestion: state.suggestion.copyWith( images: [...photos, ...state.suggestion.images], @@ -50,7 +82,7 @@ class CreateEditSuggestionCubit extends Cubit { } void removePhoto(String path) { - emit( + _update( state.newState( suggestion: state.suggestion .copyWith(images: state.suggestion.images..remove(path)), @@ -62,7 +94,7 @@ class CreateEditSuggestionCubit extends Cubit { Future showSavingResultMessage(Future isSuccess) async { final savingResult = await isSuccess; if (savingResult != null) { - emit( + _update( state.newState( savingImageResultMessageType: savingResult ? SavingResultMessageType.success @@ -73,7 +105,7 @@ class CreateEditSuggestionCubit extends Cubit { } void changeSuggestionAnonymity({required bool isAnonymous}) { - emit( + _update( state.newState( suggestion: state.suggestion.copyWith(isAnonymous: isAnonymous), ), @@ -81,23 +113,23 @@ class CreateEditSuggestionCubit extends Cubit { } void changeLabelsBottomSheetStatus({required bool isLabelsBottomSheetOpen}) { - emit(state.newState(isLabelsBottomSheetOpen: isLabelsBottomSheetOpen)); + _update(state.newState(isLabelsBottomSheetOpen: isLabelsBottomSheetOpen)); } void changeStatusBottomSheetStatus({required bool isStatusBottomSheetOpen}) { - emit(state.newState(isStatusBottomSheetOpen: isStatusBottomSheetOpen)); + _update(state.newState(isStatusBottomSheetOpen: isStatusBottomSheetOpen)); } void changePhotoViewStatus({required bool isPhotoViewOpen}) { - emit(state.newState(isPhotoViewOpen: isPhotoViewOpen)); + _update(state.newState(isPhotoViewOpen: isPhotoViewOpen)); } void onPhotoClick(int index) { - emit(state.newState(isPhotoViewOpen: true, openPhotoIndex: index)); + _update(state.newState(isPhotoViewOpen: true, openPhotoIndex: index)); } void changeSuggestionTitle(String text) { - emit( + _update( state.newState( suggestion: state.suggestion.copyWith(title: text), isShowTitleError: false, @@ -106,7 +138,7 @@ class CreateEditSuggestionCubit extends Cubit { } void changeSuggestionDescription(String text) { - emit( + _update( state.newState( suggestion: state.suggestion.copyWith(description: text), ), @@ -114,7 +146,7 @@ class CreateEditSuggestionCubit extends Cubit { } void selectLabels(List selectedLabels) { - emit( + _update( state.newState( suggestion: state.suggestion.copyWith(labels: selectedLabels), ), @@ -122,7 +154,7 @@ class CreateEditSuggestionCubit extends Cubit { } void changeStatus(SuggestionStatus status) { - emit( + _update( state.newState( suggestion: state.suggestion.copyWith(status: status), ), @@ -131,7 +163,7 @@ class CreateEditSuggestionCubit extends Cubit { Future saveSuggestion() async { if (state.suggestion.title.isEmpty || state.isLoading) { - emit(state.newState(isShowTitleError: true)); + _update(state.newState(isShowTitleError: true)); return; } if (state.isEditing) { @@ -147,6 +179,26 @@ class CreateEditSuggestionCubit extends Cubit { ); await _suggestionRepository.createSuggestion(model); } - emit(state.newState(isSubmitted: true)); + _update(state.newState(isSubmitted: true)); + } + + @override + Widget build(BuildContext context) { + return _InheritedCreateEditSuggestion( + suggestionManager: this, + child: widget.child, + ); } } + +class _InheritedCreateEditSuggestion extends InheritedWidget { + final CreateEditSuggestionStateManager suggestionManager; + + const _InheritedCreateEditSuggestion({ + required this.suggestionManager, + required super.child, + }); + + @override + bool updateShouldNotify(_InheritedCreateEditSuggestion old) => true; +} diff --git a/lib/src/presentation/pages/suggestion/suggestion_cubit_scope.dart b/lib/src/presentation/pages/suggestion/suggestion_cubit_scope.dart deleted file mode 100644 index d7d8487..0000000 --- a/lib/src/presentation/pages/suggestion/suggestion_cubit_scope.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:suggest_a_feature/src/presentation/di/injector.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_cubit.dart'; -import 'package:suggest_a_feature/suggest_a_feature.dart'; - -class SuggestionCubitScope extends StatelessWidget { - final Widget child; - final Suggestion suggestion; - final OnGetUserById onGetUserById; - - const SuggestionCubitScope({ - required this.suggestion, - required this.onGetUserById, - required this.child, - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => SuggestionCubit( - suggestionRepository: i.suggestionRepository, - suggestion: suggestion, - onGetUserById: onGetUserById, - ), - child: child, - ); - } -} diff --git a/lib/src/presentation/pages/suggestion/suggestion_page.dart b/lib/src/presentation/pages/suggestion/suggestion_page.dart index b75b2c2..6027010 100644 --- a/lib/src/presentation/pages/suggestion/suggestion_page.dart +++ b/lib/src/presentation/pages/suggestion/suggestion_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:suggest_a_feature/src/domain/entities/admin_settings.dart'; import 'package:suggest_a_feature/src/domain/entities/comment.dart'; @@ -8,9 +7,8 @@ import 'package:suggest_a_feature/src/domain/entities/suggestion_author.dart'; import 'package:suggest_a_feature/src/presentation/di/injector.dart'; import 'package:suggest_a_feature/src/presentation/localization/localization_extensions.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_cubit_scope.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_state.dart'; +import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_state_manager.dart'; import 'package:suggest_a_feature/src/presentation/pages/theme/theme_extension.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/appbar_widget.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/avatar_widget.dart'; @@ -21,6 +19,7 @@ import 'package:suggest_a_feature/src/presentation/pages/widgets/bottom_sheets/n import 'package:suggest_a_feature/src/presentation/pages/widgets/icon_button.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/network_image.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/photo_view.dart'; +import 'package:suggest_a_feature/src/presentation/pages/widgets/state_listener.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/suggestions_labels.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/votes_counter.dart'; import 'package:suggest_a_feature/src/presentation/utils/assets_strings.dart'; @@ -72,88 +71,97 @@ class _SuggestionPageState extends State { ); } if (state.isPopped) { - Navigator.of(context).pop(); + (i.navigatorKey?.currentState ?? Navigator.of(context)).pop(); } - context.read().reset(); + SuggestionManager.of(context).reset(); } @override Widget build(BuildContext context) { - return SuggestionCubitScope( + return SuggestionManager( suggestion: widget.suggestion, onGetUserById: widget.onGetUserById, - child: BlocConsumer( - listenWhen: _listenWhen, - listener: _listener, - builder: (context, state) { - final cubit = context.read(); - return Stack( - alignment: Alignment.bottomLeft, - children: [ - Scaffold( - appBar: _appBar(cubit, state.isEditable), - backgroundColor: context.theme.scaffoldBackgroundColor, - body: _MainContent( - onSaveToGallery: widget.onSaveToGallery, - ), - ), - SafeArea( - top: false, - bottom: SuggestionsPlatform.isIOS, - child: Container( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + - Dimensions.marginSmall, + child: Builder( + builder: (context) { + final stateManager = SuggestionManager.of(context); + final state = stateManager.state; + return StateListener( + state: state, + listenWhen: _listenWhen, + listener: (state) => _listener(context, state), + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Scaffold( + appBar: _appBar(stateManager, state.isEditable), + backgroundColor: context.theme.scaffoldBackgroundColor, + body: _MainContent( + onSaveToGallery: widget.onSaveToGallery, + onCommentTap: stateManager.openDeletingCommentConfirmation, ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: _NewCommentButton( - onClick: cubit.openCreateCommentBottomSheet, - ), - ), - const SizedBox(width: Dimensions.marginDefault), - if (state.suggestion.votedUserIds.contains(i.userId)) - const SizedBox.shrink() - else + ), + SafeArea( + top: false, + bottom: SuggestionsPlatform.isIOS, + child: Container( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + + Dimensions.marginSmall, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ Expanded( - child: _UpvoteButton( - isVisible: !state.suggestion.votedUserIds - .contains(i.userId), - onClick: cubit.vote, + child: _NewCommentButton( + onClick: stateManager.openCreateCommentBottomSheet, ), ), - ], + const SizedBox(width: Dimensions.marginDefault), + if (state.suggestion.votedUserIds.contains(i.userId)) + const SizedBox.shrink() + else + Expanded( + child: _UpvoteButton( + isVisible: !state.suggestion.votedUserIds + .contains(i.userId), + onClick: stateManager.vote, + ), + ), + ], + ), ), ), - ), - _BottomSheet( - onUploadMultiplePhotos: widget.onUploadMultiplePhotos, - onSaveToGallery: widget.onSaveToGallery, - onGetUserById: widget.onGetUserById, - ), - ], + _BottomSheet( + onUploadMultiplePhotos: widget.onUploadMultiplePhotos, + onSaveToGallery: widget.onSaveToGallery, + onGetUserById: widget.onGetUserById, + ), + ], + ), ); }, ), ); } - SuggestionsAppBar _appBar(SuggestionCubit cubit, bool isEditable) { + SuggestionsAppBar _appBar( + SuggestionStateManager stateManager, + bool isEditable, + ) { return SuggestionsAppBar( - onBackClick: Navigator.of(context).pop, + onBackClick: () => + (i.navigatorKey?.currentState ?? Navigator.of(context)).pop(), screenTitle: localization.suggestion, trailing: Padding( padding: const EdgeInsets.only(right: Dimensions.marginDefault), child: isEditable ? SuggestionsIconButton( - onClick: cubit.openEditDeleteBottomSheet, + onClick: stateManager.openEditDeleteBottomSheet, imageIcon: AssetStrings.penIconImage, color: context.theme.appBarTheme.actionsIconTheme?.color, ) : SuggestionsIconButton( - onClick: cubit.openNotificationBottomSheet, + onClick: stateManager.openNotificationBottomSheet, imageIcon: AssetStrings.notificationsIconImage, color: context.theme.appBarTheme.actionsIconTheme?.color, ), @@ -163,62 +171,60 @@ class _SuggestionPageState extends State { } class _MainContent extends StatelessWidget { + final ValueChanged onCommentTap; final OnSaveToGalleryCallback? onSaveToGallery; const _MainContent({ + required this.onCommentTap, this.onSaveToGallery, }); @override Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.author != current.author || - previous.suggestion != current.suggestion || - previous.loadingComments != current.loadingComments, - builder: (context, state) { - final cubit = context.read(); - return NotificationListener( - onNotification: (OverscrollIndicatorNotification overscroll) { - overscroll.disallowIndicator(); - return true; - }, - child: SingleChildScrollView( - child: Column( - children: [ - _UserInfo( - author: state.author, - isAnonymous: state.suggestion.isAnonymous, - ), - _SuggestionInfo( - suggestion: state.suggestion, - onVote: cubit.vote, - ), - const SizedBox(height: Dimensions.marginSmall), - if (state.suggestion.images.isNotEmpty) ...[ - _AttachedImages( - onSaveToGallery: onSaveToGallery, - images: state.suggestion.images, - ), - const SizedBox(height: Dimensions.marginSmall), - ], - if (state.loadingComments) - const Center(child: CircularProgressIndicator()) - else if (state.suggestion.comments.isNotEmpty) - _CommentList(comments: state.suggestion.comments), - if (state.suggestion.votedUserIds.contains(i.userId)) - const SizedBox( - height: Dimensions.size2x * 2 + Dimensions.marginMiddle, - ) - else - const SizedBox( - height: Dimensions.size2x * 3 + Dimensions.margin2x, - ), - ], - ), - ), - ); + final stateManager = SuggestionManager.of(context); + final state = stateManager.state; + return NotificationListener( + onNotification: (OverscrollIndicatorNotification overscroll) { + overscroll.disallowIndicator(); + return true; }, + child: SingleChildScrollView( + child: Column( + children: [ + _UserInfo( + author: state.author, + isAnonymous: state.suggestion.isAnonymous, + ), + _SuggestionInfo( + suggestion: state.suggestion, + onVote: stateManager.vote, + ), + const SizedBox(height: Dimensions.marginSmall), + if (state.suggestion.images.isNotEmpty) ...[ + _AttachedImages( + onSaveToGallery: onSaveToGallery, + images: state.suggestion.images, + ), + const SizedBox(height: Dimensions.marginSmall), + ], + if (state.loadingComments) + const Center(child: CircularProgressIndicator()) + else if (state.suggestion.comments.isNotEmpty) + _CommentList( + comments: state.suggestion.comments, + onCommentTap: onCommentTap, + ), + if (state.suggestion.votedUserIds.contains(i.userId)) + const SizedBox( + height: Dimensions.size2x * 2 + Dimensions.marginMiddle, + ) + else + const SizedBox( + height: Dimensions.size2x * 3 + Dimensions.margin2x, + ), + ], + ), + ), ); } } @@ -358,8 +364,9 @@ class _AttachedImages extends StatelessWidget { class _CommentList extends StatelessWidget { final List comments; + final ValueChanged onCommentTap; - const _CommentList({required this.comments}); + const _CommentList({required this.comments, required this.onCommentTap}); @override Widget build(BuildContext context) { @@ -382,7 +389,12 @@ class _CommentList extends StatelessWidget { Wrap( runSpacing: 2, children: comments - .map((comment) => _CommentCard(comment: comment)) + .map( + (comment) => _CommentCard( + comment: comment, + onTap: onCommentTap, + ), + ) .toList(), ), ], @@ -467,6 +479,7 @@ class _WrappedAttachedImage extends StatelessWidget { @override Widget build(BuildContext context) { + final suggestionManager = SuggestionManager.of(context); return GestureDetector( onTap: () { showDialog( @@ -477,10 +490,9 @@ class _WrappedAttachedImage extends StatelessWidget { builder: (_) { return PhotoView( onDownloadClick: onSaveToGallery != null - ? (path) => - context.read().showSavingResultMessage( - onSaveToGallery!(path), - ) + ? (path) => suggestionManager.showSavingResultMessage( + onSaveToGallery!(path), + ) : null, initialIndex: images.indexOf(attachedImage), photos: images, @@ -509,29 +521,35 @@ class _WrappedAttachedImage extends StatelessWidget { class _CommentCard extends StatelessWidget { final Comment comment; + final ValueChanged onTap; - const _CommentCard({required this.comment}); + const _CommentCard({required this.comment, required this.onTap}); @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(Dimensions.marginDefault), - color: context.theme.colorScheme.surfaceVariant, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _CommentInfo( - comment: comment, - ), - Padding( - padding: const EdgeInsets.only(left: Dimensions.margin3x), - child: Text( - comment.text, - style: context.theme.textTheme.bodyMedium, - softWrap: true, + return GestureDetector( + onTap: i.isAdmin || i.userId == comment.author.id + ? () => onTap(comment.id) + : null, + child: Container( + padding: const EdgeInsets.all(Dimensions.marginDefault), + color: context.theme.colorScheme.surfaceVariant, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _CommentInfo( + comment: comment, ), - ), - ], + Padding( + padding: const EdgeInsets.only(left: Dimensions.margin3x), + child: Text( + comment.text, + style: context.theme.textTheme.bodyMedium, + softWrap: true, + ), + ), + ], + ), ), ); } @@ -550,38 +568,33 @@ class _BottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.bottomSheetType != current.bottomSheetType || - previous.suggestion != current.suggestion, - builder: (context, state) { - switch (state.bottomSheetType) { - case SuggestionBottomSheetType.confirmation: - return const _OpenConfirmationBottomSheet(); - case SuggestionBottomSheetType.notification: - return _OpenNotificationBottomSheet( - isNotificationOn: - state.suggestion.notifyUserIds.contains(i.userId), - ); - case SuggestionBottomSheetType.editDelete: - return _OpenEditDeleteBottomSheet( - suggestion: state.suggestion, - ); - case SuggestionBottomSheetType.createEdit: - return _OpenCreateEditBottomSheet( - suggestion: state.suggestion, - onUploadMultiplePhotos: onUploadMultiplePhotos, - onSaveToGallery: onSaveToGallery, - ); - case SuggestionBottomSheetType.createComment: - return _OpenCreateCommentBottomSheet( - onGetUserById: onGetUserById, - ); - case SuggestionBottomSheetType.none: - return const SizedBox.shrink(); - } - }, - ); + final state = SuggestionManager.of(context).state; + switch (state.bottomSheetType) { + case SuggestionBottomSheetType.confirmation: + return const _OpenConfirmationBottomSheet(); + case SuggestionBottomSheetType.notification: + return _OpenNotificationBottomSheet( + isNotificationOn: state.suggestion.notifyUserIds.contains(i.userId), + ); + case SuggestionBottomSheetType.editDelete: + return _OpenEditDeleteBottomSheet( + suggestion: state.suggestion, + ); + case SuggestionBottomSheetType.createEdit: + return _OpenCreateEditBottomSheet( + suggestion: state.suggestion, + onUploadMultiplePhotos: onUploadMultiplePhotos, + onSaveToGallery: onSaveToGallery, + ); + case SuggestionBottomSheetType.createComment: + return _OpenCreateCommentBottomSheet( + onGetUserById: onGetUserById, + ); + case SuggestionBottomSheetType.deleteCommentConfirmation: + return const _OpenCommentConfirmationBottomSheet(); + case SuggestionBottomSheetType.none: + return const SizedBox.shrink(); + } } } @@ -591,18 +604,44 @@ class _OpenConfirmationBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { final sheetController = SheetController(); - final cubit = context.read(); + final stateManager = SuggestionManager.of(context); return ConfirmationBottomSheet( controller: sheetController, question: localization.deletionQuestion, onConfirm: () { - cubit + stateManager ..closeBottomSheet() ..deleteSuggestion(); }, onCancel: ([_]) async { await sheetController.collapse(); - cubit.closeBottomSheet(); + stateManager.closeBottomSheet(); + }, + onConfirmAsset: AssetStrings.checkIconImage, + onCancelText: localization.cancel, + onConfirmText: localization.yesDelete, + ); + } +} + +class _OpenCommentConfirmationBottomSheet extends StatelessWidget { + const _OpenCommentConfirmationBottomSheet(); + + @override + Widget build(BuildContext context) { + final sheetController = SheetController(); + final stateManager = SuggestionManager.of(context); + return ConfirmationBottomSheet( + controller: sheetController, + question: localization.deletionCommentQuestion, + onConfirm: () { + stateManager + ..closeBottomSheet() + ..deleteComment(); + }, + onCancel: ([_]) async { + await sheetController.collapse(); + stateManager.closeBottomSheet(); }, onConfirmAsset: AssetStrings.checkIconImage, onCancelText: localization.cancel, @@ -621,16 +660,17 @@ class _OpenNotificationBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { final sheetController = SheetController(); - final cubit = context.read(); + final stateManager = SuggestionManager.of(context); return NotificationSuggestionBottomSheet( controller: sheetController, isNotificationOn: isNotificationOn, - onChangeNotification: (isNotificationOn) => cubit.changeNotification( + onChangeNotification: (isNotificationOn) => + stateManager.changeNotification( isNotificationOn: isNotificationOn, ), onCancel: ([_]) async { await sheetController.collapse(); - cubit.closeBottomSheet(); + stateManager.closeBottomSheet(); }, ); } @@ -646,16 +686,16 @@ class _OpenEditDeleteBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { final sheetController = SheetController(); - final cubit = context.read(); + final stateManager = SuggestionManager.of(context); return EditDeleteSuggestionBottomSheet( creationDate: suggestion.creationTime, controller: sheetController, onCancel: ([_]) async { await sheetController.collapse(); - cubit.closeBottomSheet(); + stateManager.closeBottomSheet(); }, - onEditClick: cubit.openCreateEditBottomSheet, - onDeleteClick: cubit.openConfirmationBottomSheet, + onEditClick: stateManager.openCreateEditBottomSheet, + onDeleteClick: stateManager.openConfirmationBottomSheet, ); } } @@ -674,11 +714,11 @@ class _OpenCreateEditBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { final sheetController = SheetController(); - final cubit = context.read(); + final stateManager = SuggestionManager.of(context); return CreateEditSuggestionBottomSheet( onClose: ([_]) async { await sheetController.collapse(); - cubit.closeBottomSheet(); + stateManager.closeBottomSheet(); }, onSaveToGallery: onSaveToGallery, onUploadMultiplePhotos: onUploadMultiplePhotos, @@ -698,19 +738,19 @@ class _OpenCreateCommentBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { final sheetController = SheetController(); - final cubit = context.read(); + final stateManager = SuggestionManager.of(context); return CreateCommentBottomSheet( controller: sheetController, onClose: ([_]) async { await sheetController.collapse(); - cubit.closeBottomSheet(); + stateManager.closeBottomSheet(); }, onCreateComment: ( String text, { required bool isAnonymous, required bool postedByAdmin, }) { - cubit.createComment( + stateManager.createComment( text, onGetUserById, isAnonymous: isAnonymous, diff --git a/lib/src/presentation/pages/suggestion/suggestion_state.dart b/lib/src/presentation/pages/suggestion/suggestion_state.dart index fedbc4b..6b85f24 100644 --- a/lib/src/presentation/pages/suggestion/suggestion_state.dart +++ b/lib/src/presentation/pages/suggestion/suggestion_state.dart @@ -11,6 +11,7 @@ class SuggestionState extends Equatable { final SavingResultMessageType savingImageResultMessageType; final SuggestionBottomSheetType bottomSheetType; final bool loadingComments; + final String? selectedCommentId; const SuggestionState({ required this.isPopped, @@ -20,6 +21,7 @@ class SuggestionState extends Equatable { required this.savingImageResultMessageType, required this.bottomSheetType, required this.loadingComments, + this.selectedCommentId, }); SuggestionState newState({ @@ -30,6 +32,8 @@ class SuggestionState extends Equatable { SavingResultMessageType? savingImageResultMessageType, SuggestionBottomSheetType? bottomSheetType, bool? loadingComments, + String? selectedCommentId, + bool shouldResetSelectedCommentId = false, }) { return SuggestionState( isPopped: isPopped ?? this.isPopped, @@ -40,6 +44,8 @@ class SuggestionState extends Equatable { savingImageResultMessageType: savingImageResultMessageType ?? this.savingImageResultMessageType, loadingComments: loadingComments ?? this.loadingComments, + selectedCommentId: selectedCommentId ?? + (shouldResetSelectedCommentId ? null : this.selectedCommentId), ); } @@ -52,6 +58,7 @@ class SuggestionState extends Equatable { bottomSheetType, savingImageResultMessageType, loadingComments, + selectedCommentId, ]; } @@ -62,4 +69,5 @@ enum SuggestionBottomSheetType { editDelete, createEdit, createComment, + deleteCommentConfirmation, } diff --git a/lib/src/presentation/pages/suggestion/suggestion_cubit.dart b/lib/src/presentation/pages/suggestion/suggestion_state_manager.dart similarity index 62% rename from lib/src/presentation/pages/suggestion/suggestion_cubit.dart rename to lib/src/presentation/pages/suggestion/suggestion_state_manager.dart index a7fa1b2..a3bd8b7 100644 --- a/lib/src/presentation/pages/suggestion/suggestion_cubit.dart +++ b/lib/src/presentation/pages/suggestion/suggestion_state_manager.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:developer'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/material.dart'; import 'package:suggest_a_feature/src/domain/data_interfaces/suggestion_repository.dart'; import 'package:suggest_a_feature/src/domain/entities/comment.dart'; import 'package:suggest_a_feature/src/domain/entities/suggestion.dart'; @@ -11,39 +11,65 @@ import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_s import 'package:suggest_a_feature/src/presentation/utils/image_utils.dart'; import 'package:suggest_a_feature/src/presentation/utils/typedefs.dart'; -class SuggestionCubit extends Cubit { - final SuggestionRepository _suggestionRepository; +class SuggestionManager extends StatefulWidget { + final Suggestion suggestion; + final OnGetUserById onGetUserById; + final Widget child; + + const SuggestionManager({ + required this.suggestion, + required this.onGetUserById, + required this.child, + super.key, + }); + + static SuggestionStateManager of(BuildContext context) { + return (context.dependOnInheritedWidgetOfExactType<_InheritedSuggestion>()!) + .suggestionManager; + } + + @override + SuggestionStateManager createState() => SuggestionStateManager(); +} + +class SuggestionStateManager extends State { + late SuggestionState state; + late final SuggestionRepository _suggestionRepository; StreamSubscription>? _suggestionSubscription; - SuggestionCubit({ - required SuggestionRepository suggestionRepository, - required Suggestion suggestion, - required OnGetUserById onGetUserById, - }) : _suggestionRepository = suggestionRepository, - super( - SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: Suggestion.empty(), - loadingComments: true, - ), - ) { + @override + void initState() { + super.initState(); + _suggestionRepository = i.suggestionRepository; + state = SuggestionState( + isPopped: false, + isEditable: false, + author: const SuggestionAuthor.empty(), + savingImageResultMessageType: SavingResultMessageType.none, + bottomSheetType: SuggestionBottomSheetType.none, + suggestion: Suggestion.empty(), + loadingComments: true, + ); _init( - suggestion: suggestion, - getUserById: onGetUserById, + suggestion: widget.suggestion, + getUserById: widget.onGetUserById, isAdmin: i.isAdmin, ); } + @override + void dispose() { + _suggestionSubscription?.cancel(); + _suggestionSubscription = null; + super.dispose(); + } + void _init({ required Suggestion suggestion, required OnGetUserById getUserById, required bool isAdmin, }) { - emit( + _update( state.newState( suggestion: suggestion, isEditable: (i.userId == suggestion.authorId && @@ -68,10 +94,16 @@ class SuggestionCubit extends Cubit { final author = await getUserById(userId); if (author != null) { _suggestionRepository.userInfo[userId] = author; - emit(state.newState(author: author)); + _update( + state.newState(author: author), + ); } } else { - emit(state.newState(author: _suggestionRepository.userInfo[userId])); + _update( + state.newState( + author: _suggestionRepository.userInfo[userId], + ), + ); } } @@ -96,7 +128,7 @@ class SuggestionCubit extends Cubit { ..sort( (a, b) => b.creationTime.compareTo(a.creationTime), ); - emit( + _update( state.newState( suggestion: state.suggestion.copyWith(comments: extendedComments), ), @@ -108,18 +140,21 @@ class SuggestionCubit extends Cubit { } catch (e) { log('Comments loading error', error: e); } - emit(state.newState(loadingComments: false)); + _update( + state.newState(loadingComments: false), + ); } - @override - Future close() async { - _suggestionSubscription?.cancel(); - _suggestionSubscription = null; - await super.close(); + void _update(SuggestionState newState) { + if (newState != state) { + setState(() { + state = newState; + }); + } } void reset() { - emit( + _update( state.newState( savingImageResultMessageType: SavingResultMessageType.none, ), @@ -127,11 +162,15 @@ class SuggestionCubit extends Cubit { } void openCreateEditBottomSheet() { - emit(state.newState(bottomSheetType: SuggestionBottomSheetType.createEdit)); + _update( + state.newState( + bottomSheetType: SuggestionBottomSheetType.createEdit, + ), + ); } void openConfirmationBottomSheet() { - emit( + _update( state.newState( bottomSheetType: SuggestionBottomSheetType.confirmation, ), @@ -139,11 +178,15 @@ class SuggestionCubit extends Cubit { } void openEditDeleteBottomSheet() { - emit(state.newState(bottomSheetType: SuggestionBottomSheetType.editDelete)); + _update( + state.newState( + bottomSheetType: SuggestionBottomSheetType.editDelete, + ), + ); } void openNotificationBottomSheet() { - emit( + _update( state.newState( bottomSheetType: SuggestionBottomSheetType.notification, ), @@ -151,7 +194,7 @@ class SuggestionCubit extends Cubit { } void openCreateCommentBottomSheet() { - emit( + _update( state.newState( bottomSheetType: SuggestionBottomSheetType.createComment, ), @@ -159,14 +202,19 @@ class SuggestionCubit extends Cubit { } void closeBottomSheet() { - emit(state.newState(bottomSheetType: SuggestionBottomSheetType.none)); + _update( + state.newState( + bottomSheetType: SuggestionBottomSheetType.none, + ), + ); } void _onNewSuggestions(List suggestions) { - emit( + _update( state.newState( - suggestion: suggestions - .firstWhere((Suggestion e) => e.id == state.suggestion.id), + suggestion: suggestions.firstWhere( + (Suggestion e) => e.id == state.suggestion.id, + ), ), ); } @@ -174,7 +222,7 @@ class SuggestionCubit extends Cubit { Future showSavingResultMessage(Future isSuccess) async { final savingResult = await isSuccess; if (savingResult != null) { - emit( + _update( state.newState( savingImageResultMessageType: savingResult ? SavingResultMessageType.success @@ -184,6 +232,29 @@ class SuggestionCubit extends Cubit { } } + void openDeletingCommentConfirmation(String commentId) { + _update( + state.newState( + bottomSheetType: SuggestionBottomSheetType.deleteCommentConfirmation, + selectedCommentId: commentId, + ), + ); + } + + Future deleteComment() async { + final comments = state.suggestion.comments + .where((comment) => comment.id != state.selectedCommentId) + .toList() + ..sort((a, b) => b.creationTime.compareTo(a.creationTime)); + await _suggestionRepository.deleteCommentById(state.selectedCommentId!); + _update( + state.newState( + suggestion: state.suggestion.copyWith(comments: comments), + shouldResetSelectedCommentId: true, + ), + ); + } + Future createComment( String text, OnGetUserById getUserById, { @@ -203,13 +274,14 @@ class SuggestionCubit extends Cubit { final comments = [comment, ...state.suggestion.comments] ..sort((a, b) => b.creationTime.compareTo(a.creationTime)); - emit( + _update( state.newState( suggestion: state.suggestion.copyWith( comments: comments, ), ), ); + _suggestionRepository.refreshSuggestions( state.suggestion, saveComments: false, @@ -223,17 +295,21 @@ class SuggestionCubit extends Cubit { _suggestionSubscription?.cancel(); _suggestionSubscription = null; await _suggestionRepository.deleteSuggestion(state.suggestion.id); - emit(state.newState(isPopped: true)); + _update( + state.newState(isPopped: true), + ); } void vote() { final isVoted = state.suggestion.votedUserIds.contains(i.userId); - final newVotedUserIds = {...state.suggestion.votedUserIds}; + final newVotedUserIds = { + ...state.suggestion.votedUserIds, + }; isVoted ? _suggestionRepository.downvote(state.suggestion.id) : _suggestionRepository.upvote(state.suggestion.id); - emit( + _update( state.newState( suggestion: state.suggestion.copyWith( votedUserIds: !isVoted @@ -245,13 +321,15 @@ class SuggestionCubit extends Cubit { } Future changeNotification({required bool isNotificationOn}) async { - final newNotifyUserIds = {...state.suggestion.notifyUserIds}; + final newNotifyUserIds = { + ...state.suggestion.notifyUserIds, + }; isNotificationOn ? await _suggestionRepository.addNotifyToUpdateUser(state.suggestion.id) : await _suggestionRepository .deleteNotifyToUpdateUser(state.suggestion.id); - emit( + _update( state.newState( suggestion: state.suggestion.copyWith( notifyUserIds: isNotificationOn @@ -261,4 +339,24 @@ class SuggestionCubit extends Cubit { ), ); } + + @override + Widget build(BuildContext context) { + return _InheritedSuggestion( + suggestionManager: this, + child: widget.child, + ); + } +} + +class _InheritedSuggestion extends InheritedWidget { + final SuggestionStateManager suggestionManager; + + const _InheritedSuggestion({ + required this.suggestionManager, + required super.child, + }); + + @override + bool updateShouldNotify(_InheritedSuggestion old) => true; } diff --git a/lib/src/presentation/pages/suggestions/suggestions_cubit_scope.dart b/lib/src/presentation/pages/suggestions/suggestions_cubit_scope.dart deleted file mode 100644 index 514a8bb..0000000 --- a/lib/src/presentation/pages/suggestions/suggestions_cubit_scope.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:suggest_a_feature/src/presentation/di/injector.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/utils/typedefs.dart'; - -class SuggestionsCubitScope extends StatelessWidget { - final Widget child; - final SortType sortType; - - const SuggestionsCubitScope({ - required this.child, - required this.sortType, - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => SuggestionsCubit( - i.suggestionRepository, - sortType, - ), - child: child, - ); - } -} diff --git a/lib/src/presentation/pages/suggestions/suggestions_page.dart b/lib/src/presentation/pages/suggestions/suggestions_page.dart index 0740d95..bd441dd 100644 --- a/lib/src/presentation/pages/suggestions/suggestions_page.dart +++ b/lib/src/presentation/pages/suggestions/suggestions_page.dart @@ -1,17 +1,16 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:suggest_a_feature/src/presentation/di/injector.dart'; import 'package:suggest_a_feature/src/presentation/localization/localization_extensions.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_cubit_scope.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_state.dart'; +import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_state_manager.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestions/widgets/suggestion_list.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestions/widgets/suggestions_tab_bar.dart'; import 'package:suggest_a_feature/src/presentation/pages/theme/theme_extension.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/appbar_widget.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/bottom_sheets/sorting_bottom_sheet.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/fab.dart'; +import 'package:suggest_a_feature/src/presentation/pages/widgets/state_listener.dart'; import 'package:suggest_a_feature/src/presentation/utils/assets_strings.dart'; import 'package:suggest_a_feature/src/presentation/utils/dimensions.dart'; import 'package:suggest_a_feature/src/presentation/utils/platform_check.dart'; @@ -57,11 +56,15 @@ class SuggestionsPage extends StatefulWidget { /// Initial sorting type final SortType sortType; + /// [navigatorKey] of your Router + final GlobalKey? navigatorKey; + const SuggestionsPage({ required this.userId, required this.suggestionsDataSource, required this.theme, required this.onGetUserById, + this.navigatorKey, this.adminSettings, this.isAdmin = false, this.onSaveToGallery, @@ -92,66 +95,75 @@ class _SuggestionsPageState extends State { adminSettings: widget.adminSettings, isAdmin: widget.isAdmin, locale: widget.locale ?? SuggestionsPlatform.localeName(context), + navigatorKey: widget.navigatorKey, ); } @override Widget build(BuildContext context) { - return SuggestionsCubitScope( + return SuggestionsManager( sortType: widget.sortType, - child: BlocBuilder( - buildWhen: (previous, current) => - previous.type != current.type || - previous.activeTab != current.activeTab || - previous.sortType != current.sortType || - previous.loading != current.loading, - builder: (context, state) { - final cubit = context.read(); - return Stack( - children: [ - Scaffold( - appBar: SuggestionsAppBar( - onBackClick: Navigator.of(context).pop, - screenTitle: - widget.appBarTitle ?? localization.suggestAFeature, - ), - backgroundColor: context.theme.scaffoldBackgroundColor, - body: state.loading - ? const Center(child: CircularProgressIndicator()) - : Stack( - children: [ - _MainContent( - userId: widget.userId, - onTabChanged: (index) { - cubit.changeActiveTab( - SuggestionStatus.values[index], - ); - }, - activeTab: state.activeTab, - onGetUserById: widget.onGetUserById, - onSaveToGallery: widget.onSaveToGallery, - onUploadMultiplePhotos: - widget.onUploadMultiplePhotos, - ), - _BottomFab( - openCreateBottomSheet: cubit.openCreateBottomSheet, - ), - ], - ), - ), - if (state is CreateState) - _BottomSheet( - onSaveToGallery: widget.onSaveToGallery, - onUploadMultiplePhotos: widget.onUploadMultiplePhotos, - onCloseBottomSheet: cubit.closeBottomSheet, - ), - if (state is SortingState) - SortingBottomSheet( - closeBottomSheet: cubit.closeBottomSheet, - value: state.sortType, - onChanged: cubit.onSortTypeChanged, + child: Builder( + builder: (context) { + final stateManager = SuggestionsManager.of(context); + final state = stateManager.state; + + return StateListener( + state: state, + listenWhen: (previous, current) => + previous.type != current.type || + previous.activeTab != current.activeTab || + previous.sortType != current.sortType || + previous.loading != current.loading, + child: Stack( + children: [ + Scaffold( + appBar: SuggestionsAppBar( + onBackClick: () => + (i.navigatorKey?.currentState ?? Navigator.of(context)) + .pop(), + screenTitle: + widget.appBarTitle ?? localization.suggestAFeature, + ), + backgroundColor: context.theme.scaffoldBackgroundColor, + body: state.loading + ? const Center(child: CircularProgressIndicator()) + : Stack( + children: [ + _MainContent( + userId: widget.userId, + onTabChanged: (index) { + stateManager.changeActiveTab( + SuggestionStatus.values[index], + ); + }, + activeTab: state.activeTab, + onGetUserById: widget.onGetUserById, + onSaveToGallery: widget.onSaveToGallery, + onUploadMultiplePhotos: + widget.onUploadMultiplePhotos, + ), + _BottomFab( + openCreateBottomSheet: + stateManager.openCreateBottomSheet, + ), + ], + ), ), - ], + if (state is CreateState) + _BottomSheet( + onSaveToGallery: widget.onSaveToGallery, + onUploadMultiplePhotos: widget.onUploadMultiplePhotos, + onCloseBottomSheet: stateManager.closeBottomSheet, + ), + if (state is SortingState) + SortingBottomSheet( + closeBottomSheet: stateManager.closeBottomSheet, + value: state.sortType, + onChanged: stateManager.onSortTypeChanged, + ), + ], + ), ); }, ), @@ -201,6 +213,7 @@ class _MainContentState extends State<_MainContent> @override Widget build(BuildContext context) { + final stateManager = SuggestionsManager.of(context); return DefaultTabController( length: 5, child: Column( @@ -214,10 +227,9 @@ class _MainContentState extends State<_MainContent> onSaveToGallery: widget.onSaveToGallery, onGetUserById: widget.onGetUserById, userId: widget.userId, - onVote: context.read().vote, + onVote: stateManager.vote, tabController: _tabController, - openSortingBottomSheet: - context.read().openSortingBottomSheet, + openSortingBottomSheet: stateManager.openSortingBottomSheet, ), ], ), @@ -280,77 +292,68 @@ class _TabBarView extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.requests != current.requests || - previous.inProgress != current.inProgress || - previous.completed != current.completed || - previous.declined != current.declined || - previous.duplicated != current.duplicated, - builder: (context, state) { - return Expanded( - child: TabBarView( - controller: tabController, - children: [ - SuggestionList( - status: SuggestionStatus.requests, - suggestions: state.requests, - color: theme.requestsTabColor, - onGetUserById: onGetUserById, - onSaveToGallery: onSaveToGallery, - onUploadMultiplePhotos: onUploadMultiplePhotos, - userId: userId, - vote: (i) => onVote(SuggestionStatus.requests, i), - openSortingBottomSheet: openSortingBottomSheet, - ), - SuggestionList( - status: SuggestionStatus.inProgress, - suggestions: state.inProgress, - color: theme.inProgressTabColor, - onGetUserById: onGetUserById, - onSaveToGallery: onSaveToGallery, - onUploadMultiplePhotos: onUploadMultiplePhotos, - userId: userId, - vote: (i) => onVote(SuggestionStatus.inProgress, i), - openSortingBottomSheet: openSortingBottomSheet, - ), - SuggestionList( - status: SuggestionStatus.completed, - suggestions: state.completed, - color: theme.completedTabColor, - onGetUserById: onGetUserById, - onSaveToGallery: onSaveToGallery, - onUploadMultiplePhotos: onUploadMultiplePhotos, - userId: userId, - vote: (i) => onVote(SuggestionStatus.completed, i), - openSortingBottomSheet: openSortingBottomSheet, - ), - SuggestionList( - status: SuggestionStatus.declined, - suggestions: state.declined, - color: theme.declinedTabColor, - onGetUserById: onGetUserById, - onSaveToGallery: onSaveToGallery, - onUploadMultiplePhotos: onUploadMultiplePhotos, - userId: userId, - vote: (i) => onVote(SuggestionStatus.declined, i), - openSortingBottomSheet: openSortingBottomSheet, - ), - SuggestionList( - status: SuggestionStatus.duplicated, - suggestions: state.duplicated, - color: theme.duplicatedTabColor, - onGetUserById: onGetUserById, - onSaveToGallery: onSaveToGallery, - onUploadMultiplePhotos: onUploadMultiplePhotos, - userId: userId, - vote: (i) => onVote(SuggestionStatus.duplicated, i), - openSortingBottomSheet: openSortingBottomSheet, - ), - ], + final state = SuggestionsManager.of(context).state; + return Expanded( + child: TabBarView( + controller: tabController, + children: [ + SuggestionList( + status: SuggestionStatus.requests, + suggestions: state.requests, + color: theme.requestsTabColor, + onGetUserById: onGetUserById, + onSaveToGallery: onSaveToGallery, + onUploadMultiplePhotos: onUploadMultiplePhotos, + userId: userId, + vote: (i) => onVote(SuggestionStatus.requests, i), + openSortingBottomSheet: openSortingBottomSheet, ), - ); - }, + SuggestionList( + status: SuggestionStatus.inProgress, + suggestions: state.inProgress, + color: theme.inProgressTabColor, + onGetUserById: onGetUserById, + onSaveToGallery: onSaveToGallery, + onUploadMultiplePhotos: onUploadMultiplePhotos, + userId: userId, + vote: (i) => onVote(SuggestionStatus.inProgress, i), + openSortingBottomSheet: openSortingBottomSheet, + ), + SuggestionList( + status: SuggestionStatus.completed, + suggestions: state.completed, + color: theme.completedTabColor, + onGetUserById: onGetUserById, + onSaveToGallery: onSaveToGallery, + onUploadMultiplePhotos: onUploadMultiplePhotos, + userId: userId, + vote: (i) => onVote(SuggestionStatus.completed, i), + openSortingBottomSheet: openSortingBottomSheet, + ), + SuggestionList( + status: SuggestionStatus.declined, + suggestions: state.declined, + color: theme.declinedTabColor, + onGetUserById: onGetUserById, + onSaveToGallery: onSaveToGallery, + onUploadMultiplePhotos: onUploadMultiplePhotos, + userId: userId, + vote: (i) => onVote(SuggestionStatus.declined, i), + openSortingBottomSheet: openSortingBottomSheet, + ), + SuggestionList( + status: SuggestionStatus.duplicated, + suggestions: state.duplicated, + color: theme.duplicatedTabColor, + onGetUserById: onGetUserById, + onSaveToGallery: onSaveToGallery, + onUploadMultiplePhotos: onUploadMultiplePhotos, + userId: userId, + vote: (i) => onVote(SuggestionStatus.duplicated, i), + openSortingBottomSheet: openSortingBottomSheet, + ), + ], + ), ); } } diff --git a/lib/src/presentation/pages/suggestions/suggestions_cubit.dart b/lib/src/presentation/pages/suggestions/suggestions_state_manager.dart similarity index 64% rename from lib/src/presentation/pages/suggestions/suggestions_cubit.dart rename to lib/src/presentation/pages/suggestions/suggestions_state_manager.dart index bf0ff7c..b22b992 100644 --- a/lib/src/presentation/pages/suggestions/suggestions_cubit.dart +++ b/lib/src/presentation/pages/suggestions/suggestions_state_manager.dart @@ -1,29 +1,50 @@ import 'dart:async'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/material.dart'; import 'package:suggest_a_feature/src/domain/data_interfaces/suggestion_repository.dart'; import 'package:suggest_a_feature/src/domain/entities/suggestion.dart'; -import 'package:suggest_a_feature/src/presentation/di/injector.dart' - as injector; +import 'package:suggest_a_feature/src/presentation/di/injector.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_state.dart'; import 'package:suggest_a_feature/src/presentation/utils/typedefs.dart'; -class SuggestionsCubit extends Cubit { - final SuggestionRepository _suggestionRepository; +class SuggestionsManager extends StatefulWidget { + final SortType sortType; + final Widget child; + + const SuggestionsManager({ + required this.sortType, + required this.child, + super.key, + }); + + static SuggestionsStateManager of(BuildContext context) { + return (context + .dependOnInheritedWidgetOfExactType<_InheritedSuggestions>()!) + .suggestionManager; + } + + @override + SuggestionsStateManager createState() => SuggestionsStateManager(); +} + +class SuggestionsStateManager extends State { + late SuggestionsState state; + late final SuggestionRepository _suggestionRepository; StreamSubscription>? _suggestionSubscription; - SuggestionsCubit(this._suggestionRepository, SortType sortType) - : super( - SuggestionsState( - requests: const [], - inProgress: const [], - completed: const [], - declined: const [], - duplicated: const [], - sortType: sortType, - loading: true, - ), - ) { + @override + void initState() { + super.initState(); + _suggestionRepository = i.suggestionRepository; + state = SuggestionsState( + requests: const [], + inProgress: const [], + completed: const [], + declined: const [], + duplicated: const [], + sortType: widget.sortType, + loading: true, + ); _init(); } @@ -33,20 +54,28 @@ class SuggestionsCubit extends Cubit { _onNewSuggestions, ); await _suggestionRepository.initSuggestions(); - emit(state.newState(loading: false)); + _update(state.newState(loading: false)); } @override - Future close() async { + void dispose() { _suggestionSubscription?.cancel(); _suggestionSubscription = null; - await super.close(); + super.dispose(); + } + + void _update(SuggestionsState newState) { + if (newState != state) { + setState(() { + state = newState; + }); + } } Future _onNewSuggestions(List suggestions) async { suggestions.sort(state.sortType.sortFunction); - emit( + _update( state.newState( requests: suggestions .where((s) => s.status == SuggestionStatus.requests) @@ -84,14 +113,14 @@ class SuggestionsCubit extends Cubit { } void _voteForSuggestion(Suggestion suggestion) { - final isVoted = suggestion.votedUserIds.contains(injector.i.userId); + final isVoted = suggestion.votedUserIds.contains(i.userId); isVoted ? _suggestionRepository.downvote(suggestion.id) : _suggestionRepository.upvote(suggestion.id); } - void openCreateBottomSheet() => emit( + void openCreateBottomSheet() => _update( CreateState( requests: state.requests, inProgress: state.inProgress, @@ -104,7 +133,7 @@ class SuggestionsCubit extends Cubit { ), ); - void closeBottomSheet() => emit( + void closeBottomSheet() => _update( SuggestionsState( requests: state.requests, inProgress: state.inProgress, @@ -117,9 +146,9 @@ class SuggestionsCubit extends Cubit { ); void changeActiveTab(SuggestionStatus activeTab) => - emit(state.newState(activeTab: activeTab)); + _update(state.newState(activeTab: activeTab)); - void openSortingBottomSheet() => emit( + void openSortingBottomSheet() => _update( SortingState( requests: state.requests, inProgress: state.inProgress, @@ -133,8 +162,28 @@ class SuggestionsCubit extends Cubit { void onSortTypeChanged(SortType sortType) { if (sortType != state.sortType) { - emit(state.newState(sortType: sortType)); + _update(state.newState(sortType: sortType)); _onNewSuggestions(_suggestionRepository.suggestions); } } + + @override + Widget build(BuildContext context) { + return _InheritedSuggestions( + suggestionManager: this, + child: widget.child, + ); + } +} + +class _InheritedSuggestions extends InheritedWidget { + final SuggestionsStateManager suggestionManager; + + const _InheritedSuggestions({ + required this.suggestionManager, + required super.child, + }); + + @override + bool updateShouldNotify(_InheritedSuggestions old) => true; } diff --git a/lib/src/presentation/pages/suggestions/widgets/suggestion_list.dart b/lib/src/presentation/pages/suggestions/widgets/suggestion_list.dart index 4945c12..856898b 100644 --- a/lib/src/presentation/pages/suggestions/widgets/suggestion_list.dart +++ b/lib/src/presentation/pages/suggestions/widgets/suggestion_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:suggest_a_feature/src/presentation/di/injector.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_page.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestions/widgets/list_description.dart'; import 'package:suggest_a_feature/src/presentation/pages/suggestions/widgets/suggestion_card.dart'; @@ -95,7 +96,8 @@ class _ListItem extends StatelessWidget { color: color, status: status, index: index - 1, - onClick: () => Navigator.of(context).push( + onClick: () => + (i.navigatorKey?.currentState ?? Navigator.of(context)).push( CupertinoPageRoute( builder: (_) => SuggestionPage( suggestion: suggestions[index - 1], diff --git a/lib/src/presentation/pages/widgets/photo_view.dart b/lib/src/presentation/pages/widgets/photo_view.dart index 98d219b..2d6e222 100644 --- a/lib/src/presentation/pages/widgets/photo_view.dart +++ b/lib/src/presentation/pages/widgets/photo_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:suggest_a_feature/src/presentation/di/injector.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/icon_button.dart'; import 'package:suggest_a_feature/src/presentation/pages/widgets/zoomable_image.dart'; import 'package:suggest_a_feature/src/presentation/utils/assets_strings.dart'; @@ -146,7 +147,8 @@ class _ActionButtons extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SuggestionsIconButton( - onClick: Navigator.of(context).pop, + onClick: () => + (i.navigatorKey?.currentState ?? Navigator.of(context)).pop(), imageIcon: AssetStrings.backIconImage, color: Colors.white, ), @@ -156,7 +158,8 @@ class _ActionButtons extends StatelessWidget { SuggestionsIconButton( imageIcon: AssetStrings.deleteIconImage, onClick: () { - Navigator.of(context).pop(); + (i.navigatorKey?.currentState ?? Navigator.of(context)) + .pop(); onDeleteClick!(photos[currentIndex]); }, color: Colors.white, diff --git a/lib/src/presentation/pages/widgets/state_listener.dart b/lib/src/presentation/pages/widgets/state_listener.dart new file mode 100644 index 0000000..183a128 --- /dev/null +++ b/lib/src/presentation/pages/widgets/state_listener.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class StateListener extends StatefulWidget { + final T state; + final Widget child; + final ValueChanged? listener; + final bool Function(T, T)? listenWhen; + + const StateListener({ + required this.state, + required this.child, + super.key, + this.listener, + this.listenWhen, + }); + + @override + State> createState() => _StateListenerState(); +} + +class _StateListenerState extends State> { + @override + void didUpdateWidget(StateListener oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.listener != null && + (widget.listenWhen == null || + widget.listenWhen!(oldWidget.state, widget.state))) { + WidgetsBinding.instance + .addPostFrameCallback((_) => widget.listener!(widget.state)); + } + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 8685811..b74227f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: suggest_a_feature description: Ready-made Flutter package for collecting suggestions from users. -version: 0.3.1 +version: 0.4.0 repository: https://github.com/What-the-Flutter/Suggest-a-Feature homepage: https://pub.dev/packages/suggest_a_feature @@ -17,17 +17,14 @@ dependencies: equatable: ^2.0.5 flutter: sdk: flutter - flutter_bloc: ^8.1.3 flutter_svg: ^2.0.7 intl: ^0.18.0 wtf_sliding_sheet: ^1.1.1 dev_dependencies: - bloc_test: ^9.1.3 build_runner: ^2.4.6 flutter_test: sdk: flutter - mockito: ^5.4.2 very_good_analysis: ^5.0.0+1 flutter: diff --git a/test/presentation/cubits/suggestion_cubit_test.dart b/test/presentation/cubits/suggestion_cubit_test.dart deleted file mode 100644 index 290a32f..0000000 --- a/test/presentation/cubits/suggestion_cubit_test.dart +++ /dev/null @@ -1,314 +0,0 @@ -import 'package:bloc_test/bloc_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; -import 'package:suggest_a_feature/src/presentation/di/injector.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_state.dart'; -import 'package:suggest_a_feature/src/presentation/utils/image_utils.dart'; -import 'package:suggest_a_feature/suggest_a_feature.dart'; - -import '../../utils/mocked_entities.dart'; -import '../../utils/shared_mocks.mocks.dart'; - -void main() { - group( - 'suggestion cubit', - () { - final mockSuggestionRepository = MockSuggestionRepositoryImpl(); - final mockSuggestionsTheme = MockSuggestionsTheme(); - final mockSuggestionsDataSource = MockSuggestionsDataSource(); - - final emptySuggestionState = SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion, - loadingComments: false, - ); - - final commentedSuggestion = - mockedRequestSuggestion.copyWith(comments: [mockedComment]); - - setUp(() { - i.init( - theme: mockSuggestionsTheme, - userId: '1', - suggestionsDataSource: mockSuggestionsDataSource, - locale: 'en', - ); - }); - - blocTest( - 'create comment', - build: () { - when(mockSuggestionRepository.createComment(mockedCreateCommentModel)) - .thenAnswer((_) async => mockedComment); - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => emptySuggestionState, - act: (SuggestionCubit cubit) => cubit.createComment( - 'Comment1', - (String id) async => mockedSuggestionAuthor, - isAnonymous: true, - postedByAdmin: false, - ), - expect: () => [ - SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: commentedSuggestion, - loadingComments: false, - ), - ], - ); - - blocTest( - 'delete suggestion', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => emptySuggestionState, - act: (SuggestionCubit cubit) => cubit.deleteSuggestion(), - expect: () => [ - SuggestionState( - isPopped: true, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion, - loadingComments: false, - ), - ], - ); - - blocTest( - 'upvote', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => emptySuggestionState, - act: (SuggestionCubit cubit) => cubit.vote(), - expect: () => [ - SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion.copyWith( - votedUserIds: {mockedSuggestionAuthor.id}, - ), - loadingComments: false, - ), - emptySuggestionState, - ], - ); - - blocTest( - 'downvote', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion.copyWith( - votedUserIds: {mockedSuggestionAuthor.id}, - ), - loadingComments: false, - ), - act: (SuggestionCubit cubit) => cubit.vote(), - expect: () => [emptySuggestionState], - ); - - blocTest( - 'add notification', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => emptySuggestionState, - act: (SuggestionCubit cubit) => cubit.changeNotification( - isNotificationOn: true, - ), - expect: () => [ - SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion.copyWith( - notifyUserIds: {mockedSuggestionAuthor.id}, - ), - loadingComments: false, - ), - ], - ); - - blocTest( - 'delete notification', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion.copyWith( - notifyUserIds: {mockedSuggestionAuthor.id}, - ), - loadingComments: false, - ), - act: (SuggestionCubit cubit) => cubit.changeNotification( - isNotificationOn: false, - ), - expect: () => [emptySuggestionState], - ); - - blocTest( - 'show successful result message', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => emptySuggestionState, - act: (SuggestionCubit cubit) => - cubit.showSavingResultMessage(Future.value(true)), - expect: () => [ - SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.success, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion, - loadingComments: false, - ), - ], - ); - - blocTest( - 'show failed result message', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => emptySuggestionState, - act: (SuggestionCubit cubit) => - cubit.showSavingResultMessage(Future.value(false)), - expect: () => [ - SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.fail, - bottomSheetType: SuggestionBottomSheetType.none, - suggestion: mockedRequestSuggestion, - loadingComments: false, - ), - ], - ); - - blocTest( - 'open Bottom Sheet', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value([mockedRequestSuggestion]), - ); - when(mockSuggestionRepository.userInfo).thenAnswer((_) => {}); - return SuggestionCubit( - suggestionRepository: mockSuggestionRepository, - suggestion: mockedRequestSuggestion, - onGetUserById: (_) => Future.value(const SuggestionAuthor.empty()), - ); - }, - seed: () => emptySuggestionState, - act: (SuggestionCubit cubit) => cubit.openCreateCommentBottomSheet(), - expect: () => [ - SuggestionState( - isPopped: false, - isEditable: false, - author: const SuggestionAuthor.empty(), - savingImageResultMessageType: SavingResultMessageType.none, - bottomSheetType: SuggestionBottomSheetType.createComment, - suggestion: mockedRequestSuggestion, - loadingComments: false, - ), - ], - ); - }, - ); -} diff --git a/test/presentation/cubits/suggestions_cubit_test.dart b/test/presentation/cubits/suggestions_cubit_test.dart deleted file mode 100644 index 61a96ba..0000000 --- a/test/presentation/cubits/suggestions_cubit_test.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'package:bloc_test/bloc_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; -import 'package:suggest_a_feature/src/domain/entities/suggestion.dart'; -import 'package:suggest_a_feature/src/domain/utils/simple_behavior_subject.dart'; -import 'package:suggest_a_feature/src/presentation/di/injector.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_state.dart'; -import 'package:suggest_a_feature/src/presentation/utils/typedefs.dart'; - -import '../../utils/mocked_entities.dart'; -import '../../utils/shared_mocks.mocks.dart'; - -void main() { - group( - 'suggestions cubit', - () { - final mockSuggestionsTheme = MockSuggestionsTheme(); - final mockSuggestionsDataSource = MockSuggestionsDataSource(); - final mockSuggestionRepository = MockSuggestionRepositoryImpl(); - const mockSortType = SortType.upvotes; - final emptySuggestionsState = SuggestionsState( - requests: [mockedRequestSuggestion, mockedRequestSuggestion2], - inProgress: [mockedInProgressSuggestion, mockedInProgressSuggestion2], - completed: [mockedCompletedSuggestion, mockedCompletedSuggestion2], - declined: const [], - duplicated: const [], - sortType: SortType.upvotes, - loading: false, - ); - final mockedSuggestions = [ - mockedRequestSuggestion, - mockedInProgressSuggestion, - mockedCompletedSuggestion, - mockedRequestSuggestion2, - mockedInProgressSuggestion2, - mockedCompletedSuggestion2, - ]; - final upvotedSuggestion = mockedRequestSuggestion2.copyWith( - votedUserIds: {mockedSuggestionAuthor.id}, - ); - - setUp(() { - i.init( - theme: mockSuggestionsTheme, - userId: mockedSuggestionAuthor.id, - suggestionsDataSource: mockSuggestionsDataSource, - locale: 'en', - ); - }); - - blocTest( - 'open CreateBottomSheet', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value(mockedSuggestions), - ); - return SuggestionsCubit(mockSuggestionRepository, mockSortType); - }, - seed: () => emptySuggestionsState, - act: (cubit) => cubit.openCreateBottomSheet(), - expect: () => [ - CreateState( - requests: emptySuggestionsState.requests, - inProgress: emptySuggestionsState.inProgress, - completed: emptySuggestionsState.completed, - declined: emptySuggestionsState.declined, - duplicated: emptySuggestionsState.duplicated, - sortType: emptySuggestionsState.sortType, - activeTab: emptySuggestionsState.activeTab, - loading: emptySuggestionsState.loading, - ), - ], - ); - - blocTest( - 'close CreateBottomSheet', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value(mockedSuggestions), - ); - return SuggestionsCubit(mockSuggestionRepository, mockSortType); - }, - seed: () => CreateState( - requests: emptySuggestionsState.requests, - inProgress: emptySuggestionsState.inProgress, - completed: emptySuggestionsState.completed, - declined: emptySuggestionsState.declined, - duplicated: emptySuggestionsState.duplicated, - sortType: emptySuggestionsState.sortType, - activeTab: emptySuggestionsState.activeTab, - loading: emptySuggestionsState.loading, - ), - act: (cubit) => cubit.closeBottomSheet(), - expect: () => [ - emptySuggestionsState, - ], - ); - - blocTest( - 'change active tab', - build: () { - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => Stream.value(mockedSuggestions), - ); - return SuggestionsCubit(mockSuggestionRepository, mockSortType); - }, - seed: () => emptySuggestionsState.newState( - activeTab: SuggestionStatus.inProgress, - ), - act: (cubit) => cubit.changeActiveTab(SuggestionStatus.completed), - expect: () => [ - emptySuggestionsState.newState( - activeTab: SuggestionStatus.completed, - ), - ], - ); - - blocTest( - 'vote requested suggestion', - build: () { - final dataStream = SimpleBehaviorSubject([ - mockedRequestSuggestion, - mockedRequestSuggestion2, - ]); - - when(mockSuggestionRepository.suggestionsStream).thenAnswer( - (_) => dataStream.stream(), - ); - - when(mockSuggestionRepository.upvote(any)).thenAnswer( - (_) async => - dataStream.value = [mockedRequestSuggestion, upvotedSuggestion], - ); - - return SuggestionsCubit(mockSuggestionRepository, mockSortType); - }, - seed: () => SuggestionsState( - requests: [mockedRequestSuggestion, mockedRequestSuggestion2], - inProgress: const [], - completed: const [], - declined: const [], - duplicated: const [], - sortType: SortType.upvotes, - loading: false, - ), - act: (cubit) { - cubit.vote(SuggestionStatus.requests, 1); - }, - expect: () => [ - SuggestionsState( - requests: [upvotedSuggestion, mockedRequestSuggestion], - inProgress: const [], - completed: const [], - declined: const [], - duplicated: const [], - sortType: SortType.upvotes, - loading: false, - ), - ], - ); - }, - ); -} diff --git a/test/utils/mocked_entities.dart b/test/utils/mocked_entities.dart deleted file mode 100644 index cf43ce3..0000000 --- a/test/utils/mocked_entities.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:suggest_a_feature/suggest_a_feature.dart'; - -const SuggestionAuthor mockedSuggestionAuthor = SuggestionAuthor( - id: '1', - username: 'username', -); - -final CreateSuggestionModel mockedCreateSuggestionModel = CreateSuggestionModel( - title: 'Suggestion', - description: 'Description', - labels: [], - images: [], - authorId: '1', - isAnonymous: false, -); - -final Suggestion mockedRequestSuggestion = Suggestion( - id: '1', - title: 'Suggestion', - description: 'Description', - authorId: '1', - isAnonymous: false, - creationTime: DateTime(2022), - status: SuggestionStatus.requests, -); - -final Suggestion mockedRequestSuggestion2 = Suggestion( - id: '2', - title: 'Suggestion2', - description: 'Description2', - authorId: '1', - isAnonymous: true, - creationTime: DateTime(2022), - status: SuggestionStatus.requests, -); - -final Suggestion mockedInProgressSuggestion = Suggestion( - id: '3', - title: 'Suggestion3', - description: 'Description3', - authorId: '1', - isAnonymous: false, - creationTime: DateTime(2022), - status: SuggestionStatus.inProgress, -); - -final Suggestion mockedInProgressSuggestion2 = Suggestion( - id: '4', - title: 'Suggestion4', - description: 'Description4', - authorId: '1', - isAnonymous: true, - creationTime: DateTime(2022), - status: SuggestionStatus.inProgress, -); - -final Suggestion mockedCompletedSuggestion = Suggestion( - id: '5', - title: 'Suggestion5', - description: 'Description5', - authorId: '1', - isAnonymous: true, - creationTime: DateTime(2022), - status: SuggestionStatus.completed, -); - -final Suggestion mockedCompletedSuggestion2 = Suggestion( - id: '6', - title: 'Suggestion6', - description: 'Description6', - authorId: '1', - isAnonymous: false, - creationTime: DateTime(2022), - status: SuggestionStatus.completed, -); - -final Comment mockedComment = Comment( - id: '1', - suggestionId: '1', - author: mockedSuggestionAuthor, - isAnonymous: true, - text: 'Comment1', - creationTime: DateTime(2022), - isFromAdmin: false, -); - -final CreateCommentModel mockedCreateCommentModel = CreateCommentModel( - authorId: mockedComment.author.id, - isAnonymous: mockedComment.isAnonymous, - text: mockedComment.text, - suggestionId: mockedComment.suggestionId, - isFromAdmin: false, -); diff --git a/test/utils/shared_mocks.dart b/test/utils/shared_mocks.dart deleted file mode 100644 index 234fecd..0000000 --- a/test/utils/shared_mocks.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:mockito/annotations.dart'; -import 'package:suggest_a_feature/src/data/interfaces/suggestions_data_source.dart'; -import 'package:suggest_a_feature/src/data/repositories/suggestion_repository.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_cubit.dart'; -import 'package:suggest_a_feature/src/presentation/pages/theme/suggestions_theme.dart'; - -@GenerateMocks([ - SuggestionRepositoryImpl, - SuggestionCubit, - SuggestionsCubit, - SuggestionsDataSource, - SuggestionsTheme, -]) -class SharedMocks {} diff --git a/test/utils/shared_mocks.mocks.dart b/test/utils/shared_mocks.mocks.dart deleted file mode 100644 index 6b0b7ba..0000000 --- a/test/utils/shared_mocks.mocks.dart +++ /dev/null @@ -1,1305 +0,0 @@ -// Mocks generated by Mockito 5.4.1 from annotations -// in suggest_a_feature/test/utils/shared_mocks.dart. -// Do not manually edit this file. - -// @dart=2.19 - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i10; -import 'dart:ui' as _i6; - -import 'package:flutter/material.dart' as _i7; -import 'package:flutter_bloc/flutter_bloc.dart' as _i14; -import 'package:mockito/mockito.dart' as _i1; -import 'package:suggest_a_feature/src/data/interfaces/suggestions_data_source.dart' - as _i16; -import 'package:suggest_a_feature/src/data/repositories/suggestion_repository.dart' - as _i9; -import 'package:suggest_a_feature/src/domain/entities/comment.dart' as _i3; -import 'package:suggest_a_feature/src/domain/entities/suggestion.dart' as _i2; -import 'package:suggest_a_feature/src/domain/entities/suggestion_author.dart' - as _i11; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_cubit.dart' - as _i12; -import 'package:suggest_a_feature/src/presentation/pages/suggestion/suggestion_state.dart' - as _i4; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_cubit.dart' - as _i15; -import 'package:suggest_a_feature/src/presentation/pages/suggestions/suggestions_state.dart' - as _i5; -import 'package:suggest_a_feature/src/presentation/pages/theme/suggestions_theme.dart' - as _i8; -import 'package:suggest_a_feature/src/presentation/utils/typedefs.dart' as _i13; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -class _FakeSuggestion_0 extends _i1.SmartFake implements _i2.Suggestion { - _FakeSuggestion_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeComment_1 extends _i1.SmartFake implements _i3.Comment { - _FakeComment_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeSuggestionState_2 extends _i1.SmartFake - implements _i4.SuggestionState { - _FakeSuggestionState_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeSuggestionsState_3 extends _i1.SmartFake - implements _i5.SuggestionsState { - _FakeSuggestionsState_3( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeColor_4 extends _i1.SmartFake implements _i6.Color { - _FakeColor_4( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeTextStyle_5 extends _i1.SmartFake implements _i7.TextStyle { - _FakeTextStyle_5( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); - - @override - String toString({_i7.DiagnosticLevel? minLevel = _i7.DiagnosticLevel.info}) => - super.toString(); -} - -class _FakeSuggestionsTheme_6 extends _i1.SmartFake - implements _i8.SuggestionsTheme { - _FakeSuggestionsTheme_6( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -/// A class which mocks [SuggestionRepositoryImpl]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockSuggestionRepositoryImpl extends _i1.Mock - implements _i9.SuggestionRepositoryImpl { - MockSuggestionRepositoryImpl() { - _i1.throwOnMissingStub(this); - } - - @override - _i10.Stream> get suggestionsStream => - (super.noSuchMethod( - Invocation.getter(#suggestionsStream), - returnValue: _i10.Stream>.empty(), - ) as _i10.Stream>); - @override - List<_i2.Suggestion> get suggestions => (super.noSuchMethod( - Invocation.getter(#suggestions), - returnValue: <_i2.Suggestion>[], - ) as List<_i2.Suggestion>); - @override - Map get userInfo => (super.noSuchMethod( - Invocation.getter(#userInfo), - returnValue: {}, - ) as Map); - @override - void refreshSuggestions( - _i2.Suggestion? suggestion, { - bool? saveComments = true, - }) => - super.noSuchMethod( - Invocation.method( - #refreshSuggestions, - [suggestion], - {#saveComments: saveComments}, - ), - returnValueForMissingStub: null, - ); - @override - _i10.Future initSuggestions() => (super.noSuchMethod( - Invocation.method( - #initSuggestions, - [], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future<_i2.Suggestion> getSuggestionById(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #getSuggestionById, - [suggestionId], - ), - returnValue: _i10.Future<_i2.Suggestion>.value(_FakeSuggestion_0( - this, - Invocation.method( - #getSuggestionById, - [suggestionId], - ), - )), - ) as _i10.Future<_i2.Suggestion>); - @override - _i10.Future<_i2.Suggestion> createSuggestion( - _i2.CreateSuggestionModel? suggestion) => - (super.noSuchMethod( - Invocation.method( - #createSuggestion, - [suggestion], - ), - returnValue: _i10.Future<_i2.Suggestion>.value(_FakeSuggestion_0( - this, - Invocation.method( - #createSuggestion, - [suggestion], - ), - )), - ) as _i10.Future<_i2.Suggestion>); - @override - _i10.Future<_i2.Suggestion> updateSuggestion(_i2.Suggestion? suggestion) => - (super.noSuchMethod( - Invocation.method( - #updateSuggestion, - [suggestion], - ), - returnValue: _i10.Future<_i2.Suggestion>.value(_FakeSuggestion_0( - this, - Invocation.method( - #updateSuggestion, - [suggestion], - ), - )), - ) as _i10.Future<_i2.Suggestion>); - @override - _i10.Future deleteSuggestion(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #deleteSuggestion, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future addNotifyToUpdateUser(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #addNotifyToUpdateUser, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future deleteNotifyToUpdateUser(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #deleteNotifyToUpdateUser, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future downvote(String? suggestionId) => (super.noSuchMethod( - Invocation.method( - #downvote, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future upvote(String? suggestionId) => (super.noSuchMethod( - Invocation.method( - #upvote, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future> getAllComments(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #getAllComments, - [suggestionId], - ), - returnValue: _i10.Future>.value(<_i3.Comment>[]), - ) as _i10.Future>); - @override - _i10.Future<_i3.Comment> createComment(_i3.CreateCommentModel? comment) => - (super.noSuchMethod( - Invocation.method( - #createComment, - [comment], - ), - returnValue: _i10.Future<_i3.Comment>.value(_FakeComment_1( - this, - Invocation.method( - #createComment, - [comment], - ), - )), - ) as _i10.Future<_i3.Comment>); -} - -/// A class which mocks [SuggestionCubit]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockSuggestionCubit extends _i1.Mock implements _i12.SuggestionCubit { - MockSuggestionCubit() { - _i1.throwOnMissingStub(this); - } - - @override - _i4.SuggestionState get state => (super.noSuchMethod( - Invocation.getter(#state), - returnValue: _FakeSuggestionState_2( - this, - Invocation.getter(#state), - ), - ) as _i4.SuggestionState); - @override - _i10.Stream<_i4.SuggestionState> get stream => (super.noSuchMethod( - Invocation.getter(#stream), - returnValue: _i10.Stream<_i4.SuggestionState>.empty(), - ) as _i10.Stream<_i4.SuggestionState>); - @override - bool get isClosed => (super.noSuchMethod( - Invocation.getter(#isClosed), - returnValue: false, - ) as bool); - @override - _i10.Future close() => (super.noSuchMethod( - Invocation.method( - #close, - [], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - void reset() => super.noSuchMethod( - Invocation.method( - #reset, - [], - ), - returnValueForMissingStub: null, - ); - @override - void openCreateEditBottomSheet() => super.noSuchMethod( - Invocation.method( - #openCreateEditBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - void openConfirmationBottomSheet() => super.noSuchMethod( - Invocation.method( - #openConfirmationBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - void openEditDeleteBottomSheet() => super.noSuchMethod( - Invocation.method( - #openEditDeleteBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - void openNotificationBottomSheet() => super.noSuchMethod( - Invocation.method( - #openNotificationBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - void openCreateCommentBottomSheet() => super.noSuchMethod( - Invocation.method( - #openCreateCommentBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - void closeBottomSheet() => super.noSuchMethod( - Invocation.method( - #closeBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - _i10.Future showSavingResultMessage(_i10.Future? isSuccess) => - (super.noSuchMethod( - Invocation.method( - #showSavingResultMessage, - [isSuccess], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future createComment( - String? text, - _i13.OnGetUserById? getUserById, { - required bool? isAnonymous, - required bool? postedByAdmin, - }) => - (super.noSuchMethod( - Invocation.method( - #createComment, - [ - text, - getUserById, - ], - { - #isAnonymous: isAnonymous, - #postedByAdmin: postedByAdmin, - }, - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future deleteSuggestion() => (super.noSuchMethod( - Invocation.method( - #deleteSuggestion, - [], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - void vote() => super.noSuchMethod( - Invocation.method( - #vote, - [], - ), - returnValueForMissingStub: null, - ); - @override - _i10.Future changeNotification({required bool? isNotificationOn}) => - (super.noSuchMethod( - Invocation.method( - #changeNotification, - [], - {#isNotificationOn: isNotificationOn}, - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - void emit(_i4.SuggestionState? state) => super.noSuchMethod( - Invocation.method( - #emit, - [state], - ), - returnValueForMissingStub: null, - ); - @override - void onChange(_i14.Change<_i4.SuggestionState>? change) => super.noSuchMethod( - Invocation.method( - #onChange, - [change], - ), - returnValueForMissingStub: null, - ); - @override - void addError( - Object? error, [ - StackTrace? stackTrace, - ]) => - super.noSuchMethod( - Invocation.method( - #addError, - [ - error, - stackTrace, - ], - ), - returnValueForMissingStub: null, - ); - @override - void onError( - Object? error, - StackTrace? stackTrace, - ) => - super.noSuchMethod( - Invocation.method( - #onError, - [ - error, - stackTrace, - ], - ), - returnValueForMissingStub: null, - ); -} - -/// A class which mocks [SuggestionsCubit]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockSuggestionsCubit extends _i1.Mock implements _i15.SuggestionsCubit { - MockSuggestionsCubit() { - _i1.throwOnMissingStub(this); - } - - @override - _i5.SuggestionsState get state => (super.noSuchMethod( - Invocation.getter(#state), - returnValue: _FakeSuggestionsState_3( - this, - Invocation.getter(#state), - ), - ) as _i5.SuggestionsState); - @override - _i10.Stream<_i5.SuggestionsState> get stream => (super.noSuchMethod( - Invocation.getter(#stream), - returnValue: _i10.Stream<_i5.SuggestionsState>.empty(), - ) as _i10.Stream<_i5.SuggestionsState>); - @override - bool get isClosed => (super.noSuchMethod( - Invocation.getter(#isClosed), - returnValue: false, - ) as bool); - @override - _i10.Future close() => (super.noSuchMethod( - Invocation.method( - #close, - [], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - void vote( - _i2.SuggestionStatus? status, - int? i, - ) => - super.noSuchMethod( - Invocation.method( - #vote, - [ - status, - i, - ], - ), - returnValueForMissingStub: null, - ); - @override - void openCreateBottomSheet() => super.noSuchMethod( - Invocation.method( - #openCreateBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - void closeCreateBottomSheet() => super.noSuchMethod( - Invocation.method( - #closeCreateBottomSheet, - [], - ), - returnValueForMissingStub: null, - ); - @override - void changeActiveTab(_i2.SuggestionStatus? activeTab) => super.noSuchMethod( - Invocation.method( - #changeActiveTab, - [activeTab], - ), - returnValueForMissingStub: null, - ); - @override - void emit(_i5.SuggestionsState? state) => super.noSuchMethod( - Invocation.method( - #emit, - [state], - ), - returnValueForMissingStub: null, - ); - @override - void onChange(_i14.Change<_i5.SuggestionsState>? change) => - super.noSuchMethod( - Invocation.method( - #onChange, - [change], - ), - returnValueForMissingStub: null, - ); - @override - void addError( - Object? error, [ - StackTrace? stackTrace, - ]) => - super.noSuchMethod( - Invocation.method( - #addError, - [ - error, - stackTrace, - ], - ), - returnValueForMissingStub: null, - ); - @override - void onError( - Object? error, - StackTrace? stackTrace, - ) => - super.noSuchMethod( - Invocation.method( - #onError, - [ - error, - stackTrace, - ], - ), - returnValueForMissingStub: null, - ); -} - -/// A class which mocks [SuggestionsDataSource]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockSuggestionsDataSource extends _i1.Mock - implements _i16.SuggestionsDataSource { - MockSuggestionsDataSource() { - _i1.throwOnMissingStub(this); - } - - @override - String get userId => (super.noSuchMethod( - Invocation.getter(#userId), - returnValue: '', - ) as String); - @override - _i10.Future<_i2.Suggestion> createSuggestion( - _i2.CreateSuggestionModel? suggestion) => - (super.noSuchMethod( - Invocation.method( - #createSuggestion, - [suggestion], - ), - returnValue: _i10.Future<_i2.Suggestion>.value(_FakeSuggestion_0( - this, - Invocation.method( - #createSuggestion, - [suggestion], - ), - )), - ) as _i10.Future<_i2.Suggestion>); - @override - _i10.Future<_i2.Suggestion> getSuggestionById(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #getSuggestionById, - [suggestionId], - ), - returnValue: _i10.Future<_i2.Suggestion>.value(_FakeSuggestion_0( - this, - Invocation.method( - #getSuggestionById, - [suggestionId], - ), - )), - ) as _i10.Future<_i2.Suggestion>); - @override - _i10.Future> getAllSuggestions() => (super.noSuchMethod( - Invocation.method( - #getAllSuggestions, - [], - ), - returnValue: - _i10.Future>.value(<_i2.Suggestion>[]), - ) as _i10.Future>); - @override - _i10.Future<_i2.Suggestion> updateSuggestion(_i2.Suggestion? suggestion) => - (super.noSuchMethod( - Invocation.method( - #updateSuggestion, - [suggestion], - ), - returnValue: _i10.Future<_i2.Suggestion>.value(_FakeSuggestion_0( - this, - Invocation.method( - #updateSuggestion, - [suggestion], - ), - )), - ) as _i10.Future<_i2.Suggestion>); - @override - _i10.Future deleteSuggestionById(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #deleteSuggestionById, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future<_i3.Comment> createComment(_i3.CreateCommentModel? comment) => - (super.noSuchMethod( - Invocation.method( - #createComment, - [comment], - ), - returnValue: _i10.Future<_i3.Comment>.value(_FakeComment_1( - this, - Invocation.method( - #createComment, - [comment], - ), - )), - ) as _i10.Future<_i3.Comment>); - @override - _i10.Future> getAllComments(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #getAllComments, - [suggestionId], - ), - returnValue: _i10.Future>.value(<_i3.Comment>[]), - ) as _i10.Future>); - @override - _i10.Future deleteCommentById(String? commentId) => (super.noSuchMethod( - Invocation.method( - #deleteCommentById, - [commentId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future upvote(String? suggestionId) => (super.noSuchMethod( - Invocation.method( - #upvote, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future downvote(String? suggestionId) => (super.noSuchMethod( - Invocation.method( - #downvote, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future addNotifyToUpdateUser(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #addNotifyToUpdateUser, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); - @override - _i10.Future deleteNotifyToUpdateUser(String? suggestionId) => - (super.noSuchMethod( - Invocation.method( - #deleteNotifyToUpdateUser, - [suggestionId], - ), - returnValue: _i10.Future.value(), - returnValueForMissingStub: _i10.Future.value(), - ) as _i10.Future); -} - -/// A class which mocks [SuggestionsTheme]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockSuggestionsTheme extends _i1.Mock implements _i8.SuggestionsTheme { - MockSuggestionsTheme() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.Color get primaryBackgroundColor => (super.noSuchMethod( - Invocation.getter(#primaryBackgroundColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#primaryBackgroundColor), - ), - ) as _i6.Color); - @override - _i6.Color get secondaryBackgroundColor => (super.noSuchMethod( - Invocation.getter(#secondaryBackgroundColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#secondaryBackgroundColor), - ), - ) as _i6.Color); - @override - _i6.Color get thirdBackgroundColor => (super.noSuchMethod( - Invocation.getter(#thirdBackgroundColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#thirdBackgroundColor), - ), - ) as _i6.Color); - @override - _i6.Color get bottomSheetBackgroundColor => (super.noSuchMethod( - Invocation.getter(#bottomSheetBackgroundColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#bottomSheetBackgroundColor), - ), - ) as _i6.Color); - @override - String get fontFamily => (super.noSuchMethod( - Invocation.getter(#fontFamily), - returnValue: '', - ) as String); - @override - _i6.Color get primaryTextColor => (super.noSuchMethod( - Invocation.getter(#primaryTextColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#primaryTextColor), - ), - ) as _i6.Color); - @override - _i6.Color get secondaryTextColor => (super.noSuchMethod( - Invocation.getter(#secondaryTextColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#secondaryTextColor), - ), - ) as _i6.Color); - @override - _i6.Color get primaryIconColor => (super.noSuchMethod( - Invocation.getter(#primaryIconColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#primaryIconColor), - ), - ) as _i6.Color); - @override - _i6.Color get secondaryIconColor => (super.noSuchMethod( - Invocation.getter(#secondaryIconColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#secondaryIconColor), - ), - ) as _i6.Color); - @override - _i6.Color get actionColor => (super.noSuchMethod( - Invocation.getter(#actionColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#actionColor), - ), - ) as _i6.Color); - @override - _i6.Color get actionPressedColor => (super.noSuchMethod( - Invocation.getter(#actionPressedColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#actionPressedColor), - ), - ) as _i6.Color); - @override - _i6.Color get actionBackgroundColor => (super.noSuchMethod( - Invocation.getter(#actionBackgroundColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#actionBackgroundColor), - ), - ) as _i6.Color); - @override - _i6.Color get dividerColor => (super.noSuchMethod( - Invocation.getter(#dividerColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#dividerColor), - ), - ) as _i6.Color); - @override - _i6.Color get dialogBarrierColor => (super.noSuchMethod( - Invocation.getter(#dialogBarrierColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#dialogBarrierColor), - ), - ) as _i6.Color); - @override - _i6.Color get elevatedButtonColor => (super.noSuchMethod( - Invocation.getter(#elevatedButtonColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#elevatedButtonColor), - ), - ) as _i6.Color); - @override - _i6.Color get pressedElevatedButtonColor => (super.noSuchMethod( - Invocation.getter(#pressedElevatedButtonColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#pressedElevatedButtonColor), - ), - ) as _i6.Color); - @override - _i6.Color get elevatedButtonTextColor => (super.noSuchMethod( - Invocation.getter(#elevatedButtonTextColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#elevatedButtonTextColor), - ), - ) as _i6.Color); - @override - _i6.Color get focusedTextButtonColor => (super.noSuchMethod( - Invocation.getter(#focusedTextButtonColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#focusedTextButtonColor), - ), - ) as _i6.Color); - @override - _i6.Color get focusedTextColor => (super.noSuchMethod( - Invocation.getter(#focusedTextColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#focusedTextColor), - ), - ) as _i6.Color); - @override - _i6.Color get focusedTextFieldBorderlineColor => (super.noSuchMethod( - Invocation.getter(#focusedTextFieldBorderlineColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#focusedTextFieldBorderlineColor), - ), - ) as _i6.Color); - @override - _i6.Color get focusedTonalButtonColor => (super.noSuchMethod( - Invocation.getter(#focusedTonalButtonColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#focusedTonalButtonColor), - ), - ) as _i6.Color); - @override - _i6.Color get enabledTextColor => (super.noSuchMethod( - Invocation.getter(#enabledTextColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#enabledTextColor), - ), - ) as _i6.Color); - @override - _i6.Color get disabledTextColor => (super.noSuchMethod( - Invocation.getter(#disabledTextColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#disabledTextColor), - ), - ) as _i6.Color); - @override - _i6.Color get disabledTextButtonColor => (super.noSuchMethod( - Invocation.getter(#disabledTextButtonColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#disabledTextButtonColor), - ), - ) as _i6.Color); - @override - _i6.Color get tonalButtonColor => (super.noSuchMethod( - Invocation.getter(#tonalButtonColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#tonalButtonColor), - ), - ) as _i6.Color); - @override - _i6.Color get errorColor => (super.noSuchMethod( - Invocation.getter(#errorColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#errorColor), - ), - ) as _i6.Color); - @override - _i6.Color get fade => (super.noSuchMethod( - Invocation.getter(#fade), - returnValue: _FakeColor_4( - this, - Invocation.getter(#fade), - ), - ) as _i6.Color); - @override - _i6.Color get fabColor => (super.noSuchMethod( - Invocation.getter(#fabColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#fabColor), - ), - ) as _i6.Color); - @override - _i6.Color get upvoteArrowColor => (super.noSuchMethod( - Invocation.getter(#upvoteArrowColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#upvoteArrowColor), - ), - ) as _i6.Color); - @override - _i6.Color get activatedUpvoteArrowColor => (super.noSuchMethod( - Invocation.getter(#activatedUpvoteArrowColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#activatedUpvoteArrowColor), - ), - ) as _i6.Color); - @override - _i6.Color get barIndicatorColor => (super.noSuchMethod( - Invocation.getter(#barIndicatorColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#barIndicatorColor), - ), - ) as _i6.Color); - @override - _i6.Color get requestsTabColor => (super.noSuchMethod( - Invocation.getter(#requestsTabColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#requestsTabColor), - ), - ) as _i6.Color); - @override - _i6.Color get inProgressTabColor => (super.noSuchMethod( - Invocation.getter(#inProgressTabColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#inProgressTabColor), - ), - ) as _i6.Color); - @override - _i6.Color get completedTabColor => (super.noSuchMethod( - Invocation.getter(#completedTabColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#completedTabColor), - ), - ) as _i6.Color); - @override - _i6.Color get declinedTabColor => (super.noSuchMethod( - Invocation.getter(#declinedTabColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#declinedTabColor), - ), - ) as _i6.Color); - @override - _i6.Color get duplicatedTabColor => (super.noSuchMethod( - Invocation.getter(#duplicatedTabColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#duplicatedTabColor), - ), - ) as _i6.Color); - @override - _i6.Color get featureLabelColor => (super.noSuchMethod( - Invocation.getter(#featureLabelColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#featureLabelColor), - ), - ) as _i6.Color); - @override - _i6.Color get bugLabelColor => (super.noSuchMethod( - Invocation.getter(#bugLabelColor), - returnValue: _FakeColor_4( - this, - Invocation.getter(#bugLabelColor), - ), - ) as _i6.Color); - @override - _i7.TextStyle get base => (super.noSuchMethod( - Invocation.getter(#base), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#base), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textSmall => (super.noSuchMethod( - Invocation.getter(#textSmall), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textSmall), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textMedium => (super.noSuchMethod( - Invocation.getter(#textMedium), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textMedium), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textLarge => (super.noSuchMethod( - Invocation.getter(#textLarge), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textLarge), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textSmallPlus => (super.noSuchMethod( - Invocation.getter(#textSmallPlus), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textSmallPlus), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textSmallPlusSecondary => (super.noSuchMethod( - Invocation.getter(#textSmallPlusSecondary), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textSmallPlusSecondary), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textMediumPlus => (super.noSuchMethod( - Invocation.getter(#textMediumPlus), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textMediumPlus), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textMediumBold => (super.noSuchMethod( - Invocation.getter(#textMediumBold), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textMediumBold), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textMediumPlusBold => (super.noSuchMethod( - Invocation.getter(#textMediumPlusBold), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textMediumPlusBold), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textLargeBold => (super.noSuchMethod( - Invocation.getter(#textLargeBold), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textLargeBold), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textSmallPlusBold => (super.noSuchMethod( - Invocation.getter(#textSmallPlusBold), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textSmallPlusBold), - ), - ) as _i7.TextStyle); - @override - _i7.TextStyle get textSmallPlusSecondaryBold => (super.noSuchMethod( - Invocation.getter(#textSmallPlusSecondaryBold), - returnValue: _FakeTextStyle_5( - this, - Invocation.getter(#textSmallPlusSecondaryBold), - ), - ) as _i7.TextStyle); - @override - _i8.SuggestionsTheme copyWith({ - _i6.Color? primaryBackgroundColor, - _i6.Color? secondaryBackgroundColor, - _i6.Color? thirdBackgroundColor, - _i6.Color? bottomSheetBackgroundColor, - String? fontFamily, - _i6.Color? primaryTextColor, - _i6.Color? secondaryTextColor, - _i6.Color? primaryIconColor, - _i6.Color? secondaryIconColor, - _i6.Color? actionColor, - _i6.Color? actionPressedColor, - _i6.Color? actionBackgroundColor, - _i6.Color? dividerColor, - _i6.Color? dialogBarrierColor, - _i6.Color? elevatedButtonColor, - _i6.Color? pressedElevatedButtonColor, - _i6.Color? elevatedButtonTextColor, - _i6.Color? focusedTextButtonColor, - _i6.Color? focusedTextColor, - _i6.Color? focusedTextFieldBorderlineColor, - _i6.Color? focusedTonalButtonColor, - _i6.Color? enabledTextColor, - _i6.Color? disabledTextColor, - _i6.Color? disabledTextButtonColor, - _i6.Color? tonalButtonColor, - _i6.Color? errorColor, - _i6.Color? upvoteArrowColor, - _i6.Color? activatedUpvoteArrowColor, - _i6.Color? barIndicatorColor, - _i6.Color? activeTabColor, - _i6.Color? requestsTabColor, - _i6.Color? inProgressTabColor, - _i6.Color? completedTabColor, - _i6.Color? declinedTabColor, - _i6.Color? duplicatedTabColor, - _i6.Color? featureLabelColor, - _i6.Color? bugLabelColor, - _i6.Color? fade, - _i6.Color? fabColor, - }) => - (super.noSuchMethod( - Invocation.method( - #copyWith, - [], - { - #primaryBackgroundColor: primaryBackgroundColor, - #secondaryBackgroundColor: secondaryBackgroundColor, - #thirdBackgroundColor: thirdBackgroundColor, - #bottomSheetBackgroundColor: bottomSheetBackgroundColor, - #fontFamily: fontFamily, - #primaryTextColor: primaryTextColor, - #secondaryTextColor: secondaryTextColor, - #primaryIconColor: primaryIconColor, - #secondaryIconColor: secondaryIconColor, - #actionColor: actionColor, - #actionPressedColor: actionPressedColor, - #actionBackgroundColor: actionBackgroundColor, - #dividerColor: dividerColor, - #dialogBarrierColor: dialogBarrierColor, - #elevatedButtonColor: elevatedButtonColor, - #pressedElevatedButtonColor: pressedElevatedButtonColor, - #elevatedButtonTextColor: elevatedButtonTextColor, - #focusedTextButtonColor: focusedTextButtonColor, - #focusedTextColor: focusedTextColor, - #focusedTextFieldBorderlineColor: focusedTextFieldBorderlineColor, - #focusedTonalButtonColor: focusedTonalButtonColor, - #enabledTextColor: enabledTextColor, - #disabledTextColor: disabledTextColor, - #disabledTextButtonColor: disabledTextButtonColor, - #tonalButtonColor: tonalButtonColor, - #errorColor: errorColor, - #upvoteArrowColor: upvoteArrowColor, - #activatedUpvoteArrowColor: activatedUpvoteArrowColor, - #barIndicatorColor: barIndicatorColor, - #activeTabColor: activeTabColor, - #requestsTabColor: requestsTabColor, - #inProgressTabColor: inProgressTabColor, - #completedTabColor: completedTabColor, - #declinedTabColor: declinedTabColor, - #duplicatedTabColor: duplicatedTabColor, - #featureLabelColor: featureLabelColor, - #bugLabelColor: bugLabelColor, - #fade: fade, - #fabColor: fabColor, - }, - ), - returnValue: _FakeSuggestionsTheme_6( - this, - Invocation.method( - #copyWith, - [], - { - #primaryBackgroundColor: primaryBackgroundColor, - #secondaryBackgroundColor: secondaryBackgroundColor, - #thirdBackgroundColor: thirdBackgroundColor, - #bottomSheetBackgroundColor: bottomSheetBackgroundColor, - #fontFamily: fontFamily, - #primaryTextColor: primaryTextColor, - #secondaryTextColor: secondaryTextColor, - #primaryIconColor: primaryIconColor, - #secondaryIconColor: secondaryIconColor, - #actionColor: actionColor, - #actionPressedColor: actionPressedColor, - #actionBackgroundColor: actionBackgroundColor, - #dividerColor: dividerColor, - #dialogBarrierColor: dialogBarrierColor, - #elevatedButtonColor: elevatedButtonColor, - #pressedElevatedButtonColor: pressedElevatedButtonColor, - #elevatedButtonTextColor: elevatedButtonTextColor, - #focusedTextButtonColor: focusedTextButtonColor, - #focusedTextColor: focusedTextColor, - #focusedTextFieldBorderlineColor: focusedTextFieldBorderlineColor, - #focusedTonalButtonColor: focusedTonalButtonColor, - #enabledTextColor: enabledTextColor, - #disabledTextColor: disabledTextColor, - #disabledTextButtonColor: disabledTextButtonColor, - #tonalButtonColor: tonalButtonColor, - #errorColor: errorColor, - #upvoteArrowColor: upvoteArrowColor, - #activatedUpvoteArrowColor: activatedUpvoteArrowColor, - #barIndicatorColor: barIndicatorColor, - #activeTabColor: activeTabColor, - #requestsTabColor: requestsTabColor, - #inProgressTabColor: inProgressTabColor, - #completedTabColor: completedTabColor, - #declinedTabColor: declinedTabColor, - #duplicatedTabColor: duplicatedTabColor, - #featureLabelColor: featureLabelColor, - #bugLabelColor: bugLabelColor, - #fade: fade, - #fabColor: fabColor, - }, - ), - ), - ) as _i8.SuggestionsTheme); -}