diff --git a/CHANGELOG.md b/CHANGELOG.md
index 949e144abd..41c2579039 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+## [0.8.9] - 2023-07-31
+### Added
+- Translate Russian and French
+- Apply new view calendar event
+
+### Fixed
+- \#2052 Fix TeamMailbox email address alignment is incorrect
+- If no refreshToken return, maybe the old refreshToken still available
+
## [0.8.8] - 2023-07-20
### Fixed
- \#2046 App crashes when login account information is incorrect on web
@@ -199,6 +208,7 @@
- \#1512 remove plain text input for signature
- \#1469 remove animation when navigating screen
+[0.8.9]: https://github.com/linagora/tmail-flutter/releases/tag/v0.8.9
[0.8.8]: https://github.com/linagora/tmail-flutter/releases/tag/v0.8.8
[0.8.7]: https://github.com/linagora/tmail-flutter/releases/tag/v0.8.7
[0.8.6]: https://github.com/linagora/tmail-flutter/releases/tag/v0.8.6
diff --git a/assets/images/ic_calendar_event.svg b/assets/images/ic_calendar_event.svg
new file mode 100644
index 0000000000..be965e4fdb
--- /dev/null
+++ b/assets/images/ic_calendar_event.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/ic_event_canceled.svg b/assets/images/ic_event_canceled.svg
new file mode 100644
index 0000000000..7b011b5a95
--- /dev/null
+++ b/assets/images/ic_event_canceled.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/ic_event_invited.svg b/assets/images/ic_event_invited.svg
new file mode 100644
index 0000000000..4497cc4d99
--- /dev/null
+++ b/assets/images/ic_event_invited.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/ic_event_updated.svg b/assets/images/ic_event_updated.svg
new file mode 100644
index 0000000000..c339cd6a42
--- /dev/null
+++ b/assets/images/ic_event_updated.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/ic_format_quote.svg b/assets/images/ic_format_quote.svg
new file mode 100644
index 0000000000..6806d928a4
--- /dev/null
+++ b/assets/images/ic_format_quote.svg
@@ -0,0 +1,5 @@
+
diff --git a/contact/pubspec.lock b/contact/pubspec.lock
index f7d16f0fb9..9e14d21c45 100644
--- a/contact/pubspec.lock
+++ b/contact/pubspec.lock
@@ -492,7 +492,7 @@ packages:
description:
path: "."
ref: master
- resolved-ref: "973a57d1ffbc5237d63ea960b46e8921c6ac6a66"
+ resolved-ref: "63d47945f676adca3bf48b3940b21b77546e309e"
url: "https://github.com/linagora/jmap-dart-client.git"
source: git
version: "0.0.1"
diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart
index 7bb309437f..34b2157184 100644
--- a/core/lib/presentation/extensions/color_extension.dart
+++ b/core/lib/presentation/extensions/color_extension.dart
@@ -184,6 +184,19 @@ extension AppColor on Color {
static const colorBackgroundDeliveringState = Color(0xFFF2F3F5);
static const colorNetworkConnectionBannerBackground = Color(0x99EBEDF0);
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 colorCalendarEventInformationBackground = Color(0x0A000000);
+ static const colorCalendarEventInformationStroke = Color(0x1F000000);
+ static const colorShadowCalendarDateIcon = Color(0x26000000);
+ static const colorOrganizerMailto = Color(0xFFB3B3B3);
+ static const colorMailto = Color(0xFFB3B3B3);
+ static const colorEventDescriptionBackground = Color(0x05000000);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index 39bc83c9e6..cea00d9f59 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -194,6 +194,11 @@ class ImagePaths {
String get icArrowLeft => _getImagePath('ic_arrow_left.svg');
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 get icFormatQuote => _getImagePath('ic_format_quote.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/fcm/pubspec.lock b/fcm/pubspec.lock
index 1273272758..22fedc3afe 100644
--- a/fcm/pubspec.lock
+++ b/fcm/pubspec.lock
@@ -296,7 +296,7 @@ packages:
description:
path: "."
ref: master
- resolved-ref: "973a57d1ffbc5237d63ea960b46e8921c6ac6a66"
+ resolved-ref: "63d47945f676adca3bf48b3940b21b77546e309e"
url: "https://github.com/linagora/jmap-dart-client.git"
source: git
version: "0.0.1"
diff --git a/forward/pubspec.lock b/forward/pubspec.lock
index 1273272758..22fedc3afe 100644
--- a/forward/pubspec.lock
+++ b/forward/pubspec.lock
@@ -296,7 +296,7 @@ packages:
description:
path: "."
ref: master
- resolved-ref: "973a57d1ffbc5237d63ea960b46e8921c6ac6a66"
+ resolved-ref: "63d47945f676adca3bf48b3940b21b77546e309e"
url: "https://github.com/linagora/jmap-dart-client.git"
source: git
version: "0.0.1"
diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart
index 6ca202abc2..b3555b5b1d 100644
--- a/lib/features/base/base_controller.dart
+++ b/lib/features/base/base_controller.dart
@@ -22,7 +22,7 @@ import 'package:rule_filter/rule_filter/capability_rule_filter.dart';
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.dart';
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
-import 'package:tmail_ui_user/features/email/presentation/mdn_interactor_bindings.dart';
+import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interactor_bindings.dart';
import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
diff --git a/lib/features/base/reloadable/reloadable_controller.dart b/lib/features/base/reloadable/reloadable_controller.dart
index 35684e353b..e72d65fd79 100644
--- a/lib/features/base/reloadable/reloadable_controller.dart
+++ b/lib/features/base/reloadable/reloadable_controller.dart
@@ -140,7 +140,7 @@ abstract class ReloadableController extends BaseController {
}
void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) {
- final apiUrl = session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors.jmapUrl);;
+ final apiUrl = session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors.jmapUrl);
log('ReloadableController::updateAuthenticationAccount():apiUrl: $apiUrl');
if (apiUrl.isNotEmpty) {
consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName));
diff --git a/lib/features/base/styles/cupertino_loading_widget_styles.dart b/lib/features/base/styles/cupertino_loading_widget_styles.dart
new file mode 100644
index 0000000000..3000ad3694
--- /dev/null
+++ b/lib/features/base/styles/cupertino_loading_widget_styles.dart
@@ -0,0 +1,7 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:flutter/material.dart';
+
+class CupertinoLoadingWidgetStyles {
+ static const Color progressColor = AppColor.colorLoading;
+ static const double size = 24;
+}
\ No newline at end of file
diff --git a/lib/features/base/styles/hyper_link_widget_styles.dart b/lib/features/base/styles/hyper_link_widget_styles.dart
new file mode 100644
index 0000000000..c8668cb476
--- /dev/null
+++ b/lib/features/base/styles/hyper_link_widget_styles.dart
@@ -0,0 +1,7 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:flutter/material.dart';
+
+class HyperLinkWidgetStyles {
+ static const Color textColor = AppColor.primaryColor;
+ static const double textSize = 16;
+}
\ No newline at end of file
diff --git a/lib/features/base/widget/cupertino_loading_widget.dart b/lib/features/base/widget/cupertino_loading_widget.dart
new file mode 100644
index 0000000000..0c8d3e72a9
--- /dev/null
+++ b/lib/features/base/widget/cupertino_loading_widget.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/cupertino.dart';
+import 'package:tmail_ui_user/features/base/styles/cupertino_loading_widget_styles.dart';
+
+class CupertinoLoadingWidget extends StatelessWidget {
+ const CupertinoLoadingWidget({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return const Center(
+ child: SizedBox(
+ width: CupertinoLoadingWidgetStyles.size,
+ height: CupertinoLoadingWidgetStyles.size,
+ child: CupertinoActivityIndicator(color: CupertinoLoadingWidgetStyles.progressColor)
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/features/base/widget/hyper_link_widget.dart b/lib/features/base/widget/hyper_link_widget.dart
new file mode 100644
index 0000000000..f0249179ea
--- /dev/null
+++ b/lib/features/base/widget/hyper_link_widget.dart
@@ -0,0 +1,33 @@
+import 'package:flutter/gestures.dart';
+import 'package:flutter/widgets.dart';
+import 'package:tmail_ui_user/features/base/styles/hyper_link_widget_styles.dart';
+import 'package:url_launcher/url_launcher_string.dart';
+
+class HyperLinkWidget extends StatelessWidget {
+
+ final String urlString;
+
+ const HyperLinkWidget({Key? key, required this.urlString}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return RichText(
+ text: TextSpan(
+ text: urlString,
+ style: const TextStyle(
+ color: HyperLinkWidgetStyles.textColor,
+ fontSize: HyperLinkWidgetStyles.textSize,
+ decoration: TextDecoration.underline
+ ),
+ recognizer: TapGestureRecognizer()..onTap = () async {
+ if (await canLaunchUrlString(urlString)) {
+ launchUrlString(
+ urlString,
+ mode: LaunchMode.externalApplication
+ );
+ }
+ }
+ )
+ );
+ }
+}
diff --git a/lib/features/email/data/datasource/calendar_event_datasource.dart b/lib/features/email/data/datasource/calendar_event_datasource.dart
new file mode 100644
index 0000000000..3f34688d1b
--- /dev/null
+++ b/lib/features/email/data/datasource/calendar_event_datasource.dart
@@ -0,0 +1,8 @@
+
+import 'package:jmap_dart_client/jmap/account_id.dart';
+import 'package:jmap_dart_client/jmap/core/id.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
+
+abstract class CalendarEventDataSource {
+ Future> parse(AccountId accountId, Set blobIds);
+}
\ No newline at end of file
diff --git a/lib/features/email/data/datasource_impl/calendar_event_datasource_impl.dart b/lib/features/email/data/datasource_impl/calendar_event_datasource_impl.dart
new file mode 100644
index 0000000000..aabcc5193a
--- /dev/null
+++ b/lib/features/email/data/datasource_impl/calendar_event_datasource_impl.dart
@@ -0,0 +1,22 @@
+
+import 'package:jmap_dart_client/jmap/account_id.dart';
+import 'package:jmap_dart_client/jmap/core/id.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
+import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
+import 'package:tmail_ui_user/features/email/data/network/calendar_event_api.dart';
+import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
+
+class CalendarEventDataSourceImpl extends CalendarEventDataSource {
+
+ final CalendarEventAPI _calendarEventAPI;
+ final ExceptionThrower _exceptionThrower;
+
+ CalendarEventDataSourceImpl(this._calendarEventAPI, this._exceptionThrower);
+
+ @override
+ Future> parse(AccountId accountId, Set blobIds) {
+ return Future.sync(() async {
+ return await _calendarEventAPI.parse(accountId, blobIds);
+ }).catchError(_exceptionThrower.throwException);
+ }
+}
\ No newline at end of file
diff --git a/lib/features/email/data/network/calendar_event_api.dart b/lib/features/email/data/network/calendar_event_api.dart
new file mode 100644
index 0000000000..19ce720465
--- /dev/null
+++ b/lib/features/email/data/network/calendar_event_api.dart
@@ -0,0 +1,41 @@
+import 'dart:async';
+
+import 'package:jmap_dart_client/http/http_client.dart';
+import 'package:jmap_dart_client/jmap/account_id.dart';
+import 'package:jmap_dart_client/jmap/core/id.dart';
+import 'package:jmap_dart_client/jmap/jmap_request.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/parse/calendar_event_parse_method.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/parse/calendar_event_parse_response.dart';
+import 'package:tmail_ui_user/features/email/domain/exceptions/calendar_event_exceptions.dart';
+
+class CalendarEventAPI {
+
+ final HttpClient _httpClient;
+
+ CalendarEventAPI(this._httpClient);
+
+ Future> parse(AccountId accountId, Set blobIds) async {
+ final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
+ final calendarEventParseMethod = CalendarEventParseMethod(accountId, blobIds);
+ final calendarEventParseInvocation = requestBuilder.invocation(calendarEventParseMethod);
+ final response = await (requestBuilder
+ ..usings(calendarEventParseMethod.requiredCapabilities))
+ .build()
+ .execute();
+
+ final calendarEventParseResponse = response.parse(
+ calendarEventParseInvocation.methodCallId,
+ CalendarEventParseResponse.deserialize);
+
+ if (calendarEventParseResponse?.parsed?.isNotEmpty == true) {
+ return calendarEventParseResponse!.parsed!.values.toList();
+ } else if (calendarEventParseResponse?.notParsable?.isNotEmpty == true) {
+ throw NotParsableCalendarEventException();
+ } else if (calendarEventParseResponse?.notFound?.isNotEmpty == true) {
+ throw NotFoundCalendarEventException();
+ } else {
+ throw NotParsableCalendarEventException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/features/email/data/repository/calendar_event_repository_impl.dart b/lib/features/email/data/repository/calendar_event_repository_impl.dart
new file mode 100644
index 0000000000..9d94ecc749
--- /dev/null
+++ b/lib/features/email/data/repository/calendar_event_repository_impl.dart
@@ -0,0 +1,18 @@
+
+import 'package:jmap_dart_client/jmap/account_id.dart';
+import 'package:jmap_dart_client/jmap/core/id.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
+import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
+import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart';
+
+class CalendarEventRepositoryImpl extends CalendarEventRepository {
+
+ final CalendarEventDataSource _calendarEventDataSource;
+
+ CalendarEventRepositoryImpl(this._calendarEventDataSource);
+
+ @override
+ Future> parse(AccountId accountId, Set blobIds) {
+ return _calendarEventDataSource.parse(accountId, blobIds);
+ }
+}
\ No newline at end of file
diff --git a/lib/features/email/domain/exceptions/calendar_event_exceptions.dart b/lib/features/email/domain/exceptions/calendar_event_exceptions.dart
new file mode 100644
index 0000000000..1129d36f55
--- /dev/null
+++ b/lib/features/email/domain/exceptions/calendar_event_exceptions.dart
@@ -0,0 +1,4 @@
+
+class NotFoundCalendarEventException implements Exception {}
+
+class NotParsableCalendarEventException implements Exception {}
\ No newline at end of file
diff --git a/lib/features/email/domain/extensions/list_attachments_extension.dart b/lib/features/email/domain/extensions/list_attachments_extension.dart
index 1cbfc601a3..bd9a0fc08a 100644
--- a/lib/features/email/domain/extensions/list_attachments_extension.dart
+++ b/lib/features/email/domain/extensions/list_attachments_extension.dart
@@ -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 {
List toHiveCache() => map((attachment) => attachment.toHiveCache()).toList();
+
+ Set get calendarAttachments => where((attachment) => attachment.isCalendarEvent).toSet();
+
+ Set get calendarEventBlobIds => calendarAttachments
+ .map((attachment) => attachment.blobId)
+ .whereNotNull()
+ .toSet();
}
\ No newline at end of file
diff --git a/lib/features/email/domain/repository/calendar_event_repository.dart b/lib/features/email/domain/repository/calendar_event_repository.dart
new file mode 100644
index 0000000000..809b12c7e3
--- /dev/null
+++ b/lib/features/email/domain/repository/calendar_event_repository.dart
@@ -0,0 +1,8 @@
+
+import 'package:jmap_dart_client/jmap/account_id.dart';
+import 'package:jmap_dart_client/jmap/core/id.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
+
+abstract class CalendarEventRepository {
+ Future> parse(AccountId accountId, Set blobIds);
+}
\ No newline at end of file
diff --git a/lib/features/email/domain/state/parse_calendar_event_state.dart b/lib/features/email/domain/state/parse_calendar_event_state.dart
new file mode 100644
index 0000000000..4d1be20264
--- /dev/null
+++ b/lib/features/email/domain/state/parse_calendar_event_state.dart
@@ -0,0 +1,19 @@
+import 'package:core/presentation/state/failure.dart';
+import 'package:core/presentation/state/success.dart';
+import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
+
+class ParseCalendarEventLoading extends LoadingState {}
+
+class ParseCalendarEventSuccess extends UIState {
+
+ final List calendarEventList;
+
+ ParseCalendarEventSuccess(this.calendarEventList);
+
+ @override
+ List