diff --git a/lib/di/global/get_it_initializer.dart b/lib/di/global/get_it_initializer.dart index 54bd0d1994..763604cd6b 100644 --- a/lib/di/global/get_it_initializer.dart +++ b/lib/di/global/get_it_initializer.dart @@ -79,6 +79,7 @@ import 'package:fluffychat/domain/usecase/search/search_recent_chat_interactor.d import 'package:fluffychat/domain/usecase/search/server_search_interactor.dart'; import 'package:fluffychat/domain/usecase/settings/save_language_interactor.dart'; import 'package:fluffychat/domain/usecase/settings/update_profile_interactor.dart'; +import 'package:fluffychat/domain/usecase/verify_name_interactor.dart'; import 'package:fluffychat/event/twake_event_dispatcher.dart'; import 'package:fluffychat/pages/chat/chat_pinned_events/pinned_events_controller.dart'; import 'package:fluffychat/utils/famedlysdk_store.dart'; @@ -344,6 +345,10 @@ class GetItInitializer { getIt.registerSingleton( DownloadMediaFileInteractor(), ); + + getIt.registerFactory( + () => VerifyNameInteractor(), + ); } void _bindingControllers() { diff --git a/lib/pages/chat_details/chat_details_edit.dart b/lib/pages/chat_details/chat_details_edit.dart index 22319549d5..f66084d2c6 100644 --- a/lib/pages/chat_details/chat_details_edit.dart +++ b/lib/pages/chat_details/chat_details_edit.dart @@ -5,9 +5,13 @@ import 'package:fluffychat/di/global/get_it_initializer.dart'; import 'package:fluffychat/domain/app_state/room/update_group_chat_failure.dart'; import 'package:fluffychat/domain/app_state/room/update_group_chat_success.dart'; import 'package:fluffychat/domain/app_state/room/upload_content_state.dart'; +import 'package:fluffychat/domain/app_state/validator/verify_name_view_state.dart'; +import 'package:fluffychat/domain/model/extensions/validator_failure_extension.dart'; +import 'package:fluffychat/domain/model/verification/name_with_space_only_validator.dart'; import 'package:fluffychat/domain/usecase/room/update_group_chat_interactor.dart'; import 'package:fluffychat/domain/usecase/room/upload_content_for_web_interactor.dart'; import 'package:fluffychat/domain/usecase/room/upload_content_interactor.dart'; +import 'package:fluffychat/domain/usecase/verify_name_interactor.dart'; import 'package:fluffychat/pages/chat_details/chat_details_edit_context_menu_actions.dart'; import 'package:fluffychat/pages/chat_details/chat_details_edit_view.dart'; import 'package:fluffychat/pages/chat_details/chat_details_edit_view_style.dart'; @@ -54,6 +58,7 @@ class ChatDetailsEditController extends State final uploadContentInteractor = getIt.get(); final uploadContentWebInteractor = getIt.get(); + final verifyNameInteractor = getIt.get(); Room? room; @@ -73,6 +78,7 @@ class ChatDetailsEditController extends State final MenuController menuController = MenuController(); final isEditedGroupInfoNotifier = ValueNotifier(false); + final isValidGroupNameNotifier = ValueNotifier(false); Client get client => Matrix.of(context).client; @@ -443,6 +449,20 @@ class ChatDetailsEditController extends State avatarAssetEntity = null; } + String? getErrorMessage(String content) { + return verifyNameInteractor + .execute(content, [NameWithSpaceOnlyValidator()]).fold( + (failure) { + if (failure is VerifyNameFailure) { + return failure.getMessage(context); + } else { + return null; + } + }, + (success) => null, + ); + } + @override void dispose() { _clearImageInMemory(); @@ -463,11 +483,12 @@ class ChatDetailsEditController extends State if (_isEditAvatar) { return; } - isEditedGroupInfoNotifier.value = groupNameTextEditingController.text != room?.name; groupNameEmptyNotifier.value = groupNameTextEditingController.text.isEmpty; + isValidGroupNameNotifier.value = + getErrorMessage(groupNameTextEditingController.text) == null; }); } diff --git a/lib/pages/chat_details/chat_details_edit_view.dart b/lib/pages/chat_details/chat_details_edit_view.dart index 1b5b04a276..63fe346dff 100644 --- a/lib/pages/chat_details/chat_details_edit_view.dart +++ b/lib/pages/chat_details/chat_details_edit_view.dart @@ -67,23 +67,28 @@ class ChatDetailsEditView extends StatelessWidget { ), const Spacer(), ValueListenableBuilder( - valueListenable: controller.isEditedGroupInfoNotifier, - builder: (context, value, child) { - if (!value) { - return const SizedBox.shrink(); - } - return child!; + valueListenable: controller.isValidGroupNameNotifier, + builder: (context, isValid, child) { + return ValueListenableBuilder( + valueListenable: controller.isEditedGroupInfoNotifier, + builder: (context, value, child) { + if (!value || !isValid) { + return const SizedBox.shrink(); + } + return child!; + }, + child: Padding( + padding: ChatDetailEditViewStyle.doneIconPadding, + child: IconButton( + highlightColor: Colors.transparent, + hoverColor: Colors.transparent, + splashColor: Colors.transparent, + onPressed: () => controller.handleSaveAction(context), + icon: const Icon(Icons.done), + ), + ), + ); }, - child: Padding( - padding: ChatDetailEditViewStyle.doneIconPadding, - child: IconButton( - highlightColor: Colors.transparent, - hoverColor: Colors.transparent, - splashColor: Colors.transparent, - onPressed: () => controller.handleSaveAction(context), - icon: const Icon(Icons.done), - ), - ), ), ], ), @@ -323,40 +328,48 @@ class _GroupNameField extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: ChatDetailEditViewStyle.textFieldPadding, - child: TextField( - style: ChatDetailEditViewStyle.textFieldStyle(context), - controller: controller.groupNameTextEditingController, - contextMenuBuilder: mobileTwakeContextMenuBuilder, - focusNode: controller.groupNameFocusNode, - decoration: InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide(color: Theme.of(context).colorScheme.shadow), - ), - labelText: L10n.of(context)!.widgetName, - labelStyle: ChatDetailEditViewStyle.textFieldLabelStyle(context), - hintText: L10n.of(context)!.enterGroupName, - hintStyle: ChatDetailEditViewStyle.textFieldHintStyle(context), - contentPadding: ChatDetailEditViewStyle.contentPadding, - suffixIcon: ValueListenableBuilder( - valueListenable: controller.groupNameEmptyNotifier, - builder: (context, isGroupNameEmpty, child) { - if (isGroupNameEmpty) { - return child!; - } - - return IconButton( - onPressed: () => - controller.groupNameTextEditingController.clear(), - icon: Icon( - Icons.cancel_outlined, - size: ChatDetailEditViewStyle.clearIconSize, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ); - }, - child: const SizedBox.shrink(), - ), - ), + child: ValueListenableBuilder( + valueListenable: controller.isValidGroupNameNotifier, + builder: (context, value, _) { + return TextField( + style: ChatDetailEditViewStyle.textFieldStyle(context), + controller: controller.groupNameTextEditingController, + contextMenuBuilder: mobileTwakeContextMenuBuilder, + focusNode: controller.groupNameFocusNode, + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: + BorderSide(color: Theme.of(context).colorScheme.shadow), + ), + labelText: L10n.of(context)!.widgetName, + labelStyle: ChatDetailEditViewStyle.textFieldLabelStyle(context), + hintText: L10n.of(context)!.enterGroupName, + hintStyle: ChatDetailEditViewStyle.textFieldHintStyle(context), + contentPadding: ChatDetailEditViewStyle.contentPadding, + errorText: controller.getErrorMessage( + controller.groupNameTextEditingController.text, + ), + suffixIcon: ValueListenableBuilder( + valueListenable: controller.groupNameEmptyNotifier, + builder: (context, isGroupNameEmpty, child) { + if (isGroupNameEmpty) { + return child!; + } + return IconButton( + onPressed: () => + controller.groupNameTextEditingController.clear(), + icon: Icon( + Icons.cancel_outlined, + size: ChatDetailEditViewStyle.clearIconSize, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ); + }, + child: const SizedBox.shrink(), + ), + ), + ); + }, ), ); } diff --git a/lib/pages/new_group/new_group_chat_info.dart b/lib/pages/new_group/new_group_chat_info.dart index 8e0f201634..ab5a8449eb 100644 --- a/lib/pages/new_group/new_group_chat_info.dart +++ b/lib/pages/new_group/new_group_chat_info.dart @@ -59,7 +59,7 @@ class NewGroupChatInfoController extends State final groupNameTextEditingController = TextEditingController(); final avatarAssetEntityNotifier = ValueNotifier(null); final avatarFilePickerNotifier = ValueNotifier(null); - VerifyNameInteractor verifyNameInteractor = VerifyNameInteractor(); + VerifyNameInteractor verifyNameInteractor = getIt.get(); final groupNameFocusNode = FocusNode(); StreamSubscription? createNewGroupChatInteractorStreamSubscription;