TF-2035 Show calendar event banner in email view

(cherry picked from commit 130f575da86e23095d2b626ff8af133a7122ab2e)
This commit is contained in:
dab246
2023-07-24 19:40:09 +07:00
committed by Dat Vu
parent 41e0f841da
commit 588614f034
16 changed files with 570 additions and 22 deletions
+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 16.4L10.4 18C10.2167 18.1833 9.98333 18.275 9.7 18.275C9.41667 18.275 9.18333 18.1833 9 18C8.81667 17.8167 8.725 17.5833 8.725 17.3C8.725 17.0167 8.81667 16.7833 9 16.6L10.6 15L9 13.4C8.81667 13.2167 8.725 12.9833 8.725 12.7C8.725 12.4167 8.81667 12.1833 9 12C9.18333 11.8167 9.41667 11.725 9.7 11.725C9.98333 11.725 10.2167 11.8167 10.4 12L12 13.6L13.6 12C13.7833 11.8167 14.0167 11.725 14.3 11.725C14.5833 11.725 14.8167 11.8167 15 12C15.1833 12.1833 15.275 12.4167 15.275 12.7C15.275 12.9833 15.1833 13.2167 15 13.4L13.4 15L15 16.6C15.1833 16.7833 15.275 17.0167 15.275 17.3C15.275 17.5833 15.1833 17.8167 15 18C14.8167 18.1833 14.5833 18.275 14.3 18.275C14.0167 18.275 13.7833 18.1833 13.6 18L12 16.4ZM5 22C4.45 22 3.97917 21.8042 3.5875 21.4125C3.19583 21.0208 3 20.55 3 20V6C3 5.45 3.19583 4.97917 3.5875 4.5875C3.97917 4.19583 4.45 4 5 4H6V3C6 2.71667 6.09583 2.47917 6.2875 2.2875C6.47917 2.09583 6.71667 2 7 2C7.28333 2 7.52083 2.09583 7.7125 2.2875C7.90417 2.47917 8 2.71667 8 3V4H16V3C16 2.71667 16.0958 2.47917 16.2875 2.2875C16.4792 2.09583 16.7167 2 17 2C17.2833 2 17.5208 2.09583 17.7125 2.2875C17.9042 2.47917 18 2.71667 18 3V4H19C19.55 4 20.0208 4.19583 20.4125 4.5875C20.8042 4.97917 21 5.45 21 6V20C21 20.55 20.8042 21.0208 20.4125 21.4125C20.0208 21.8042 19.55 22 19 22H5ZM5 20H19V10H5V20ZM5 8H19V6H5V8Z"
fill="#FF3347" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M15.95 21.575C15.8167 21.575 15.6917 21.5542 15.575 21.5125C15.4583 21.4708 15.35 21.4 15.25 21.3L12.4 18.45C12.2167 18.2667 12.125 18.0333 12.125 17.75C12.125 17.4667 12.2167 17.2333 12.4 17.05C12.5833 16.8667 12.8167 16.775 13.1 16.775C13.3833 16.775 13.6167 16.8667 13.8 17.05L15.95 19.2L20.9 14.25C21.0833 14.0667 21.3167 13.975 21.6 13.975C21.8833 13.975 22.1167 14.0667 22.3 14.25C22.4833 14.4333 22.575 14.6667 22.575 14.95C22.575 15.2333 22.4833 15.4667 22.3 15.65L16.65 21.3C16.55 21.4 16.4417 21.4708 16.325 21.5125C16.2083 21.5542 16.0833 21.575 15.95 21.575ZM4 6L12 11L20 6H4ZM4 20C3.45 20 2.97917 19.8042 2.5875 19.4125C2.19583 19.0208 2 18.55 2 18V6C2 5.45 2.19583 4.97917 2.5875 4.5875C2.97917 4.19583 3.45 4 4 4H20C20.55 4 21.0208 4.19583 21.4125 4.5875C21.8042 4.97917 22 5.45 22 6V10.35L20 12.35V8L12.525 12.675C12.4417 12.725 12.3542 12.7625 12.2625 12.7875C12.1708 12.8125 12.0833 12.825 12 12.825C11.9167 12.825 11.8292 12.8125 11.7375 12.7875C11.6458 12.7625 11.5583 12.725 11.475 12.675L4 8V18H9.15L11.15 20H4Z"
fill="#007AFF" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M5 19H6.4L15.025 10.375L13.625 8.975L5 17.6V19ZM19.3 8.925L15.05 4.725L16.45 3.325C16.8333 2.94167 17.3042 2.75 17.8625 2.75C18.4208 2.75 18.8917 2.94167 19.275 3.325L20.675 4.725C21.0583 5.10833 21.2583 5.57083 21.275 6.1125C21.2917 6.65417 21.1083 7.11667 20.725 7.5L19.3 8.925ZM4 21C3.71667 21 3.47917 20.9042 3.2875 20.7125C3.09583 20.5208 3 20.2833 3 20V17.175C3 17.0417 3.025 16.9125 3.075 16.7875C3.125 16.6625 3.2 16.55 3.3 16.45L13.6 6.15L17.85 10.4L7.55 20.7C7.45 20.8 7.3375 20.875 7.2125 20.925C7.0875 20.975 6.95833 21 6.825 21H4Z"
fill="#43A843" />
</svg>

