Made CommunityDialog reusable for edit and create features.

This commit is contained in:
Faris Armoush
2025-07-06 10:46:20 +03:00
parent 15b36fd052
commit 6dcc851d97
11 changed files with 161 additions and 111 deletions

View File

@ -1,24 +1,21 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
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/create_community/presentation/create_community_dialog.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/presentation/edit_community_dialog.dart';
abstract final class SpaceManagementCommunityDialogHelper { abstract final class SpaceManagementCommunityDialogHelper {
static void showCreateDialog(BuildContext context) { static void showCreateDialog(BuildContext context) => showDialog<void>(
context: context,
builder: (_) => const CreateCommunityDialog(),
);
static void showEditDialog(
BuildContext context,
CommunityModel community,
) {
showDialog<void>( showDialog<void>(
context: context, context: context,
builder: (_) => CreateCommunityDialog( builder: (_) => EditCommunityDialog(community: community),
title: const SelectableText('Community Name'),
onCreateCommunity: (community) {
context.read<CommunitiesBloc>().add(
InsertCommunity(community),
);
context.read<CommunitiesTreeSelectionBloc>().add(
SelectCommunityEvent(community: community),
);
},
),
); );
} }
} }

View File

@ -1,28 +1,28 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_name_text_field.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_name_text_field.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class CreateCommunityDialogWidget extends StatefulWidget { class CommunityDialog extends StatefulWidget {
final String? initialName; final String? initialName;
final Widget title; final Widget title;
final void Function(String name) onSubmit;
final String? errorMessage;
const CreateCommunityDialogWidget({ const CommunityDialog({
super.key,
required this.title, required this.title,
required this.onSubmit,
this.initialName, this.initialName,
this.errorMessage,
super.key,
}); });
@override @override
State<CreateCommunityDialogWidget> createState() => State<CommunityDialog> createState() => _CommunityDialogState();
_CreateCommunityDialogWidgetState();
} }
class _CreateCommunityDialogWidgetState extends State<CreateCommunityDialogWidget> { class _CommunityDialogState extends State<CommunityDialog> {
late final TextEditingController _nameController; late final TextEditingController _nameController;
@override @override
@ -63,35 +63,31 @@ class _CreateCommunityDialogWidgetState extends State<CreateCommunityDialogWidge
child: Form( child: Form(
key: _formKey, key: _formKey,
child: SingleChildScrollView( child: SingleChildScrollView(
child: BlocBuilder<CreateCommunityBloc, CreateCommunityState>( child: Column(
builder: (context, state) { mainAxisSize: MainAxisSize.min,
return Column( crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, children: [
crossAxisAlignment: CrossAxisAlignment.start, DefaultTextStyle(
children: [ style: Theme.of(context).textTheme.headlineMedium!,
DefaultTextStyle( child: widget.title,
style: Theme.of(context).textTheme.headlineMedium!, ),
child: widget.title, const SizedBox(height: 18),
CreateCommunityNameTextField(
nameController: _nameController,
),
if (widget.errorMessage != null)
Padding(
padding: const EdgeInsets.only(top: 18),
child: SelectableText(
'* ${widget.errorMessage}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.error,
),
), ),
const SizedBox(height: 18), ),
CreateCommunityNameTextField( const SizedBox(height: 24),
nameController: _nameController, _buildActionButtons(context),
), ],
if (state case CreateCommunityFailure(:final message))
Padding(
padding: const EdgeInsets.only(top: 18),
child: SelectableText(
'* $message',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.error,
),
),
),
const SizedBox(height: 24),
_buildActionButtons(context),
],
);
},
), ),
), ),
), ),
@ -132,13 +128,7 @@ class _CreateCommunityDialogWidgetState extends State<CreateCommunityDialogWidge
void _onSubmit(BuildContext context) { void _onSubmit(BuildContext context) {
if (_formKey.currentState?.validate() ?? false) { if (_formKey.currentState?.validate() ?? false) {
context.read<CreateCommunityBloc>().add( widget.onSubmit.call(_nameController.text.trim());
CreateCommunity(
CreateCommunityParam(
name: _nameController.text.trim(),
),
),
);
} }
} }
} }

View File

