From d21850edc853740206fdc18b72fd3ce016838cec Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Jul 2025 12:37:27 +0300 Subject: [PATCH] Enhance `DuplicateSpaceDialog` to use `Bloc` for state management and streamline the duplication process with success and error handling. Update `CommunityStructureHeaderActionButtonsComposer` to integrate the new dialog for duplicating spaces. --- ...ucture_header_action_buttons_composer.dart | 15 ++- .../views/duplicate_space_dialog.dart | 107 +++++++++--------- .../widgets/duplicate_space_dialog_form.dart | 84 ++++++++++++++ 3 files changed, 153 insertions(+), 53 deletions(-) create mode 100644 lib/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_dialog_form.dart diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart index d7403588..d3e2d18a 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart'; @@ -7,6 +9,7 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/views/duplicate_space_dialog.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart'; class CommunityStructureHeaderActionButtonsComposer extends StatelessWidget { @@ -44,7 +47,17 @@ class CommunityStructureHeaderActionButtonsComposer extends StatelessWidget { }, ), ), - onDuplicate: (space) {}, + onDuplicate: (space) => showDialog( + context: context, + builder: (_) => DuplicateSpaceDialog( + initialName: space.spaceName, + selectedSpaceUuid: space.uuid, + selectedCommunityUuid: selectedCommunity.uuid, + onSuccess: (space) { + log('space: $space'); + }, + ), + ), onEdit: (space) => SpaceDetailsDialogHelper.showEdit( context, spaceModel: selectedSpace!, diff --git a/lib/pages/space_management_v2/modules/duplicate_space/presentation/views/duplicate_space_dialog.dart b/lib/pages/space_management_v2/modules/duplicate_space/presentation/views/duplicate_space_dialog.dart index f7c491d7..ec98dd51 100644 --- a/lib/pages/space_management_v2/modules/duplicate_space/presentation/views/duplicate_space_dialog.dart +++ b/lib/pages/space_management_v2/modules/duplicate_space/presentation/views/duplicate_space_dialog.dart @@ -1,68 +1,71 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_text_field.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/common/widgets/app_loading_indicator.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/data/services/remote_duplicate_space_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/bloc/duplicate_space_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_dialog_form.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/extension/app_snack_bar.dart'; -class DuplicateSpaceDialog extends StatefulWidget { +class DuplicateSpaceDialog extends StatelessWidget { const DuplicateSpaceDialog({ required this.initialName, - required this.onSubmit, + required this.onSuccess, + required this.selectedSpaceUuid, + required this.selectedCommunityUuid, super.key, }); final String initialName; - final void Function(String) onSubmit; - - @override - State createState() => _DuplicateSpaceDialogState(); -} - -class _DuplicateSpaceDialogState extends State { - late final TextEditingController _nameController; - bool _isNameValid = true; - - @override - void initState() { - super.initState(); - _nameController = TextEditingController(text: '${widget.initialName}(1)'); - _nameController.addListener(_validateName); - } - - void _validateName() => setState( - () => _isNameValid = _nameController.text.trim() != widget.initialName, - ); - - @override - void dispose() { - _nameController.dispose(); - super.dispose(); - } + final void Function(SpaceModel space) onSuccess; + final String selectedSpaceUuid; + final String selectedCommunityUuid; @override Widget build(BuildContext context) { - return AlertDialog( - title: const SelectableText('Duplicate Space'), - content: Column( - mainAxisSize: MainAxisSize.min, - spacing: 16, - children: [ - const SelectableText('Enter a new name for the duplicated space:'), - DuplicateSpaceTextField( - nameController: _nameController, - isNameValid: _isNameValid, - initialName: widget.initialName, - ), - ], + return BlocProvider( + create: (context) => DuplicateSpaceBloc( + RemoteDuplicateSpaceService(HTTPService()), ), - actions: [ - TextButton( - onPressed: Navigator.of(context).pop, - child: const Text('Cancel'), + child: BlocListener( + listener: _listener, + child: DuplicateSpaceDialogForm( + initialName: initialName, + selectedSpaceUuid: selectedSpaceUuid, + selectedCommunityUuid: selectedCommunityUuid, ), - TextButton( - onPressed: - _isNameValid ? () => widget.onSubmit(_nameController.text) : null, - child: const Text('Duplicate'), - ), - ], + ), ); } + + void _listener(BuildContext context, DuplicateSpaceState state) { + switch (state) { + case DuplicateSpaceLoading(): + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const AppLoadingIndicator(), + ); + break; + + case DuplicateSpaceFailure(:final errorMessage): + Navigator.pop(context); + Navigator.pop(context); + context.showFailureSnackbar(errorMessage); + break; + + case DuplicateSpaceSuccess(:final space): + onSuccess.call(space); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + context.showSuccessSnackbar( + '${space.spaceName} duplicated successfully', + ); + break; + + default: + break; + } + } } diff --git a/lib/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_dialog_form.dart b/lib/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_dialog_form.dart new file mode 100644 index 00000000..7f4ec966 --- /dev/null +++ b/lib/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_dialog_form.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/domain/params/duplicate_space_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/bloc/duplicate_space_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_text_field.dart'; + +class DuplicateSpaceDialogForm extends StatefulWidget { + const DuplicateSpaceDialogForm({ + required this.initialName, + required this.selectedSpaceUuid, + required this.selectedCommunityUuid, + super.key, + }); + + final String initialName; + final String selectedSpaceUuid; + final String selectedCommunityUuid; + + @override + State createState() => _DuplicateSpaceDialogFormState(); +} + +class _DuplicateSpaceDialogFormState extends State { + late final TextEditingController _nameController; + bool _isNameValid = true; + + @override + void initState() { + super.initState(); + _nameController = TextEditingController(text: '${widget.initialName}(1)'); + _nameController.addListener(_validateName); + } + + void _validateName() => setState( + () => _isNameValid = _nameController.text.trim() != widget.initialName, + ); + + @override + void dispose() { + _nameController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const SelectableText('Duplicate Space'), + content: Column( + mainAxisSize: MainAxisSize.min, + spacing: 16, + children: [ + const SelectableText('Enter a new name for the duplicated space:'), + DuplicateSpaceTextField( + nameController: _nameController, + isNameValid: _isNameValid, + initialName: widget.initialName, + ), + ], + ), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: const Text('Cancel'), + ), + TextButton( + onPressed: _isNameValid ? () => _submit(context) : null, + child: const Text('Duplicate'), + ), + ], + ); + } + + void _submit(BuildContext context) { + context.read().add( + DuplicateSpaceEvent( + param: DuplicateSpaceParam( + newSpaceName: _nameController.text, + spaceUuid: widget.selectedSpaceUuid, + communityUuid: widget.selectedCommunityUuid, + ), + ), + ); + } +}