After

Width:  |  Height:  |  Size: 695 B

@@ -186,6 +186,11 @@ extension AppColor on Color {
static const colorNetworkConnectionLabel = Color(0xFF818C99);
static const colorCalendarEventRead = Color(0xFF818C99);
static const colorCalendarEventUnread = Color(0xFF1C1B1F);
static const colorMaybeEventActionText = Color(0xFFFFC107);
static const colorInvitedEventActionText = Color(0xFF007AFF);
static const colorUpdatedEventActionText = Color(0xFF4BB34B);
static const colorCanceledEventActionText = Color(0xFFFF3347);
static const colorSubTitleEventActionText = Color(0xFF939393);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
@@ -195,6 +195,9 @@ class ImagePaths {
String get icArrowRight => _getImagePath('ic_arrow_right.svg');
String get icAddPicture => _getImagePath('ic_add_picture.svg');
String get icCalendarEvent => _getImagePath('ic_calendar_event.svg');
String get icEventInvited => _getImagePath('ic_event_invited.svg');
String get icEventUpdated => _getImagePath('ic_event_updated.svg');
String get icEventCanceled => _getImagePath('ic_event_canceled.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
@@ -1,8 +1,17 @@
import 'package:collection/collection.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/email/domain/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/offline_mode/model/attachment_hive_cache.dart';
extension ListAttachmentsExtension on List<Attachment> {
List<AttachmentHiveCache> toHiveCache() => map((attachment) => attachment.toHiveCache()).toList();
Set<Attachment> get calendarAttachments => where((attachment) => attachment.isCalendarEvent).toSet();
Set<Id> get calendarEventBlobIds => calendarAttachments
.map((attachment) => attachment.blobId)
.whereNotNull()
.toSet();
}
@@ -1,4 +1,5 @@
import 'package:get/get.dart';
import 'package:jmap_dart_client/http/http_client.dart';
import 'package:tmail_ui_user/features/base/interactors_bindings.dart';
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/calendar_event_datasource_impl.dart';
@@ -17,6 +18,7 @@ class CalendarEventInteractorBindings extends InteractorsBindings {
@override
void bindingsDataSourceImpl() {
Get.lazyPut(() => CalendarEventAPI(Get.find<HttpClient>()));
Get.lazyPut(() => CalendarEventDataSourceImpl(
Get.find<CalendarEventAPI>(),
Get.find<RemoteExceptionThrower>()));
@@ -6,7 +6,6 @@ import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
@@ -14,6 +13,7 @@ import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/identities/identity.dart';
import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:jmap_dart_client/jmap/mdn/disposition.dart';
@@ -27,7 +27,9 @@ import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
import 'package:tmail_ui_user/features/email/domain/extensions/list_attachments_extension.dart';
import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_calendar_event_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/bindings/calendar_event_interactor_bindings.dart';
@@ -109,6 +111,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
final attachmentsExpandMode = ExpandMode.COLLAPSE.obs;
final emailContents = RxnString();
final attachments = <Attachment>[].obs;
final calendarEvent = Rxn<CalendarEvent>();
EmailId? _currentEmailId;
Identity? _identitySelected;
String? initialEmailContents;
@@ -175,6 +179,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_sendReceiptToSenderSuccess(success);
} else if (success is CreateNewRuleFilterSuccess) {
_createNewRuleFilterSuccess(success);
} else if (success is ParseCalendarEventSuccess) {
_handleParseCalendarEventSuccess(success);
}
}
@@ -391,6 +397,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
initialEmailContents = success.emailContent;
attachments.value = success.attachments;
_loadCalendarEventAction(success.attachments.calendarEventBlobIds);
final isShowMessageReadReceipt = success.emailCurrent?.hasReadReceipt(mailboxDashBoardController.mapMailboxById) == true;
if (isShowMessageReadReceipt) {
_handleReadReceipt();
@@ -416,6 +424,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
initialEmailContents = success.emailContent;
attachments.value = success.attachments;
_loadCalendarEventAction(success.attachments.calendarEventBlobIds);
if (PlatformInfo.isMobile) {
final detailedEmail = DetailedEmail(
emailId: currentEmail!.id!,
@@ -461,6 +471,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
emailContents.value = null;
initialEmailContents = null;
attachments.clear();
calendarEvent.value = null;
}
PresentationMailbox? getMailboxContain(PresentationEmail email) {
@@ -1229,4 +1240,45 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
consumeState(_storeOpenedEmailInteractor.execute(session, accountId, detailedEmail));
}
}
void _loadCalendarEventAction(Set<Id> blobIds) {
log('SingleEmailController::_loadCalendarEventAction:blobIds: $blobIds');
if (_isCalendarEventSupported) {
if (currentEmail?.hasCalendarEvent == true &&
blobIds.isNotEmpty &&
mailboxDashBoardController.accountId.value != null) {
_parseCalendarEventAction(
mailboxDashBoardController.accountId.value!,
blobIds
);
} else {
logError('SingleEmailController::_loadCalendarEventAction: not found calendar event header');
}
} else {
logError('SingleEmailController::_loadCalendarEventAction: calendar event not supported');
}
}
bool get _isCalendarEventSupported {
final accountId = mailboxDashBoardController.accountId.value;
final session = mailboxDashBoardController.sessionCurrent;
return session != null &&
accountId != null &&
CapabilityIdentifier.jamesCalendarEvent.isSupported(session, accountId);
}
void _parseCalendarEventAction(AccountId accountId, Set<Id> blobIds) {
log("SingleEmailController::_parseCalendarEventAction:blobIds: $blobIds");
if (_parseCalendarEventInteractor != null) {
consumeState(_parseCalendarEventInteractor!.execute(accountId, blobIds));
} else {
logError("SingleEmailController::_parseCalendarEventAction: _parseCalendarEventInteractor is NULL");
}
}
void _handleParseCalendarEventSuccess(ParseCalendarEventSuccess success) {
if (success.calendarEventList.isNotEmpty) {
calendarEvent.value = success.calendarEventList.first;
}
}
}
+42 -20
View File
@@ -1,6 +1,5 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/icon_utils.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/button/icon_button_web.dart';
@@ -11,7 +10,6 @@ import 'package:core/utils/app_logger.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
@@ -21,12 +19,17 @@ import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/list_attachment_extension.dart';
import 'package:model/extensions/presentation_email_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/base/widget/custom_scroll_behavior.dart';
import 'package:tmail_ui_user/features/base/widget/popup_item_widget.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart';
import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_styles.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/app_bar_mail_widget_builder.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_file_tile_builder.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mail_widget_builder.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/calendar_event_action_banner_widget.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/information_sender_and_receiver_builder.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
@@ -34,7 +37,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widg
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';
class EmailView extends GetWidget<SingleEmailController> {
class EmailView extends GetWidget<SingleEmailController> with AppLoaderMixin {
final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
@@ -278,38 +281,57 @@ class EmailView extends GetWidget<SingleEmailController> {
imagePaths: imagePaths,
responsiveUtils: responsiveUtils,
),
_buildLoadingView(),
_buildLoadingContentView(),
_buildAttachments(context),
Obx(() {
if (controller.calendarEvent.value != null) {
return CalendarEventActionBannerWidget(
calendarEvent: controller.calendarEvent.value!,
listFromEmailAddress: controller.currentEmail?.from
);
} else {
return _buildLoadingCalendarEventBanner();
}
}),
if (PlatformInfo.isWeb)
Expanded(child: Padding(
padding: EdgeInsets.only(
left: AppUtils.isDirectionRTL(context) ? 0 : 16,
right: AppUtils.isDirectionRTL(context) ? 16 : 0,
bottom: 16
),
padding: const EdgeInsetsDirectional.only(start: 16, bottom: 16),
child: _buildEmailContent(context, constraints, email)
))
else
Padding(
padding: const EdgeInsets.all(16),
child: _buildEmailContent(context, constraints, email))
padding: const EdgeInsetsDirectional.symmetric(
vertical: EmailViewStyles.mobileContentVerticalMargin,
horizontal: EmailViewStyles.mobileContentHorizontalMargin
),
child: _buildEmailContent(context, constraints, email)
)
],
);
});
}
Widget _buildLoadingView() {
Widget _buildLoadingContentView() {
return Obx(() {
return controller.viewState.value.fold(
(failure) => const SizedBox.shrink(),
(success) {
if (success is LoadingState) {
return const Align(alignment: Alignment.topCenter, child: Padding(
padding: EdgeInsets.all(16),
child: SizedBox(
width: 30,
height: 30,
child: CupertinoActivityIndicator(color: AppColor.colorLoading))));
if (success is GetEmailContentLoading) {
return loadingWidget;
} else {
return const SizedBox.shrink();
}
});
});
}
Widget _buildLoadingCalendarEventBanner() {
return Obx(() {
return controller.viewState.value.fold(
(failure) => const SizedBox.shrink(),
(success) {
if (success is ParseCalendarEventLoading) {
return loadingWidget;
} else {
return const SizedBox.shrink();
}
@@ -329,7 +351,7 @@ class EmailView extends GetWidget<SingleEmailController> {
Widget _buildAttachmentsBody(BuildContext context, List<Attachment> attachments) {
return Container(
color: Colors.white,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 12, top: 10),
padding: const EdgeInsetsDirectional.symmetric(vertical: 12, horizontal: 16),
child: Column(crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildAttachmentsHeader(context, attachments),
@@ -0,0 +1,178 @@
import 'package:collection/collection.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
import 'package:jmap_dart_client/jmap/mail/calendar/properties/attendee/calendar_attendee.dart';
import 'package:jmap_dart_client/jmap/mail/calendar/properties/attendee/calendar_attendee_participation_status.dart';
import 'package:jmap_dart_client/jmap/mail/calendar/properties/event_method.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
extension CalendarEventExtension on CalendarEvent {
Color getColorEventActionBanner(String senderEmailAddress) {
switch(method) {
case EventMethod.request:
case EventMethod.add:
return AppColor.colorInvitedEventActionText;
case EventMethod.refresh:
case EventMethod.counter:
return AppColor.colorUpdatedEventActionText;
case EventMethod.cancel:
case EventMethod.declineCounter:
return AppColor.colorCanceledEventActionText;
case EventMethod.reply:
final matchedAttendee = findAttendeeHasUpdatedStatus(senderEmailAddress);
if (matchedAttendee != null) {
return getAttendeeMessageTextColor(matchedAttendee.participationStatus);
} else {
return Colors.transparent;
}
default:
return Colors.transparent;
}
}
Color getColorEventActionText(String senderEmailAddress) {
switch(method) {
case EventMethod.request:
case EventMethod.add:
return AppColor.colorInvitedEventActionText;
case EventMethod.refresh:
case EventMethod.counter:
return AppColor.colorUpdatedEventActionText;
case EventMethod.cancel:
case EventMethod.declineCounter:
return AppColor.colorCanceledEventActionText;
case EventMethod.reply:
final matchedAttendee = findAttendeeHasUpdatedStatus(senderEmailAddress);
if (matchedAttendee != null) {
return getAttendeeMessageTextColor(matchedAttendee.participationStatus);
} else {
return Colors.transparent;
}
default:
return Colors.transparent;
}
}
String getIconEventAction(ImagePaths imagePaths) {
switch(method) {
case EventMethod.request:
case EventMethod.add:
return imagePaths.icEventInvited;
case EventMethod.refresh:
return imagePaths.icEventUpdated;
case EventMethod.cancel:
return imagePaths.icEventCanceled;
default:
return '';
}
}
String getTitleEventAction(BuildContext context, String senderEmailAddress) {
switch(method) {
case EventMethod.request:
case EventMethod.add:
return AppLocalizations.of(context).messageEventActionBannerOrganizerInvited;
case EventMethod.refresh:
return AppLocalizations.of(context).messageEventActionBannerOrganizerUpdated;
case EventMethod.cancel:
return AppLocalizations.of(context).messageEventActionBannerOrganizerCanceled;
case EventMethod.reply:
final matchedAttendee = findAttendeeHasUpdatedStatus(senderEmailAddress);
if (matchedAttendee != null) {
return getAttendeeMessageStatus(context, matchedAttendee.participationStatus);
} else {
return '';
}
case EventMethod.counter:
return AppLocalizations.of(context).messageEventActionBannerAttendeeCounter;
case EventMethod.declineCounter:
return AppLocalizations.of(context).messageEventActionBannerAttendeeCounterDeclined;
default:
return '';
}
}
String getSubTitleEventAction(BuildContext context) {
switch(method) {
case EventMethod.refresh:
return AppLocalizations.of(context).subMessageEventActionBannerUpdated;
case EventMethod.cancel:
return AppLocalizations.of(context).subMessageEventActionBannerCanceled;
default:
return '';
}
}
String getUserNameEventAction({
required BuildContext context,
required ImagePaths imagePaths,
required String senderEmailAddress
}) {
switch(method) {
case EventMethod.request:
case EventMethod.add:
case EventMethod.refresh:
case EventMethod.cancel:
case EventMethod.declineCounter:
return getOrganizerName(context);
case EventMethod.reply:
case EventMethod.counter:
return getAttendeeName(context, senderEmailAddress);
default:
return '';
}
}
String getOrganizerName(BuildContext context) => organizer?.name ?? AppLocalizations.of(context).you;
String getAttendeeName(BuildContext context, String senderEmailAddress) {
final matchedAttendee = findAttendeeHasUpdatedStatus(senderEmailAddress);
if (matchedAttendee != null) {
return matchedAttendee.name?.name ?? AppLocalizations.of(context).anAttendee;
} else {
return AppLocalizations.of(context).anAttendee;
}
}
CalendarAttendee? findAttendeeHasUpdatedStatus(String senderEmailAddress) {
if (participants?.isNotEmpty == true) {
final listMatchedAttendee = participants
!.where((attendee) => attendee.mailto?.mailAddress.value == senderEmailAddress)
.whereNotNull();
log('CalendarEventExtension::findAttendeeHasUpdatedStatus:listMatchedAttendee: $listMatchedAttendee');
if (listMatchedAttendee.isNotEmpty) {
return listMatchedAttendee.first;
}
}
return null;
}
String getAttendeeMessageStatus(BuildContext context, CalendarAttendeeParticipationStatus? status) {
if (status == CalendarAttendeeParticipationStatus('ACCEPTED')) {
return AppLocalizations.of(context).messageEventActionBannerAttendeeAccepted;
} else if (status == CalendarAttendeeParticipationStatus('TENTATIVE')) {
return AppLocalizations.of(context).messageEventActionBannerAttendeeTentative;
} else if (status == CalendarAttendeeParticipationStatus('DECLINED')) {
return AppLocalizations.of(context).messageEventActionBannerAttendeeDeclined;
} else {
return '';
}
}
Color getAttendeeMessageTextColor(CalendarAttendeeParticipationStatus? status) {
if (status == CalendarAttendeeParticipationStatus('ACCEPTED')) {
return AppColor.colorUpdatedEventActionText;
} else if (status == CalendarAttendeeParticipationStatus('TENTATIVE')) {
return AppColor.colorMaybeEventActionText;
} else if (status == CalendarAttendeeParticipationStatus('DECLINED')) {
return AppColor.colorCanceledEventActionText;
} else {
return Colors.transparent;
}
}
}
@@ -0,0 +1,7 @@
class CalendarEventActionBannerStyles {
static const double borderRadius = 16;
static const double contentPadding = 16;
static const double viewHorizontalMargin = 16;
static const double viewVerticalMargin = 12;
}
@@ -0,0 +1,5 @@
class EmailViewStyles {
static const double mobileContentHorizontalMargin = 16;
static const double mobileContentVerticalMargin = 12;
}
@@ -0,0 +1,104 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/calendar_event_action_banner_styles.dart';
class CalendarEventActionBannerWidget extends StatelessWidget {
final CalendarEvent calendarEvent;
final Set<EmailAddress>? listFromEmailAddress;
const CalendarEventActionBannerWidget({
super.key,
required this.calendarEvent,
required this.listFromEmailAddress,
});
@override
Widget build(BuildContext context) {
final imagePaths = Get.find<ImagePaths>();
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(CalendarEventActionBannerStyles.borderRadius)),
color: calendarEvent.getColorEventActionBanner(_getSenderEmailAddress()).withOpacity(0.12)
),
padding: const EdgeInsets.all(CalendarEventActionBannerStyles.contentPadding),
margin: const EdgeInsets.symmetric(
vertical: CalendarEventActionBannerStyles.viewVerticalMargin,
horizontal: CalendarEventActionBannerStyles.viewHorizontalMargin,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (calendarEvent.getIconEventAction(imagePaths).isNotEmpty)
Padding(
padding: const EdgeInsetsDirectional.only(end: 8),
child: SvgPicture.asset(
calendarEvent.getIconEventAction(imagePaths),
width: 24,
height: 24,
fit: BoxFit.fill,
),
),
Expanded(child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
RichText(
text: TextSpan(
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
color: calendarEvent.getColorEventActionText(_getSenderEmailAddress())
),
children: [
TextSpan(
text: calendarEvent.getUserNameEventAction(
context: context,
imagePaths: imagePaths,
senderEmailAddress: _getSenderEmailAddress()
),
style: TextStyle(
color: calendarEvent.getColorEventActionText(_getSenderEmailAddress()),
fontSize: 20,
fontWeight: FontWeight.w700
),
),
TextSpan(text: calendarEvent.getTitleEventAction(context, _getSenderEmailAddress()))
]
)
),
if (calendarEvent.getSubTitleEventAction(context).isNotEmpty)
Text(
calendarEvent.getSubTitleEventAction(context),
style: const TextStyle(
color: AppColor.colorSubTitleEventActionText,
fontSize: 16,
fontWeight: FontWeight.w400
),
)
]
))
]
),
);
}
String _getSenderEmailAddress() {
if (listFromEmailAddress?.isNotEmpty == true) {
final senderEmailAddress = listFromEmailAddress!.first.emailAddress;
log('CalendarEventActionBannerWidget::getSenderEmailAddress: $senderEmailAddress');
return senderEmailAddress;
} else {
return '';
}
}
}
+73 -1
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2023-07-12T18:53:48.611802",
"@@last_modified": "2023-07-24T18:44:17.206723",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -2975,5 +2975,77 @@
"placeholders": {
"maxSize": {}
}
},
"messageEventActionBannerOrganizerInvited": " has invited you in to a meeting",
"@messageEventActionBannerOrganizerInvited": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageEventActionBannerOrganizerUpdated": " has updated a meeting",
"@messageEventActionBannerOrganizerUpdated": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageEventActionBannerOrganizerCanceled": " has canceled a meeting",
"@messageEventActionBannerOrganizerCanceled": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"subMessageEventActionBannerUpdated": "\"The time has been updated to better suit all of you\"",
"@subMessageEventActionBannerUpdated": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"subMessageEventActionBannerCanceled": "\"We are canceling the event due to bad weather.\"",
"@subMessageEventActionBannerCanceled": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"anAttendee": "An attendee",
"@anAttendee": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"you": "You",
"@you": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageEventActionBannerAttendeeAccepted": " has accepted this invitation",
"@messageEventActionBannerAttendeeAccepted": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageEventActionBannerAttendeeTentative": " has replied \"Maybe\" to this invitation",
"@messageEventActionBannerAttendeeTentative": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageEventActionBannerAttendeeDeclined": " has declined this invitation",
"@messageEventActionBannerAttendeeDeclined": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageEventActionBannerAttendeeCounter": " has proposed changes to the event",
"@messageEventActionBannerAttendeeCounter": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageEventActionBannerAttendeeCounterDeclined": "Your counter proposal was declined",
"@messageEventActionBannerAttendeeCounterDeclined": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
}
}
@@ -3071,4 +3071,76 @@ class AppLocalizations {
name: 'pleaseChooseAnImageSizeCorrectly',
args: [maxSize]);
}
String get messageEventActionBannerOrganizerInvited {
return Intl.message(
' has invited you in to a meeting',
name: 'messageEventActionBannerOrganizerInvited');
}
String get messageEventActionBannerOrganizerUpdated {
return Intl.message(
' has updated a meeting',
name: 'messageEventActionBannerOrganizerUpdated');
}
String get messageEventActionBannerOrganizerCanceled {
return Intl.message(
' has canceled a meeting',
name: 'messageEventActionBannerOrganizerCanceled');
}
String get subMessageEventActionBannerUpdated {
return Intl.message(
'"The time has been updated to better suit all of you"',
name: 'subMessageEventActionBannerUpdated');
}
String get subMessageEventActionBannerCanceled {
return Intl.message(
'"We are canceling the event due to bad weather."',
name: 'subMessageEventActionBannerCanceled');
}
String get anAttendee {
return Intl.message(
'An attendee',
name: 'anAttendee');
}
String get you {
return Intl.message(
'You',
name: 'you');
}
String get messageEventActionBannerAttendeeAccepted {
return Intl.message(
' has accepted this invitation',
name: 'messageEventActionBannerAttendeeAccepted');
}
String get messageEventActionBannerAttendeeTentative {
return Intl.message(
' has replied "Maybe" to this invitation',
name: 'messageEventActionBannerAttendeeTentative');
}
String get messageEventActionBannerAttendeeDeclined {
return Intl.message(
' has declined this invitation',
name: 'messageEventActionBannerAttendeeDeclined');
}
String get messageEventActionBannerAttendeeCounter {
return Intl.message(
' has proposed changes to the event',
name: 'messageEventActionBannerAttendeeCounter');
}
String get messageEventActionBannerAttendeeCounterDeclined {
return Intl.message(
'Your counter proposal was declined',
name: 'messageEventActionBannerAttendeeCounterDeclined');
}
}
+2
View File
@@ -50,6 +50,8 @@ class Attachment with EquatableMixin {
}
}
bool get isCalendarEvent => type?.subtype == 'ics' || type?.subtype == 'calendar';
@override
List<Object?> get props => [partId, blobId, size, name, type, cid, disposition];
}