@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.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/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@ -44,18 +44,6 @@ class CommunityStructureHeader extends StatelessWidget {
); );
} }
void _showCreateCommunityDialog(BuildContext context) {
showDialog<void>(
context: context,
builder: (context) => CreateCommunityDialog(
title: const Text('Edit Community'),
onCreateCommunity: (community) {
// TODO(FarisArmoush): Implement
},
),
);
}
Widget _buildCommunityInfo( Widget _buildCommunityInfo(
BuildContext context, ThemeData theme, double screenWidth) { BuildContext context, ThemeData theme, double screenWidth) {
final selectedCommunity = final selectedCommunity =
@ -86,7 +74,12 @@ class CommunityStructureHeader extends StatelessWidget {
), ),
const SizedBox(width: 2), const SizedBox(width: 2),
GestureDetector( GestureDetector(
onTap: () => _showCreateCommunityDialog(context), onTap: () {
SpaceManagementCommunityDialogHelper.showEditDialog(
context,
selectedCommunity,
);
},
child: SvgPicture.asset( child: SvgPicture.asset(
Assets.iconEdit, Assets.iconEdit,
width: 16, width: 16,

View File

@ -39,6 +39,26 @@ class CommunityModel extends Equatable {
.toList(); .toList();
} }
CommunityModel copyWith({
String? uuid,
String? name,
DateTime? createdAt,
DateTime? updatedAt,
String? description,
String? externalId,
List<SpaceModel>? spaces,
}) {
return CommunityModel(
uuid: uuid ?? this.uuid,
name: name ?? this.name,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
description: description ?? this.description,
externalId: externalId ?? this.externalId,
spaces: spaces ?? this.spaces,
);
}
@override @override
List<Object?> get props => [uuid, name, spaces]; List<Object?> get props => [uuid, name, spaces];
} }

View File

@ -1,28 +1,23 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart';
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/create_community/data/services/remote_create_community_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/data/services/remote_create_community_service.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog_widget.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
class CreateCommunityDialog extends StatelessWidget { class CreateCommunityDialog extends StatelessWidget {
final void Function(CommunityModel community) onCreateCommunity; const CreateCommunityDialog({super.key});
final String? initialName;
final Widget title;
const CreateCommunityDialog({
super.key,
required this.onCreateCommunity,
required this.title,
this.initialName,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => CreateCommunityBloc(RemoteCreateCommunityService(HTTPService())), create: (_) => CreateCommunityBloc(
child: BlocListener<CreateCommunityBloc, CreateCommunityState>( RemoteCreateCommunityService(HTTPService()),
),
child: BlocConsumer<CreateCommunityBloc, CreateCommunityState>(
listener: (context, state) { listener: (context, state) {
switch (state) { switch (state) {
case CreateCommunityLoading(): case CreateCommunityLoading():
@ -39,7 +34,12 @@ class CreateCommunityDialog extends StatelessWidget {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Community created successfully')), const SnackBar(content: Text('Community created successfully')),
); );
onCreateCommunity.call(community); context.read<CommunitiesBloc>().add(
InsertCommunity(community),
);
context.read<CommunitiesTreeSelectionBloc>().add(
SelectCommunityEvent(community: community),
);
break; break;
case CreateCommunityFailure(): case CreateCommunityFailure():
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -48,10 +48,16 @@ class CreateCommunityDialog extends StatelessWidget {
break; break;
} }
}, },
child: CreateCommunityDialogWidget( builder: (BuildContext context, CreateCommunityState state) {
title: title, return CommunityDialog(
initialName: initialName, title: const Text('Create Community'),
), initialName: null,
onSubmit: (name) => context.read<CreateCommunityBloc>().add(
CreateCommunity(CreateCommunityParam(name: name)),
),
errorMessage: state is CreateCommunityFailure ? state.message : null,
);
},
), ),
); );
} }

View File

@ -1,6 +1,5 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart';
import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/api_exception.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
@ -13,7 +12,7 @@ class RemoteUpdateCommunityService implements UpdateCommunityService {
static const _defaultErrorMessage = 'Failed to update community'; static const _defaultErrorMessage = 'Failed to update community';
@override @override
Future<CommunityModel> updateCommunity(UpdateCommunityParam param) async { Future<CommunityModel> updateCommunity(CommunityModel param) async {
try { try {
final response = await _httpService.put( final response = await _httpService.put(
path: 'endpoint', path: 'endpoint',

View File

@ -1,10 +0,0 @@
import 'package:equatable/equatable.dart';
class UpdateCommunityParam extends Equatable {
const UpdateCommunityParam({required this.name});
final String name;
@override
List<Object> get props => [name];
}

View File

@ -1,6 +1,5 @@
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart';
abstract class UpdateCommunityService { abstract class UpdateCommunityService {
Future<CommunityModel> updateCommunity(UpdateCommunityParam param); Future<CommunityModel> updateCommunity(CommunityModel community);
} }

View File

@ -1,7 +1,6 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart';
import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/api_exception.dart';
@ -24,7 +23,7 @@ class UpdateCommunityBloc extends Bloc<UpdateCommunityEvent, UpdateCommunityStat
emit(UpdateCommunityLoading()); emit(UpdateCommunityLoading());
try { try {
final updatedCommunity = await _updateCommunityService.updateCommunity( final updatedCommunity = await _updateCommunityService.updateCommunity(
event.param, event.communityModel,
); );
emit(UpdateCommunitySuccess(updatedCommunity)); emit(UpdateCommunitySuccess(updatedCommunity));
} on APIException catch (e) { } on APIException catch (e) {

View File

@ -8,10 +8,10 @@ sealed class UpdateCommunityEvent extends Equatable {
} }
final class UpdateCommunity extends UpdateCommunityEvent { final class UpdateCommunity extends UpdateCommunityEvent {
const UpdateCommunity(this.param); const UpdateCommunity(this.communityModel);
final UpdateCommunityParam param; final CommunityModel communityModel ;
@override @override
List<Object> get props => [param]; List<Object> get props => [communityModel];
} }

View File

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart';
import 'package:syncrow_web/services/api/http_service.dart';
class EditCommunityDialog extends StatelessWidget {
const EditCommunityDialog({required this.community, super.key});
final CommunityModel community;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) =>
UpdateCommunityBloc(RemoteUpdateCommunityService(HTTPService())),
child: BlocConsumer<UpdateCommunityBloc, UpdateCommunityState>(
listener: (context, state) {
switch (state) {
case UpdateCommunityLoading():
showDialog<void>(
context: context,
builder: (context) => const Center(
child: CircularProgressIndicator(),
),
);
break;
case UpdateCommunitySuccess(:final community):
Navigator.of(context).pop();
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${community.name} updated successfully'),
),
);
break;
case UpdateCommunityFailure():
Navigator.of(context).pop();
break;
default:
break;
}
},
builder: (context, state) => CommunityDialog(
title: const Text('Edit Community'),
initialName: community.name,
errorMessage: state is UpdateCommunityFailure ? state.message : null,
onSubmit: (name) => context.read<UpdateCommunityBloc>().add(
UpdateCommunity(community.copyWith(name: name)),
),
),
),
);
}
}