Add presentation layer for mailbox, mail, message

This commit is contained in:
dab246
2021-07-29 12:49:17 +07:00
committed by Dat H. Pham
parent 5d3d2f4c1e
commit 7a99fd2c59
31 changed files with 626 additions and 105 deletions
@@ -41,6 +41,7 @@ class ButtonBuilder {
Widget build() {
return Container(
key: _key,
alignment: Alignment.center,
color: Colors.transparent,
child: MediaQuery(
+8
View File
@@ -13,6 +13,14 @@ abstract class BaseController extends GetxController {
);
}
void getState(Future<Either<Failure, Success>> newStateStream) async {
final state = await newStateStream;
state.fold(
(failure) => onError(failure),
(success) => onData(state)
);
}
void onData(Either<Failure, Success> newState) {
viewState.value = newState;
}
@@ -34,6 +34,6 @@ class HomeController extends GetxController {
credentialViewState.userName.userName,
credentialViewState.password.value,
);
Get.offNamed(AppRoutes.MAILBOX);
Get.offNamed(AppRoutes.MAILBOX_DASHBOARD);
}
}
@@ -60,7 +60,7 @@ class LoginController extends GetxController {
loginState.value = LoginState(Right(success));
_dynamicUrlInterceptors.changeBaseUrl(_urlText);
_authorizationInterceptors.changeAuthorization(_userNameText, _passwordText);
Get.offNamed(AppRoutes.MAILBOX);
Get.offNamed(AppRoutes.MAILBOX_DASHBOARD);
}
void _loginFailureAction(AuthenticationUserFailure failure) {
@@ -0,0 +1,10 @@
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mail/presentation/mail_controller.dart';
class MailBindings extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => MailController(Get.find<ResponsiveUtils>()));
}
}
@@ -0,0 +1,29 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
class MailController extends BaseController {
final ResponsiveUtils responsiveUtils;
MailController(this.responsiveUtils);
@override
void onReady() {
super.onReady();
}
void goToMailboxListMail(BuildContext context) {
Get.back();
}
@override
void onDone() {
}
@override
void onError(error) {
}
}
@@ -0,0 +1,72 @@
import 'package:core/core.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mail/presentation/mail_controller.dart';
import 'package:tmail_ui_user/features/mail/presentation/widgets/app_bar_mail_widget_builder.dart';
import 'package:tmail_ui_user/features/mail/presentation/widgets/bottom_bar_mail_widget_builder.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class MailView extends GetWidget<MailController> {
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: AppColor.primaryLightColor,
body: SafeArea(
right: false,
left: false,
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildAppBar(context),
Expanded(child: _buildListMail(context)),
_buildBottomBar(context),
])
)
),
);
}
Widget _buildAppBar(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 16, top: 16.0, right: 16, bottom: 16),
child: AppBarMailWidgetBuilder(context, imagePaths, responsiveUtils)
.onBackActionClick(() => controller.goToMailboxListMail(context))
.build());
}
Widget _buildBottomBar(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 16, top: 16.0, right: 16, bottom: 16),
child: BottomBarMailWidgetBuilder(context, imagePaths, responsiveUtils)
.build());
}
Widget _buildListMail(BuildContext context) {
return Container(
margin: EdgeInsets.zero,
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
padding: EdgeInsets.all(20),
color: AppColor.bgMessenger,
child: Obx(() => Text(
mailboxDashBoardController.mailCurrent.value.id != 'empty'
? '${mailboxDashBoardController.mailCurrent.value.message}'
: AppLocalizations.of(context).no_mail_selected,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold)
))
);
}
}
@@ -5,20 +5,20 @@ import 'package:flutter/widgets.dart';
typedef OnBackActionClick = void Function();
class AppBarMessengerWidgetBuilder {
class AppBarMailWidgetBuilder {
OnBackActionClick? _onBackActionClick;
final BuildContext _context;
final ImagePaths _imagePaths;
final ResponsiveUtils _responsiveUtils;
AppBarMessengerWidgetBuilder(
AppBarMailWidgetBuilder(
this._context,
this._imagePaths,
this._responsiveUtils,
);
AppBarMessengerWidgetBuilder onBackActionClick(
AppBarMailWidgetBuilder onBackActionClick(
OnBackActionClick onBackActionClick) {
_onBackActionClick = onBackActionClick;
return this;
@@ -3,13 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class BottomBarMessengerWidgetBuilder {
class BottomBarMailWidgetBuilder {
final BuildContext _context;
final ImagePaths _imagePaths;
final ResponsiveUtils _responsiveUtils;
BottomBarMessengerWidgetBuilder(
BottomBarMailWidgetBuilder(
this._context,
this._imagePaths,
this._responsiveUtils,
@@ -1,3 +1,4 @@
class MailboxConstants {
static const MAILBOX_LEVEL_SEPARATOR = "/";
static const ROLE_DEFAULT = "inbox";
}
@@ -1,3 +1,4 @@
import 'package:core/core.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart';
@@ -17,6 +18,9 @@ class MailboxBindings extends Bindings {
Get.lazyPut<MailboxRepository>(() => Get.find<MailboxRepositoryImpl>());
Get.lazyPut(() => GetAllMailboxInteractor(Get.find<MailboxRepository>()));
Get.lazyPut(() => TreeBuilder());
Get.lazyPut(() => MailboxController(Get.find<GetAllMailboxInteractor>(), Get.find<TreeBuilder>()));
Get.lazyPut(() => MailboxController(
Get.find<GetAllMailboxInteractor>(),
Get.find<TreeBuilder>(),
Get.find<ResponsiveUtils>()));
}
}
@@ -1,26 +1,37 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.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/mail/mailbox/mailbox.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:model/mailbox/select_mode.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/mailbox/domain/constants/mailbox_constants.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
class MailboxController extends BaseController {
final GetAllMailboxInteractor _getAllMailboxInteractor;
final TreeBuilder _treeBuilder;
final ResponsiveUtils responsiveUtils;
MailboxController(this._getAllMailboxInteractor, this._treeBuilder);
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
MailboxController(this._getAllMailboxInteractor, this._treeBuilder, this.responsiveUtils);
final folderMailboxTree = MailboxTree(MailboxNode.root()).obs;
final selectedMailbox = PresentationMailbox.createMailboxEmpty().obs;
@override
void onReady() {
@@ -29,17 +40,19 @@ class MailboxController extends BaseController {
}
void getAllMailboxAction() async {
final AccountId accountId = AccountId(Id('93c56f4408cff66f0a929aea8e3940e753c3275e5622582ae3010e7277b7696c'));
final AccountId accountId = AccountId(Id('3ce33c876a726662c627746eb9537a1d13c2338193ef27bd051a3ce5c0fe5b12'));
consumeState(_getAllMailboxInteractor.execute(accountId));
}
@override
void onData(Either<Failure, Success> newState) {
super.onData(newState);
newState
.map((success) => success is GetAllMailboxSuccess
? _buildTree(success.folderMailboxList)
: null);
newState.map((success) {
if (success is GetAllMailboxSuccess) {
_buildTree(success.folderMailboxList);
_setDefaultSelected(success.defaultMailboxList);
}
});
}
@override
@@ -54,6 +67,40 @@ class MailboxController extends BaseController {
folderMailboxTree.value = await _treeBuilder.generateMailboxTree(folderMailboxList);
}
void _setDefaultSelected(List<PresentationMailbox> defaultMailboxList) {
try {
final mailboxDefault = defaultMailboxList
.firstWhere((presentationMailbox) => presentationMailbox.role == Role(MailboxConstants.ROLE_DEFAULT));
selectedMailbox.value = mailboxDefault;
mailboxDashBoardController.mailboxCurrent.value = mailboxDefault;
} catch (e) {}
}
SelectMode getSelectMode(PresentationMailbox presentationMailbox) {
return presentationMailbox.id == selectedMailbox.value.id
? SelectMode.ACTIVE
: SelectMode.INACTIVE;
}
void selectMailbox(
BuildContext context,
PresentationMailbox presentationMailboxSelected,
{
required GlobalKey<ScaffoldState> keyWidgetMailboxDashBoard
}
) {
selectedMailbox.value = presentationMailboxSelected;
mailboxDashBoardController.mailboxCurrent.value = presentationMailboxSelected;
if (responsiveUtils.isMobile(context)) {
goToThread(keyWidgetMailboxDashBoard: keyWidgetMailboxDashBoard);
}
}
void goToThread({required GlobalKey<ScaffoldState> keyWidgetMailboxDashBoard}) {
keyWidgetMailboxDashBoard.currentState?.openEndDrawer();
}
void closeMailboxScreen() {
Get.offAllNamed(AppRoutes.LOGIN);
}
@@ -17,65 +17,101 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_tile
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/search_form_widget_builder.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/storage_widget_builder.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget_builder.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class MailboxView extends GetWidget<MailboxController> {
final mailboxController = Get.find<MailboxController>();
final imagePaths = Get.find<ImagePaths>();
final responsiveUtils = Get.find<ResponsiveUtils>();
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: AppColor.primaryLightColor,
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCloseScreenButton(),
_buildUserInformationWidget(context),
_buildSearchFormWidget(context),
_buildLoadingView(),
Expanded(child: RefreshIndicator(
return Drawer(
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: AppColor.primaryLightColor,
body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: SafeArea(
right: false,
left: false,
child: RefreshIndicator(
color: AppColor.primaryColor,
onRefresh: () async => mailboxController.getAllMailboxAction(),
child: _buildListMailbox(context)))
])),
bottomNavigationBar: _buildStorageWidget(context),
onRefresh: () async => controller.getAllMailboxAction(),
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeaderMailbox(context),
_buildSearchFormWidget(context),
_buildLoadingView(),
_buildListMailbox(context)
]
)
)
)
)
)
)),
bottomNavigationBar: responsiveUtils.isMobile(context) ? _buildStorageWidget(context) : null
)
);
}
Widget _buildCloseScreenButton() {
Widget _buildHeaderMailbox(BuildContext context) {
return ResponsiveWidget(
tablet: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCloseScreenButton(context),
_buildUserInformationWidget(context)
]),
mobile: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCloseScreenButton(context),
Expanded(child: _buildUserInformationWidget(context))
]),
responsiveUtils: responsiveUtils);
}
Widget _buildCloseScreenButton(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 24, top: 12, right: 16),
padding: EdgeInsets.only(left: 24, top: responsiveUtils.isMobile(context) ? 0 : 12),
child: IconButton(
key: Key('mailbox_close_button'),
onPressed: () => mailboxController.closeMailboxScreen(),
onPressed: () => controller.closeMailboxScreen(),
icon: SvgPicture.asset(imagePaths.icCloseMailbox, width: 30, height: 30, fit: BoxFit.fill)));
}
Widget _buildUserInformationWidget(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 16, top: 8.0, right: 16),
child: UserInformationWidgetBuilder()
.onOpenUserInformationAction(() => {})
child: UserInformationWidgetBuilder(imagePaths)
.build());
}
Widget _buildSearchFormWidget(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 16, bottom: 12, left: 16, right: 16),
child: SearchFormWidgetBuilder(context)
child: SearchFormWidgetBuilder(context, imagePaths)
.onNewSearchQuery((searchQuery) => {})
.build());
}
Widget _buildLoadingView() {
return Obx(() => mailboxController.viewState.value.fold(
return Obx(() => controller.viewState.value.fold(
(failure) => SizedBox.shrink(),
(success) => success == UIState.loading
? Center(child: Padding(
@@ -90,37 +126,43 @@ class MailboxView extends GetWidget<MailboxController> {
Widget _buildListMailbox(BuildContext context) {
return ListView(
key: Key('mailbox_list'),
primary: true,
primary: false,
shrinkWrap: true,
children: [
Obx(() => mailboxController.viewState.value.fold(
Obx(() => controller.viewState.value.fold(
(failure) => SizedBox.shrink(),
(success) => success is GetAllMailboxSuccess
? _buildDefaultMailbox(success.defaultMailboxList)
? _buildDefaultMailbox(context, success.defaultMailboxList)
: SizedBox.shrink())
),
Padding(
padding: EdgeInsets.only(left: 40, top: 20),
padding: EdgeInsets.only(
left: responsiveUtils.isMobile(context) ? 40 : 32,
top: 20),
child: Text(
AppLocalizations.of(context).my_folders,
maxLines: 1,
style: TextStyle(fontSize: 12, color: AppColor.myFolderTitleColor, fontWeight: FontWeight.w500))
),
Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 10),
child: MailboxNewFolderTileBuilder()
padding: EdgeInsets.only(
left: responsiveUtils.isMobile(context) ? 16 : 2,
right: 16,
top: 10),
child: MailboxNewFolderTileBuilder(context, responsiveUtils)
.addIcon(imagePaths.icMailboxNewFolder)
.addName(AppLocalizations.of(context).new_folder)
.build()
),
Obx(() => mailboxController.folderMailboxTree.value.root.childrenItems.isNotEmpty
? _buildFolderMailbox(context, mailboxController.folderMailboxTree.value)
Obx(() => controller.folderMailboxTree.value.root.childrenItems.isNotEmpty
? _buildFolderMailbox(context, controller.folderMailboxTree.value)
: SizedBox.shrink()
),
]
);
}
Widget _buildDefaultMailbox(List<PresentationMailbox> defaultMailbox) {
Widget _buildDefaultMailbox(BuildContext context, List<PresentationMailbox> defaultMailbox) {
return ListView.builder(
padding: EdgeInsets.only(top: 16, left: 16, right: 16),
key: Key('default_mailbox_list'),
@@ -128,17 +170,25 @@ class MailboxView extends GetWidget<MailboxController> {
shrinkWrap: true,
primary: false,
itemBuilder: (context, index) =>
MailboxTileBuilder(defaultMailbox[index])
.build());
Obx(() => MailboxTileBuilder(
context,
imagePaths,
responsiveUtils,
defaultMailbox[index],
controller.getSelectMode(defaultMailbox[index]))
.onOpenMailboxAction(() => controller.selectMailbox(context, defaultMailbox[index], keyWidgetMailboxDashBoard: mailboxDashBoardController.scaffoldKey))
.build()));
}
Widget _buildFolderMailbox(BuildContext context, MailboxTree mailboxTree) {
return Padding(
padding: EdgeInsets.only(left: 4, right: 16),
child: TreeView(
startExpanded: false,
key: Key('folder_mailbox_list'),
children: _buildListChildTileWidget(context, mailboxTree.root.childrenItems))
return Transform(
transform: Matrix4.translationValues(responsiveUtils.isMobile(context) ? 0.0 : -15.0, 0.0, 0.0),
child: Padding(
padding: EdgeInsets.only(left: 4, right: 16),
child: TreeView(
startExpanded: false,
key: Key('folder_mailbox_list'),
children: _buildListChildTileWidget(context, mailboxTree.root.childrenItems)))
);
}
@@ -149,11 +199,11 @@ class MailboxView extends GetWidget<MailboxController> {
padding: EdgeInsets.only(left: 16),
child: TreeViewChild(
key: Key('children_tree_mailbox_child'),
parent: MailBoxFolderTileBuilder(mailboxNode).build(context),
parent: MailBoxFolderTileBuilder(context, imagePaths, responsiveUtils, mailboxNode).build(context),
children: _buildListChildTileWidget(context, mailboxNode.childrenItems)))
: Padding(
padding: EdgeInsets.only(left: 16),
child: MailBoxFolderTileBuilder(mailboxNode).build(context)))
child: MailBoxFolderTileBuilder(context, imagePaths, responsiveUtils, mailboxNode).build(context)))
.toList();
}
@@ -0,0 +1,15 @@
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart';
class MailboxDashBoardBindings extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => MailboxDashBoardController());
MailboxBindings().dependencies();
ThreadBindings().dependencies();
}
}
@@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
class MailboxDashBoardController extends BaseController {
final scaffoldKey = GlobalKey<ScaffoldState>();
final mailboxCurrent = PresentationMailbox.createMailboxEmpty().obs;
final mailCurrent = PresentationThread.createThreadEmpty().obs;
MailboxDashBoardController();
@override
void onReady() {
super.onReady();
scaffoldKey.currentState?.openDrawer();
}
@override
void onDone() {
}
@override
void onError(error) {
}
}
@@ -0,0 +1,38 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mail/presentation/mail_view.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> {
final responsiveUtils = Get.find<ResponsiveUtils>();
@override
Widget build(BuildContext context) {
return Scaffold(
key: controller.scaffoldKey,
drawer: responsiveUtils.isMobile(context)
? Container(
child: MailboxView(),
width: responsiveUtils.getSizeWidthScreen(context))
: null,
drawerEnableOpenDragGesture: responsiveUtils.isMobile(context),
body: SafeArea(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!responsiveUtils.isMobile(context)) Expanded(child: MailboxView()),
Expanded(
flex: responsiveUtils.isDesktop(context) ? 1 : 3,
child: ThreadView(),
),
if (responsiveUtils.isDesktop(context)) Expanded(flex: 2, child: MailView()),
],
),
),
);
}
}
@@ -0,0 +1,10 @@
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart';
class ThreadBindings extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => ThreadController(Get.find<ResponsiveUtils>()));
}
}
@@ -0,0 +1,59 @@
import 'package:core/core.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
class ThreadController extends BaseController {
final ResponsiveUtils responsiveUtils;
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final presentationMailCurrent = PresentationThread.createThreadEmpty().obs;
ThreadController(this.responsiveUtils);
@override
void onReady() {
super.onReady();
mailboxDashBoardController.scaffoldKey.currentState?.openEndDrawer();
}
SelectMode getSelectMode(PresentationThread presentationThread) {
return presentationMailCurrent.value.id == presentationThread.id
? SelectMode.ACTIVE
: SelectMode.INACTIVE;
}
void selectMail(BuildContext context, PresentationThread presentationMailSelected) {
presentationMailCurrent.value = presentationMailSelected;
mailboxDashBoardController.mailCurrent.value = presentationMailSelected;
if (!responsiveUtils.isDesktop(context)) {
goToMail(context);
}
}
void goToMailbox({required GlobalKey<ScaffoldState> keyWidgetMailboxContainer}) {
keyWidgetMailboxContainer.currentState?.openDrawer();
}
void goToMail(BuildContext context) {
Get.toNamed(AppRoutes.MAIL);
}
@override
void onData(Either<Failure, Success> newState) {
super.onData(newState);
}
@override
void onDone() {}
@override
void onError(error) {}
}
@@ -0,0 +1,93 @@
import 'package:core/core.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/thread_tile_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ThreadView extends GetWidget<ThreadController> {
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: AppColor.primaryLightColor,
body: SafeArea(
right: false,
left: false,
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() => _buildAppBarMailboxListMail(context)),
Expanded(child: _buildListMail(context)),
])
)
),
);
}
Widget _buildAppBarMailboxListMail(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 16, top: 8.0, right: 16),
child: AppBarThreadWidgetBuilder(
context,
imagePaths,
responsiveUtils,
mailboxDashBoardController.mailboxCurrent.value)
.onOpenListMailboxActionClick(() => controller.goToMailbox(keyWidgetMailboxContainer: mailboxDashBoardController.scaffoldKey))
.build());
}
Widget _buildListMail(BuildContext context) {
return Container(
margin: EdgeInsets.zero,
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
padding: EdgeInsets.zero,
color: responsiveUtils.isMobile(context) ? AppColor.bgMailboxListMail : Colors.white,
child: Obx(() => controller.viewState.value.fold(
(failure) => _buildEmptyThread(context),
(success) => _buildListThreadBody(context, []))
),
);
}
Widget _buildListThreadBody(BuildContext context, List<PresentationThread> listThread) {
if (listThread.isEmpty) {
return _buildEmptyThread(context);
} else {
return ListView.builder(
padding: EdgeInsets.only(top: 16, left: 16, right: 16),
key: Key('thread_list'),
itemCount: listThread.length,
itemBuilder: (context, index) =>
Obx(() => ThreadTileBuilder(
imagePaths,
controller.getSelectMode(listThread[index]),
listThread[index])
.onOpenMailAction(() => controller.selectMail(context, listThread[index]))
.build()
)
);
}
}
Widget _buildEmptyThread(BuildContext context) {
return Text(
AppLocalizations.of(context).no_threads,
maxLines: 1,
style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold));
}
}
@@ -10,7 +10,7 @@ typedef OnOpenSearchMailActionClick = void Function();
typedef OnOpenListMailboxActionClick = void Function();
typedef OnOpenUserInformationActionClick = void Function();
class AppBarListMailWidgetBuilder {
class AppBarThreadWidgetBuilder {
OnOpenSearchMailActionClick? _onOpenSearchMailActionClick;
OnOpenListMailboxActionClick? _onOpenListMailboxActionClick;
OnOpenUserInformationActionClick? _onOpenUserInformationActionClick;
@@ -20,26 +20,26 @@ class AppBarListMailWidgetBuilder {
final ResponsiveUtils _responsiveUtils;
final PresentationMailbox _presentationMailbox;
AppBarListMailWidgetBuilder(
AppBarThreadWidgetBuilder(
this._context,
this._imagePaths,
this._responsiveUtils,
this._presentationMailbox
);
AppBarListMailWidgetBuilder onOpenUserInformationAction(
AppBarThreadWidgetBuilder onOpenUserInformationAction(
OnOpenUserInformationActionClick onOpenUserInformationActionClick) {
_onOpenUserInformationActionClick = onOpenUserInformationActionClick;
return this;
}
AppBarListMailWidgetBuilder onOpenSearchMailActionClick(
AppBarThreadWidgetBuilder onOpenSearchMailActionClick(
OnOpenSearchMailActionClick onOpenSearchMailActionClick) {
_onOpenSearchMailActionClick = onOpenSearchMailActionClick;
return this;
}
AppBarListMailWidgetBuilder onOpenListMailboxActionClick(
AppBarThreadWidgetBuilder onOpenListMailboxActionClick(
OnOpenListMailboxActionClick onOpenListMailboxActionClick) {
_onOpenListMailboxActionClick = onOpenListMailboxActionClick;
return this;
@@ -47,7 +47,7 @@ class AppBarListMailWidgetBuilder {
Widget build() {
return Container(
key: Key('app_bar_list_mail_widget'),
key: Key('app_bar_thread_widget'),
alignment: Alignment.center,
color: Colors.white,
child: MediaQuery(
@@ -8,21 +8,21 @@ import 'package:model/model.dart';
typedef OnOpenMailActionClick = void Function();
class MailTileBuilder {
class ThreadTileBuilder {
final SelectMode _selectMode;
final ImagePaths _imagePaths;
final PresentationMail _presentationMail;
final PresentationThread _presentationThread;
OnOpenMailActionClick? _onOpenMailActionClick;
MailTileBuilder(
ThreadTileBuilder(
this._imagePaths,
this._selectMode,
this._presentationMail,
this._presentationThread,
);
MailTileBuilder onOpenMailAction(OnOpenMailActionClick onOpenMailActionClick) {
ThreadTileBuilder onOpenMailAction(OnOpenMailActionClick onOpenMailActionClick) {
_onOpenMailActionClick = onOpenMailActionClick;
return this;
}
@@ -33,7 +33,7 @@ class MailTileBuilder {
splashColor: Colors.transparent,
highlightColor: Colors.transparent),
child: Container(
key: Key('mail_list_tile'),
key: Key('thread_tile'),
margin: EdgeInsets.only(bottom: 10),
padding: EdgeInsets.only(left: 16, right: 16, top: 10, bottom: 10),
alignment: Alignment.center,
@@ -69,7 +69,7 @@ class MailTileBuilder {
children: [
Expanded(
child: Text(
'TMail app',
'',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(
@@ -77,7 +77,7 @@ class MailTileBuilder {
color: _selectMode == SelectMode.ACTIVE ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor,
fontWeight: FontWeight.bold))),
Text(
'9:29 AM',
'',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(
@@ -95,7 +95,7 @@ class MailTileBuilder {
Padding(
padding: EdgeInsets.only(bottom: 3),
child: Text(
'Welcome to TMail!',
'',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(
@@ -108,7 +108,7 @@ class MailTileBuilder {
children: [
Expanded(
child: Text(
'${_presentationMail.message}',
'${_presentationThread.message}',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor))),
@@ -86,5 +86,40 @@ class AppLocalizations {
name: 'new_folder',
);
}
String get reply_all {
return Intl.message(
'Reply all',
name: 'reply_all',
);
}
String get reply {
return Intl.message(
'Reply',
name: 'reply',
);
}
String get forward {
return Intl.message(
'Forward',
name: 'forward',
);
}
String get no_threads {
return Intl.message(
'No threads',
name: 'no_threads',
);
}
String get no_mail_selected {
return Intl.message(
'No mail selected',
name: 'no_mail_selected',
);
}
}
+8
View File
@@ -1,5 +1,6 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/main/bindings/main_bindings.dart';
@@ -10,6 +11,13 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MainBindings().dependencies();
// Disable landscape on device mobile
if (Get.size.shortestSide < ResponsiveUtils.minTabletWidth) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
runApp(TMailApp());
}
+18
View File
@@ -3,8 +3,14 @@ import 'package:tmail_ui_user/features/home/presentation/home_bindings.dart';
import 'package:tmail_ui_user/features/home/presentation/home_view.dart';
import 'package:tmail_ui_user/features/login/presentation/login_bindings.dart';
import 'package:tmail_ui_user/features/login/presentation/login_view.dart';
import 'package:tmail_ui_user/features/mail/presentation/mail_bindings.dart';
import 'package:tmail_ui_user/features/mail/presentation/mail_view.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
class AppPages {
@@ -21,5 +27,17 @@ class AppPages {
name: AppRoutes.MAILBOX,
page: () => MailboxView(),
binding: MailboxBindings()),
GetPage(
name: AppRoutes.MAILBOX_DASHBOARD,
page: () => MailboxDashBoardView(),
binding: MailboxDashBoardBindings()),
GetPage(
name: AppRoutes.THREAD,
page: () => ThreadView(),
binding: ThreadBindings()),
GetPage(
name: AppRoutes.MAIL,
page: () => MailView(),
binding: MailBindings()),
];
}
+4 -1
View File
@@ -1,5 +1,8 @@
abstract class AppRoutes {
static const HOME = '/';
static const LOGIN = '/login';
static const MAILBOX = '/mailbox';
static const MAILBOX = '$MAILBOX_DASHBOARD/mailbox';
static const MAILBOX_DASHBOARD = '/mailboxDashBoard';
static const THREAD = '$MAILBOX_DASHBOARD/thread';
static const MAIL = '$THREAD/mail';
}
-8
View File
@@ -1,11 +1,3 @@
# model
A model module contains entities
## Getting Started
For help getting started with Flutter, view our online
[documentation](https://flutter.dev/).
For instructions integrating Flutter modules to your existing applications,
see the [add-to-app documentation](https://flutter.dev/docs/development/add-to-app).
-17
View File
@@ -1,17 +0,0 @@
import 'package:equatable/equatable.dart';
class PresentationMail with EquatableMixin {
final String id;
final String? message;
PresentationMail(this.id, {this.message});
factory PresentationMail.createMailEmpty() {
return PresentationMail('empty');
}
@override
List<Object?> get props => [id, message];
}
+17
View File
@@ -0,0 +1,17 @@
import 'package:equatable/equatable.dart';
class PresentationThread with EquatableMixin {
final String id;
final String? message;
PresentationThread(this.id, {this.message});
factory PresentationThread.createThreadEmpty() {
return PresentationThread('empty');
}
@override
List<Object?> get props => [id, message];
}
+1 -1
View File
@@ -14,4 +14,4 @@ export 'mailbox/select_mode.dart';
export 'mailbox/expand_mode.dart';
// Mail
export 'mail/presentation_mail.dart';
export 'mail/presentation_thread.dart';
+1 -1
View File
@@ -38,7 +38,7 @@ dependencies:
jmap_dart_client:
git:
url: git://github.com/hoangdat/jmap-dart-client.git
ref: serialize
ref: get_session_dat
dev_dependencies:
flutter_test:
+1 -1
View File
@@ -78,7 +78,7 @@ dependencies:
jmap_dart_client:
git:
url: git://github.com/hoangdat/jmap-dart-client.git
ref: serialize
ref: get_session_dat
dev_dependencies:
flutter_test: