TF-4068 Change profile (set identity as default) in setting

This commit is contained in:
dab246
2025-09-29 18:10:44 +07:00
committed by Dat H. Pham
parent e04b88f87c
commit 038aba0af3
14 changed files with 313 additions and 123 deletions
@@ -35,6 +35,7 @@ class LoginMessageWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appLocalizations = AppLocalizations.of(context);
return Padding( return Padding(
padding: const EdgeInsetsDirectional.only( padding: const EdgeInsetsDirectional.only(
top: 11, top: 11,
@@ -50,34 +51,34 @@ class LoginMessageWidget extends StatelessWidget {
viewState.fold( viewState.fold(
(failure) { (failure) {
if (failure is FeatureFailure && failure.exception is NoNetworkError) { if (failure is FeatureFailure && failure.exception is NoNetworkError) {
return AppLocalizations.of(context).youAreOffline; return appLocalizations.youAreOffline;
} }
if (failure is GetOIDCConfigurationFailure) { if (failure is GetOIDCConfigurationFailure) {
return AppLocalizations.of(context).canNotVerifySSOConfiguration; return appLocalizations.canNotVerifySSOConfiguration;
} else if (failure is DNSLookupToGetJmapUrlFailure) { } else if (failure is DNSLookupToGetJmapUrlFailure) {
return AppLocalizations.of(context).dnsLookupLoginMessage; return appLocalizations.dnsLookupLoginMessage;
} else if (failure is GetTokenOIDCFailure && failure.exception is NoSuitableBrowserForOIDCException) { } else if (failure is GetTokenOIDCFailure && failure.exception is NoSuitableBrowserForOIDCException) {
return AppLocalizations.of(context).noSuitableBrowserForOIDC; return appLocalizations.noSuitableBrowserForOIDC;
} else if (failure is FeatureFailure) { } else if (failure is FeatureFailure) {
return _toastManager?.getMessageByException( return _toastManager?.getMessageByException(
context, appLocalizations,
failure.exception, failure.exception,
useDefaultMessage: true, useDefaultMessage: true,
) ?? AppLocalizations.of(context).unknownError; ) ?? appLocalizations.unknownError;
} else { } else {
return AppLocalizations.of(context).unknownError; return appLocalizations.unknownError;
} }
}, },
(success) { (success) {
if (formType == LoginFormType.credentialForm) { if (formType == LoginFormType.credentialForm) {
return AppLocalizations.of(context).loginInputCredentialMessage; return appLocalizations.loginInputCredentialMessage;
} else if (formType == LoginFormType.dnsLookupForm) { } else if (formType == LoginFormType.dnsLookupForm) {
return AppLocalizations.of(context).dnsLookupLoginMessage; return appLocalizations.dnsLookupLoginMessage;
} else if (formType == LoginFormType.passwordForm) { } else if (formType == LoginFormType.passwordForm) {
return AppLocalizations.of(context).enterYourPasswordToSignIn; return appLocalizations.enterYourPasswordToSignIn;
} else if (formType == LoginFormType.baseUrlForm) { } else if (formType == LoginFormType.baseUrlForm) {
return AppLocalizations.of(context).loginInputUrlMessage; return appLocalizations.loginInputUrlMessage;
} else { } else {
return ''; return '';
} }
@@ -10,6 +10,7 @@ class EditDefaultIdentityRequest extends EditIdentityRequest {
required super.identityId, required super.identityId,
required super.identityRequest, required super.identityRequest,
required super.isDefaultIdentity, required super.isDefaultIdentity,
required super.isSetAsDefault,
this.oldDefaultIdentityIds, this.oldDefaultIdentityIds,
super.publicAssetsInIdentityArguments super.publicAssetsInIdentityArguments
}); });
@@ -10,19 +10,33 @@ class EditIdentityRequest with EquatableMixin {
final IdentityId identityId; final IdentityId identityId;
final bool isDefaultIdentity; final bool isDefaultIdentity;
final PublicAssetsInIdentityArguments? publicAssetsInIdentityArguments; final PublicAssetsInIdentityArguments? publicAssetsInIdentityArguments;
final bool isSetAsDefault;
EditIdentityRequest({ EditIdentityRequest({
required this.identityId, required this.identityId,
required this.identityRequest, required this.identityRequest,
this.isDefaultIdentity = false, this.isDefaultIdentity = false,
this.isSetAsDefault = false,
this.publicAssetsInIdentityArguments this.publicAssetsInIdentityArguments
}); });
factory EditIdentityRequest.fromIdentityWithoutPublicAssets(
Identity identity,
) {
return EditIdentityRequest(
identityId: identity.id!,
identityRequest: identity.toIdentityRequest(),
isDefaultIdentity: true,
isSetAsDefault: true,
);
}
@override @override
List<Object?> get props => [ List<Object?> get props => [
identityId, identityId,
identityRequest, identityRequest,
isDefaultIdentity, isDefaultIdentity,
publicAssetsInIdentityArguments publicAssetsInIdentityArguments,
isSetAsDefault,
]; ];
} }
@@ -2,12 +2,19 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/edit_identity_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/edit_identity_state.dart';
class EditDefaultIdentityLoading extends UIState {} class EditDefaultIdentityLoading extends LoadingState {
final bool isSetAsDefault;
EditDefaultIdentityLoading({this.isSetAsDefault = false});
@override
List<Object?> get props => [isSetAsDefault];
}
class EditDefaultIdentitySuccess extends EditIdentitySuccess { class EditDefaultIdentitySuccess extends EditIdentitySuccess {
EditDefaultIdentitySuccess( EditDefaultIdentitySuccess(
super.identityId, super.identityId,
{super.publicAssetsInIdentityArguments}); {super.publicAssetsInIdentityArguments, super.isSetAsDefault = false});
} }
class EditDefaultIdentityFailure extends FeatureFailure { class EditDefaultIdentityFailure extends FeatureFailure {
@@ -8,13 +8,20 @@ class EditIdentityLoading extends UIState {}
class EditIdentitySuccess extends UIState { class EditIdentitySuccess extends UIState {
final IdentityId identityId; final IdentityId identityId;
final PublicAssetsInIdentityArguments? publicAssetsInIdentityArguments; final PublicAssetsInIdentityArguments? publicAssetsInIdentityArguments;
final bool isSetAsDefault;
EditIdentitySuccess( EditIdentitySuccess(
this.identityId, this.identityId, {
{this.publicAssetsInIdentityArguments}); this.publicAssetsInIdentityArguments,
this.isSetAsDefault = false,
});
@override @override
List<Object?> get props => [identityId, publicAssetsInIdentityArguments]; List<Object?> get props => [
identityId,
publicAssetsInIdentityArguments,
isSetAsDefault,
];
} }
class EditIdentityFailure extends FeatureFailure { class EditIdentityFailure extends FeatureFailure {
@@ -27,7 +27,11 @@ class EditDefaultIdentityInteractor {
EditIdentityRequest editIdentityRequest EditIdentityRequest editIdentityRequest
) async* { ) async* {
try { try {
yield Right(EditDefaultIdentityLoading()); yield Right(
EditDefaultIdentityLoading(
isSetAsDefault: editIdentityRequest.isSetAsDefault,
),
);
final defaultIdentities = await _getDefaultIdentities(session, accountId); final defaultIdentities = await _getDefaultIdentities(session, accountId);
_removeEditIdentityFromDefaultIdentities(defaultIdentities, editIdentityRequest.identityId); _removeEditIdentityFromDefaultIdentities(defaultIdentities, editIdentityRequest.identityId);
@@ -36,6 +40,7 @@ class EditDefaultIdentityInteractor {
identityId: editIdentityRequest.identityId, identityId: editIdentityRequest.identityId,
identityRequest: editIdentityRequest.identityRequest, identityRequest: editIdentityRequest.identityRequest,
isDefaultIdentity: editIdentityRequest.isDefaultIdentity, isDefaultIdentity: editIdentityRequest.isDefaultIdentity,
isSetAsDefault: editIdentityRequest.isSetAsDefault,
publicAssetsInIdentityArguments: editIdentityRequest.publicAssetsInIdentityArguments, publicAssetsInIdentityArguments: editIdentityRequest.publicAssetsInIdentityArguments,
oldDefaultIdentityIds: defaultIdentities oldDefaultIdentityIds: defaultIdentities
?.map((identity) => identity.id!) ?.map((identity) => identity.id!)
@@ -45,7 +50,9 @@ class EditDefaultIdentityInteractor {
yield result yield result
? Right(EditDefaultIdentitySuccess( ? Right(EditDefaultIdentitySuccess(
editDefaultRequest.identityId, editDefaultRequest.identityId,
publicAssetsInIdentityArguments: editDefaultRequest.publicAssetsInIdentityArguments)) publicAssetsInIdentityArguments: editDefaultRequest.publicAssetsInIdentityArguments,
isSetAsDefault: editDefaultRequest.isSetAsDefault,
))
: Left(EditDefaultIdentityFailure(null)); : Left(EditDefaultIdentityFailure(null));
} catch (exception) { } catch (exception) {
yield Left(EditDefaultIdentityFailure(exception)); yield Left(EditDefaultIdentityFailure(exception));
@@ -0,0 +1,45 @@
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
import 'package:jmap_dart_client/jmap/identities/identity.dart';
import 'package:tmail_ui_user/features/mailbox/domain/exceptions/null_session_or_accountid_exception.dart';
import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/edit_default_identity_state.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_controller.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart';
extension VerifyDefaultIdentitySupportedExtension on IdentitiesController {
bool get isDefaultIdentitySupported {
final accountId = accountDashBoardController.accountId.value;
final session = accountDashBoardController.sessionCurrent;
if (session != null && accountId != null) {
return [CapabilityIdentifier.jamesSortOrder].isSupported(
session,
accountId,
);
} else {
return false;
}
}
void changeIdentityAsDefault(Identity identity) {
final accountId = accountDashBoardController.accountId.value;
final session = accountDashBoardController.sessionCurrent;
if (session != null && accountId != null && identity.id != null) {
editIdentityAction(
session,
accountId,
EditIdentityRequest.fromIdentityWithoutPublicAssets(
identity.asDefault(),
),
);
} else {
consumeState(
Stream.value(
Left(EditDefaultIdentityFailure(NullSessionOrAccountIdException())),
),
);
}
}
}
@@ -1,4 +1,6 @@
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart';
extension IdentityExtension on Identity { extension IdentityExtension on Identity {
@@ -16,4 +18,32 @@ extension IdentityExtension on Identity {
IdentitySignature? toIdentitySignature() => id != null IdentitySignature? toIdentitySignature() => id != null
? IdentitySignature(identityId: id!, signature: signatureAsString) ? IdentitySignature(identityId: id!, signature: signatureAsString)
: null; : null;
Identity asDefault() => copyWith(sortOrder: UnsignedInt(0));
Identity copyWith({
IdentityId? id,
String? description,
String? name,
String? email,
Set<EmailAddress>? bcc,
Set<EmailAddress>? replyTo,
Signature? textSignature,
Signature? htmlSignature,
bool? mayDelete,
UnsignedInt? sortOrder,
}) {
return Identity(
id: id ?? this.id,
description: description ?? this.description,
name: name ?? this.name,
email: email ?? this.email,
bcc: bcc ?? this.bcc,
replyTo: replyTo ?? this.replyTo,
textSignature: textSignature ?? this.textSignature,
htmlSignature: htmlSignature ?? this.htmlSignature,
mayDelete: mayDelete ?? this.mayDelete,
sortOrder: sortOrder ?? this.sortOrder,
);
}
} }
@@ -236,7 +236,7 @@ class IdentitiesController extends ReloadableController implements BeforeReconne
if (newIdentityArguments is CreateNewIdentityRequest) { if (newIdentityArguments is CreateNewIdentityRequest) {
_createNewIdentityAction(session, accountId, newIdentityArguments); _createNewIdentityAction(session, accountId, newIdentityArguments);
} else if (newIdentityArguments is EditIdentityRequest) { } else if (newIdentityArguments is EditIdentityRequest) {
_editIdentityAction(session, accountId, newIdentityArguments); editIdentityAction(session, accountId, newIdentityArguments);
} }
} }
} }
@@ -388,12 +388,12 @@ class IdentitiesController extends ReloadableController implements BeforeReconne
if (newIdentityArguments is CreateNewIdentityRequest) { if (newIdentityArguments is CreateNewIdentityRequest) {
_createNewIdentityAction(session, accountId, newIdentityArguments); _createNewIdentityAction(session, accountId, newIdentityArguments);
} else if (newIdentityArguments is EditIdentityRequest) { } else if (newIdentityArguments is EditIdentityRequest) {
_editIdentityAction(session, accountId, newIdentityArguments); editIdentityAction(session, accountId, newIdentityArguments);
} }
} }
} }
void _editIdentityAction( void editIdentityAction(
Session session, Session session,
AccountId accountId, AccountId accountId,
EditIdentityRequest editIdentityRequest EditIdentityRequest editIdentityRequest
@@ -406,19 +406,18 @@ class IdentitiesController extends ReloadableController implements BeforeReconne
} }
void _editIdentitySuccess(EditIdentitySuccess success) { void _editIdentitySuccess(EditIdentitySuccess success) {
_removeIdentityCache(); toastManager.showMessageSuccess(success);
if (currentOverlayContext != null && currentContext != null) { if (!success.isSetAsDefault) {
appToast.showToastSuccessMessage( _removeIdentityCache();
currentOverlayContext!,
AppLocalizations.of(currentContext!).you_are_changed_your_identity_successfully); _cleanUpPublicAssets(
success.identityId,
IdentityActionType.edit,
success.publicAssetsInIdentityArguments,
);
} }
_cleanUpPublicAssets(
success.identityId,
IdentityActionType.edit,
success.publicAssetsInIdentityArguments);
_refreshAllIdentities(); _refreshAllIdentities();
} }
@@ -491,7 +490,7 @@ class IdentitiesController extends ReloadableController implements BeforeReconne
if (newIdentityArguments is CreateNewIdentityRequest) { if (newIdentityArguments is CreateNewIdentityRequest) {
_createNewIdentityAction(session, accountId, newIdentityArguments); _createNewIdentityAction(session, accountId, newIdentityArguments);
} else if (newIdentityArguments is EditIdentityRequest) { } else if (newIdentityArguments is EditIdentityRequest) {
_editIdentityAction(session, accountId, newIdentityArguments); editIdentityAction(session, accountId, newIdentityArguments);
} }
} }
@@ -1,9 +1,12 @@
import 'package:core/presentation/extensions/either_view_state_extension.dart';
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/list/no_stretch_scroll_behavior.dart'; import 'package:core/presentation/views/list/no_stretch_scroll_behavior.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/edit_default_identity_state.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/base/setting_detail_view_builder.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/base/setting_detail_view_builder.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/change_identity_as_default_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/create_new_identity_button_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/create_new_identity_button_widget.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/identity_list_tile_builder.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/identity_list_tile_builder.dart';
@@ -88,41 +91,60 @@ class IdentitiesView extends GetWidget<IdentitiesController> {
)), )),
Expanded( Expanded(
child: Obx(() { child: Obx(() {
final identities = controller.listAllIdentities;
final isDefaultIdentitySupported = controller.isDefaultIdentitySupported;
final identitySelected = controller.identitySelected.value;
final Widget listView = ListView.separated( final Widget listView = ListView.separated(
shrinkWrap: true, shrinkWrap: true,
itemCount: controller.listAllIdentities.length + 1, itemCount: identities.length + 1,
controller: controller.listIdentityScrollController, controller: controller.listIdentityScrollController,
padding: const EdgeInsetsDirectional.only( padding: EdgeInsetsDirectional.only(
start: 24, start: isDefaultIdentitySupported ? 12 : 24,
end: 24, end: 24,
bottom: 24, bottom: 24,
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == controller.listAllIdentities.length) { if (index == identities.length) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
final identity = controller.listAllIdentities[index]; final identity = identities[index];
final isSelected = identity == controller.identitySelected.value; final isSelected = identity == identitySelected;
return Obx(() => IdentityListTileBuilder(
imagePaths: controller.imagePaths, return Obx(() {
identity: identity, final loadingState = controller.viewState.value
isSelected: isSelected, .foldSuccessWithResult<EditDefaultIdentityLoading>();
mapIdentitySignatures: controller.mapIdentitySignatures,
signatureViewState: controller.signatureViewState.value, bool isItemLoading =
isDesktop: isDesktop, loadingState is EditDefaultIdentityLoading &&
scrollController: controller.listIdentityScrollController, loadingState.isSetAsDefault;
onEditIdentityAction: (identitySelected) =>
controller.goToEditIdentity(context, identitySelected), return IdentityListTileBuilder(
onDeleteIdentityAction: (identitySelected) => imagePaths: controller.imagePaths,
controller.openConfirmationDialogDeleteIdentityAction( identity: identity,
context, isSelected: isSelected,
identitySelected, isDefaultIdentitySupported: isDefaultIdentitySupported,
), mapIdentitySignatures: controller.mapIdentitySignatures,
)); signatureViewState: controller.signatureViewState.value,
isDesktop: isDesktop,
isItemLoading: isItemLoading,
scrollController: controller.listIdentityScrollController,
onEditIdentityAction: (identitySelected) =>
controller.goToEditIdentity(context, identitySelected),
onDeleteIdentityAction: (identitySelected) =>
controller.openConfirmationDialogDeleteIdentityAction(
context,
identitySelected,
),
onChangeIdentityAsDefaultAction: controller.changeIdentityAsDefault,
);
});
}, },
separatorBuilder: (_, index) => Padding( separatorBuilder: (_, index) => Padding(
padding: const EdgeInsets.symmetric(vertical: 24), padding: isDefaultIdentitySupported
? const EdgeInsets.only(top: 24, bottom: 12)
: const EdgeInsets.symmetric(vertical: 24),
child: Divider(color: Colors.black.withValues(alpha: 0.08)), child: Divider(color: Colors.black.withValues(alpha: 0.08)),
), ),
); );
@@ -3,9 +3,9 @@ import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart';
import 'package:model/extensions/list_email_address_extension.dart'; import 'package:model/extensions/list_email_address_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart';
@@ -14,6 +14,8 @@ import 'package:tmail_ui_user/features/manage_account/presentation/identities/wi
import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/signature_loading_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/signature_loading_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnChangeIdentityAsDefaultAction = Function(Identity identity);
class IdentityListTileBuilder extends StatelessWidget { class IdentityListTileBuilder extends StatelessWidget {
const IdentityListTileBuilder({ const IdentityListTileBuilder({
@@ -26,7 +28,10 @@ class IdentityListTileBuilder extends StatelessWidget {
required this.onDeleteIdentityAction, required this.onDeleteIdentityAction,
this.isDesktop = false, this.isDesktop = false,
this.isSelected = false, this.isSelected = false,
this.isDefaultIdentitySupported = false,
this.isItemLoading = false,
this.scrollController, this.scrollController,
this.onChangeIdentityAsDefaultAction,
}) : super(key: key); }) : super(key: key);
final Identity identity; final Identity identity;
@@ -35,17 +40,26 @@ class IdentityListTileBuilder extends StatelessWidget {
final ImagePaths imagePaths; final ImagePaths imagePaths;
final bool isSelected; final bool isSelected;
final bool isDesktop; final bool isDesktop;
final bool isDefaultIdentitySupported;
final bool isItemLoading;
final OnEditIdentityAction onEditIdentityAction; final OnEditIdentityAction onEditIdentityAction;
final OnDeleteIdentityAction onDeleteIdentityAction; final OnDeleteIdentityAction onDeleteIdentityAction;
final OnChangeIdentityAsDefaultAction? onChangeIdentityAsDefaultAction;
final ScrollController? scrollController; final ScrollController? scrollController;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final selectedIcon = SvgPicture.asset( final selectedIcon = AbsorbPointer(
isSelected ? imagePaths.icRadioSelected : imagePaths.icRadio, absorbing: isItemLoading,
width: 18, child: TMailButtonWidget.fromIcon(
height: 18, icon: isSelected ? imagePaths.icRadioSelected : imagePaths.icRadio,
fit: BoxFit.fill, iconSize: 18,
padding: const EdgeInsets.all(12),
backgroundColor: Colors.transparent,
borderRadius: 100,
onTapActionCallback: () =>
onChangeIdentityAsDefaultAction?.call(identity),
),
); );
final signatureContent = mapIdentitySignatures[identity.id!] ?? final signatureContent = mapIdentitySignatures[identity.id!] ??
@@ -113,18 +127,30 @@ class IdentityListTileBuilder extends StatelessWidget {
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
selectedIcon, if (isDefaultIdentitySupported)
Padding(
padding: const EdgeInsetsDirectional.only(end: 6),
child: selectedIcon,
),
Container( Container(
width: 280, width: 280,
padding: const EdgeInsetsDirectional.only(start: 18, end: 12), padding: EdgeInsetsDirectional.only(
end: 12,
top: isDefaultIdentitySupported ? 10 : 0,
),
child: identityContent, child: identityContent,
), ),
ListIdentityItemActionsWidget( Padding(
identity: identity, padding: isDefaultIdentitySupported
imagePaths: imagePaths, ? const EdgeInsetsDirectional.only(top: 10)
onEditIdentityAction: onEditIdentityAction, : EdgeInsetsDirectional.zero,
onDeleteIdentityAction: onDeleteIdentityAction, child: ListIdentityItemActionsWidget(
isDesktop: isDesktop, identity: identity,
imagePaths: imagePaths,
onEditIdentityAction: onEditIdentityAction,
onDeleteIdentityAction: onDeleteIdentityAction,
isDesktop: isDesktop,
),
), ),
const Spacer(), const Spacer(),
], ],
@@ -134,14 +160,24 @@ class IdentityListTileBuilder extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( if (isDefaultIdentitySupported)
crossAxisAlignment: CrossAxisAlignment.start, Row(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
selectedIcon, children: [
const SizedBox(width: 18), Padding(
Expanded(child: identityContent), padding: const EdgeInsetsDirectional.only(end: 6),
], child: selectedIcon,
), ),
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 10),
child: identityContent,
),
),
],
)
else
identityContent,
const SizedBox(height: 24), const SizedBox(height: 24),
ListIdentityItemActionsWidget( ListIdentityItemActionsWidget(
identity: identity, identity: identity,
+6
View File
@@ -4887,5 +4887,11 @@
"placeholders": { "placeholders": {
"count": {} "count": {}
} }
},
"defaultIdentitySetupSuccessful": "Default identity setup successful",
"@defaultIdentitySetupSuccessful": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -5161,4 +5161,11 @@ class AppLocalizations {
args: [count], args: [count],
); );
} }
String get defaultIdentitySetupSuccessful {
return Intl.message(
'Default identity setup successful',
name: 'defaultIdentitySetupSuccessful',
);
}
} }
+53 -45
View File
@@ -6,7 +6,6 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/presentation/utils/app_toast.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_appauth_web/authorization_exception.dart'; import 'package:flutter_appauth_web/authorization_exception.dart';
import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart'; import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart';
@@ -27,9 +26,10 @@ import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_ex
import 'package:tmail_ui_user/features/login/domain/exceptions/oauth_authorization_error.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/oauth_authorization_error.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/add_recipient_in_forwarding_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/add_recipient_in_forwarding_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/delete_recipient_in_forwarding_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/edit_local_copy_in_forwarding_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_rule_filter_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_rule_filter_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/delete_recipient_in_forwarding_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/edit_identity_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/edit_local_copy_in_forwarding_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart';
import 'package:tmail_ui_user/features/starting_page/domain/state/sign_in_twake_workplace_state.dart'; import 'package:tmail_ui_user/features/starting_page/domain/state/sign_in_twake_workplace_state.dart';
import 'package:tmail_ui_user/features/starting_page/domain/state/sign_up_twake_workplace_state.dart'; import 'package:tmail_ui_user/features/starting_page/domain/state/sign_up_twake_workplace_state.dart';
@@ -46,47 +46,48 @@ class ToastManager {
ToastManager(this.appToast); ToastManager(this.appToast);
String getDefaultMessageByException(BuildContext context, dynamic exception) { String getDefaultMessageByException(
AppLocalizations appLocalizations,
dynamic exception,
) {
try { try {
return AppLocalizations.of(context).unexpectedError(exception.message); return appLocalizations.unexpectedError(exception.message);
} catch (_) { } catch (_) {
return AppLocalizations.of(context).unexpectedError( return appLocalizations.unexpectedError(exception.toString());
exception.toString(),
);
} }
} }
String? getMessageByException( String? getMessageByException(
BuildContext context, AppLocalizations appLocalizations,
dynamic exception, { dynamic exception, {
bool useDefaultMessage = false, bool useDefaultMessage = false,
}) { }) {
if (exception is CanNotFoundBaseUrl) { if (exception is CanNotFoundBaseUrl) {
return AppLocalizations.of(context).requiredUrl; return appLocalizations.requiredUrl;
} else if (exception is CanNotFoundUserName) { } else if (exception is CanNotFoundUserName) {
return AppLocalizations.of(context).requiredEmail; return appLocalizations.requiredEmail;
} else if (exception is CanNotFoundPassword) { } else if (exception is CanNotFoundPassword) {
return AppLocalizations.of(context).requiredPassword; return appLocalizations.requiredPassword;
} else if (exception is CanNotFoundOIDCLinks) { } else if (exception is CanNotFoundOIDCLinks) {
return AppLocalizations.of(context).ssoNotAvailable; return appLocalizations.ssoNotAvailable;
} else if (exception is CanNotFindToken) { } else if (exception is CanNotFindToken) {
return AppLocalizations.of(context).canNotGetToken; return appLocalizations.canNotGetToken;
} else if (exception is ConnectionTimeout || } else if (exception is ConnectionTimeout ||
exception is BadGateway || exception is BadGateway ||
exception is SocketError) { exception is SocketError) {
return AppLocalizations.of(context).wrongUrlMessage; return appLocalizations.wrongUrlMessage;
} else if (exception is BadCredentialsException) { } else if (exception is BadCredentialsException) {
return AppLocalizations.of(context).badCredentials; return appLocalizations.badCredentials;
} else if (exception is ConnectionError) { } else if (exception is ConnectionError) {
return AppLocalizations.of(context).connectionError; return appLocalizations.connectionError;
} else if (exception is UnknownError && exception.message != null) { } else if (exception is UnknownError && exception.message != null) {
return '[${exception.code ?? ''}] ${exception.message}'; return '[${exception.code ?? ''}] ${exception.message}';
} else if (exception is NotFoundSessionException) { } else if (exception is NotFoundSessionException) {
return AppLocalizations.of(context).notFoundSession; return appLocalizations.notFoundSession;
} else if (exception is NoNetworkError) { } else if (exception is NoNetworkError) {
return AppLocalizations.of(context).youAreOffline; return appLocalizations.youAreOffline;
} else if (exception is HandshakeException) { } else if (exception is HandshakeException) {
return AppLocalizations.of(context) return appLocalizations
.handshakeException(exception.osError?.message ?? ''); .handshakeException(exception.osError?.message ?? '');
} else if (exception is SetMethodException && } else if (exception is SetMethodException &&
exception.mapErrors.isNotEmpty) { exception.mapErrors.isNotEmpty) {
@@ -104,12 +105,11 @@ class ToastManager {
exception.message?.isNotEmpty == true) { exception.message?.isNotEmpty == true) {
return exception.message!; return exception.message!;
} else if (exception is NotGrantedPermissionStorageException) { } else if (exception is NotGrantedPermissionStorageException) {
return AppLocalizations.of(context) return appLocalizations.youNeedToGrantFilesPermissionToExportFile;
.youNeedToGrantFilesPermissionToExportFile;
} else if (exception is NotFoundFileInFolderException) { } else if (exception is NotFoundFileInFolderException) {
return AppLocalizations.of(context).noLogsHaveBeenRecordedYet; return appLocalizations.noLogsHaveBeenRecordedYet;
} else if (exception is CannotOpenNewWindowException) { } else if (exception is CannotOpenNewWindowException) {
return AppLocalizations.of(context).cannotOpenNewWindow; return appLocalizations.cannotOpenNewWindow;
} else if (exception is CannotReplyCalendarEventException) { } else if (exception is CannotReplyCalendarEventException) {
final mapErrors = exception.mapErrors ?? {}; final mapErrors = exception.mapErrors ?? {};
if (mapErrors.isNotEmpty) { if (mapErrors.isNotEmpty) {
@@ -125,7 +125,7 @@ class ToastManager {
} }
if (useDefaultMessage) { if (useDefaultMessage) {
return getDefaultMessageByException(context, exception); return getDefaultMessageByException(appLocalizations, exception);
} else { } else {
return null; return null;
} }
@@ -140,50 +140,51 @@ class ToastManager {
} }
final exception = failure.exception; final exception = failure.exception;
String? message = getMessageByException(context, exception); final appLocalizations = AppLocalizations.of(context);
String? message = getMessageByException(appLocalizations, exception);
if (failure is GetSessionFailure) { if (failure is GetSessionFailure) {
message = message ?? message = message ??
getMessageByException( getMessageByException(
context, appLocalizations,
exception, exception,
useDefaultMessage: true, useDefaultMessage: true,
); );
} else if (_isEmptySpamFolderFailure(exception)) { } else if (_isEmptySpamFolderFailure(exception)) {
message = message ?? AppLocalizations.of(context).emptySpamFolderFailed; message = message ?? appLocalizations.emptySpamFolderFailed;
} else if (_isEmptyTrashFolderFailure(failure)) { } else if (_isEmptyTrashFolderFailure(failure)) {
message = message ?? AppLocalizations.of(context).emptyTrashFolderFailed; message = message ?? appLocalizations.emptyTrashFolderFailed;
} else if (_isMarkAsSpamFailure(failure)) { } else if (_isMarkAsSpamFailure(failure)) {
message = message ?? AppLocalizations.of(context).markAsSpamFailed; message = message ??appLocalizations.markAsSpamFailed;
} else if (_isArchiveMessagesFailure(failure)) { } else if (_isArchiveMessagesFailure(failure)) {
message = message ?? AppLocalizations.of(context).archiveMessagesFailed; message = message ?? appLocalizations.archiveMessagesFailed;
} else if (failure is SignInTwakeWorkplaceFailure) { } else if (failure is SignInTwakeWorkplaceFailure) {
message = message ?? AppLocalizations.of(context).sigInSaasFailed; message = message ?? appLocalizations.sigInSaasFailed;
} else if (failure is SignUpTwakeWorkplaceFailure) { } else if (failure is SignUpTwakeWorkplaceFailure) {
message = message ?? AppLocalizations.of(context).createTwakeIdFailed; message = message ?? appLocalizations.createTwakeIdFailed;
} else if (failure is ParseEmailByBlobIdFailure) { } else if (failure is ParseEmailByBlobIdFailure) {
message = message =
message ?? AppLocalizations.of(context).parseEmailByBlobIdFailed; message ?? appLocalizations.parseEmailByBlobIdFailed;
} else if (failure is PreviewEmailFromEmlFileFailure) { } else if (failure is PreviewEmailFromEmlFileFailure) {
message = message =
message ?? AppLocalizations.of(context).previewEmailFromEMLFileFailed; message ?? appLocalizations.previewEmailFromEMLFileFailed;
} else if (failure is ExportTraceLogFailure) { } else if (failure is ExportTraceLogFailure) {
message = message ?? AppLocalizations.of(context).exportTraceLogFailed; message = message ?? appLocalizations.exportTraceLogFailed;
} else if (failure is AddRecipientsInForwardingFailure || } else if (failure is AddRecipientsInForwardingFailure ||
failure is AddRecipientsInForwardingSuccessWithSomeCaseFailure) { failure is AddRecipientsInForwardingSuccessWithSomeCaseFailure) {
message = message ?? AppLocalizations.of(context).addRecipientsFailed; message = message ?? appLocalizations.addRecipientsFailed;
} else if (failure is EditLocalCopyInForwardingFailure || } else if (failure is EditLocalCopyInForwardingFailure ||
failure is EditLocalCopyInForwardingSuccessWithSomeCaseFailure) { failure is EditLocalCopyInForwardingSuccessWithSomeCaseFailure) {
message = message =
message ?? AppLocalizations.of(context).editLocalCopyInForwardFailed; message ?? appLocalizations.editLocalCopyInForwardFailed;
} else if (failure is DeleteRecipientInForwardingFailure || } else if (failure is DeleteRecipientInForwardingFailure ||
failure is DeleteRecipientInForwardingSuccessWithSomeCaseFailure) { failure is DeleteRecipientInForwardingSuccessWithSomeCaseFailure) {
message = message ?? AppLocalizations.of(context).deleteRecipientsFailed; message = message ?? appLocalizations.deleteRecipientsFailed;
} else if (failure is CreateNewRuleFilterFailure) { } else if (failure is CreateNewRuleFilterFailure) {
message = message ?? AppLocalizations.of(context).createFilterRuleFailed; message = message ?? appLocalizations.createFilterRuleFailed;
} else if (failure is CalendarEventReplyFailure) { } else if (failure is CalendarEventReplyFailure) {
message = message ?? message = message ??
AppLocalizations.of(context).eventReplyWasSentUnsuccessfully; appLocalizations.eventReplyWasSentUnsuccessfully;
} }
log('ToastManager::showMessageFailure: Message: $message'); log('ToastManager::showMessageFailure: Message: $message');
if (message?.trim().isNotEmpty == true) { if (message?.trim().isNotEmpty == true) {
@@ -224,18 +225,25 @@ class ToastManager {
} }
String? message; String? message;
final appLocalizations = AppLocalizations.of(context);
if (success is ClearMailboxSuccess || if (success is ClearMailboxSuccess ||
success is EmptySpamFolderSuccess || success is EmptySpamFolderSuccess ||
success is EmptyTrashFolderSuccess) { success is EmptyTrashFolderSuccess) {
message = message =
AppLocalizations.of(context).toast_message_empty_trash_folder_success; appLocalizations.toast_message_empty_trash_folder_success;
} else if (success is ExportTraceLogSuccess) { } else if (success is ExportTraceLogSuccess) {
message = AppLocalizations.of(context).exportTraceLogSuccess( message = appLocalizations.exportTraceLogSuccess(
success.savePath, success.savePath,
); );
} else if (success is CreateNewRuleFilterSuccess) { } else if (success is CreateNewRuleFilterSuccess) {
message = AppLocalizations.of(context).newFilterWasCreated; message = appLocalizations.newFilterWasCreated;
} else if (success is EditIdentitySuccess) {
if (success.isSetAsDefault) {
message = appLocalizations.defaultIdentitySetupSuccessful;
} else {
message = appLocalizations.you_are_changed_your_identity_successfully;
}
} }
log('ToastManager::showMessageSuccess: Message: $message'); log('ToastManager::showMessageSuccess: Message: $message');
if (message?.trim().isNotEmpty == true) { if (message?.trim().isNotEmpty == true) {