diff --git a/sama_chat_client/lib/src/features/conversation_group_create/view/group_create_page.dart b/sama_chat_client/lib/src/features/conversation_group_create/view/group_create_page.dart index 617bb15..42c6ab1 100644 --- a/sama_chat_client/lib/src/features/conversation_group_create/view/group_create_page.dart +++ b/sama_chat_client/lib/src/features/conversation_group_create/view/group_create_page.dart @@ -50,18 +50,17 @@ class GroupCreatePage extends StatelessWidget { @override Widget build(BuildContext context) { - final LoadingOverlay loadingOverlay = LoadingOverlay(); return BlocListener( listener: (context, state) { if (state is ConversationCreatedLoading) { - loadingOverlay.show(context); + LoadingOverlay.instance.show(context); } else if (state is ConversationCreatedState) { - loadingOverlay.hide(); + LoadingOverlay.instance.hide(); ConversationModel conversation = state.conversation; context.go('$conversationListScreenPath/$conversationScreenSubPath', extra: conversation); } else if (state is ConversationCreatedStateError) { - loadingOverlay.hide(); + LoadingOverlay.instance.hide(); ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( diff --git a/sama_chat_client/lib/src/features/profile/bloc/profile_bloc.dart b/sama_chat_client/lib/src/features/profile/bloc/profile_bloc.dart index 7d94e1d..74776c0 100644 --- a/sama_chat_client/lib/src/features/profile/bloc/profile_bloc.dart +++ b/sama_chat_client/lib/src/features/profile/bloc/profile_bloc.dart @@ -4,6 +4,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:formz/formz.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:sama_sdk/api/connection/exceptions.dart'; import '../../../db/models/models.dart'; import '../../../repository/user/user_repository.dart'; @@ -182,8 +183,9 @@ class ProfileBloc extends Bloc { Emitter emit, ) async { if (state.isValid) { - emit(state.copyWith(status: FormzSubmissionStatus.inProgress)); - + emit(state.copyWith( + informationMessage: 'User updating in progress', + status: FormzSubmissionStatus.inProgress)); try { var user = await _userRepository.updateCurrentUser( currentPsw: state.userPassword.isValid @@ -214,7 +216,7 @@ class ProfileBloc extends Bloc { userPhone: UserPhone.pure(user.phone ?? ''), userEmail: UserEmail.pure(user.email ?? ''), informationMessage: 'User was successfully updated')); - } catch (e) { + } on ResponseException catch (ex) { var user = await _userRepository.getCurrentUser(); emit(state.copyWith( status: FormzSubmissionStatus.failure, @@ -224,7 +226,7 @@ class ProfileBloc extends Bloc { userPhone: UserPhone.pure(user?.phone ?? ''), userEmail: UserEmail.pure(user?.email ?? ''), userPassword: const UserPassword.pure(), - errorMessage: 'User wasn\'t updated: $e')); + errorMessage: 'User wasn\'t updated: ${ex.message}')); } } } diff --git a/sama_chat_client/lib/src/features/profile/view/profile_form.dart b/sama_chat_client/lib/src/features/profile/view/profile_form.dart index 0618e64..11db160 100644 --- a/sama_chat_client/lib/src/features/profile/view/profile_form.dart +++ b/sama_chat_client/lib/src/features/profile/view/profile_form.dart @@ -9,6 +9,7 @@ import '../../../features/profile/bloc/profile_bloc.dart'; import '../../../shared/auth/bloc/auth_bloc.dart'; import '../../../shared/connection/view/connection_checker.dart'; import '../../../shared/ui/colors.dart'; +import '../../../shared/ui/view/loading_overlay.dart'; import '../../../shared/ui/view/user_forms.dart'; import '../../../shared/utils/screen_factor.dart'; import '../models/models.dart'; @@ -20,7 +21,11 @@ class ProfileForm extends StatelessWidget { Widget build(BuildContext context) { return BlocListener( listener: (context, state) { + if (state.status.isInProgress) { + LoadingOverlay.instance.show(context); + } if (state.status.isFailure) { + LoadingOverlay.instance.hide(); ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( @@ -28,6 +33,7 @@ class ProfileForm extends StatelessWidget { ); } else if (state.status.isSuccess && state.informationMessage != null) { + LoadingOverlay.instance.hide(); ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( diff --git a/sama_chat_client/lib/src/shared/ui/view/loading_overlay.dart b/sama_chat_client/lib/src/shared/ui/view/loading_overlay.dart index 7e72f7e..a6d75d9 100644 --- a/sama_chat_client/lib/src/shared/ui/view/loading_overlay.dart +++ b/sama_chat_client/lib/src/shared/ui/view/loading_overlay.dart @@ -1,30 +1,90 @@ +import 'dart:async'; +import 'dart:ui'; + import 'package:flutter/material.dart'; import '../colors.dart'; class LoadingOverlay { - OverlayEntry? _overlay; + LoadingOverlay._(); - LoadingOverlay(); + static final LoadingOverlay instance = LoadingOverlay._(); - void show(BuildContext context) { - if (_overlay == null) { - _overlay = OverlayEntry( - builder: (context) => const ColoredBox( - color: semiBlack, - child: Center( - child: CircularProgressIndicator(), - ), - ), - ); - Overlay.of(context).insert(_overlay!); - } + OverlayEntry? _overlayEntry; + Timer? _autoHideTimer; + + bool get isShowing => _overlayEntry != null; + + void show(BuildContext context, + {String? message, Duration duration = const Duration(seconds: 10)}) { + if (_overlayEntry != null) return; + + _overlayEntry = OverlayEntry( + builder: (_) => _LoaderWidget(message: message), + ); + + Overlay.of( + context, + rootOverlay: true, + ).insert(_overlayEntry!); + + _autoHideTimer?.cancel(); + + _autoHideTimer = Timer(duration, () { + hide(); + }); } void hide() { - if (_overlay != null) { - _overlay!.remove(); - _overlay = null; - } + _autoHideTimer?.cancel(); + _autoHideTimer = null; + + _overlayEntry?.remove(); + _overlayEntry = null; + } +} + +class _LoaderWidget extends StatelessWidget { + final String? message; + + const _LoaderWidget({ + this.message, + }); + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.transparent, + child: Stack( + children: [ + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 2, + sigmaY: 2, + ), + child: Container( + color: black.withValues(alpha: 0.25), + ), + ), + ), + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const CircularProgressIndicator(), + if (message != null) ...[ + const SizedBox(height: 16), + Text( + message!, + textAlign: TextAlign.center, + ), + ], + ], + ), + ), + ], + ), + ); } }