TF-4308 Add NoLabelYetWidget for choose label modal
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<svg width="98" height="98" viewBox="0 0 98 98" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="49" cy="49" r="49" fill="#E0EDFF"/>
|
||||
<path d="M49.9441 28.2701C49.1313 27.4571 48.0289 27.0002 46.8794 27H31.3348C30.1851 27 29.0826 27.4567 28.2696 28.2696C27.4567 29.0826 27 30.1851 27 31.3348V46.8794C27.0002 48.0289 27.4571 49.1313 28.2701 49.9441L47.1351 68.8091C48.1202 69.788 49.4526 70.3374 50.8414 70.3374C52.2301 70.3374 53.5625 69.788 54.5476 68.8091L68.8091 54.5476C69.788 53.5625 70.3374 52.2301 70.3374 50.8414C70.3374 49.4526 69.788 48.1202 68.8091 47.1351L49.9441 28.2701Z" stroke="#9EBFEB" stroke-width="4.33479" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M38.9206 40.0043C39.5191 40.0043 40.0043 39.5191 40.0043 38.9206C40.0043 38.3221 39.5191 37.8369 38.9206 37.8369C38.3221 37.8369 37.8369 38.3221 37.8369 38.9206C37.8369 39.5191 38.3221 40.0043 38.9206 40.0043Z" fill="#9EBFEB" stroke="#9EBFEB" stroke-width="4.33479" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1016 B |
@@ -282,6 +282,7 @@ class ImagePaths {
|
||||
String get icColorPicker => _getImagePath('ic_color_picker.svg');
|
||||
String get icThumbsUp => _getImagePath('ic_thumbs_up.svg');
|
||||
String get icAllEmail => _getImagePath('ic_all_email.svg');
|
||||
String get icNoTag => _getImagePath('ic_no_tag.svg');
|
||||
|
||||
String _getImagePath(String imageName) {
|
||||
return AssetsPaths.images + imageName;
|
||||
|
||||
@@ -4,10 +4,10 @@ import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/theme_utils.dart';
|
||||
import 'package:core/presentation/views/button/default_close_button_widget.dart';
|
||||
import 'package:core/presentation/views/dialog/modal_list_action_button_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:labels/labels.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/list_label_with_action_modal.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/no_label_yet_widget.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
@@ -30,10 +30,6 @@ class ChooseLabelModal extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
||||
final ValueNotifier<bool> _addLabelStateNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<List<Label>> _selectedLabelStateNotifier =
|
||||
ValueNotifier([]);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appLocalizations = AppLocalizations.of(context);
|
||||
@@ -41,6 +37,8 @@ class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
||||
return LayoutBuilder(builder: (_, constraints) {
|
||||
final currentScreenWidth = constraints.maxWidth;
|
||||
final currentScreenHeight = constraints.maxHeight;
|
||||
double height = math.min(currentScreenHeight - 100, 645);
|
||||
double width = math.min(currentScreenWidth - 32, 536);
|
||||
|
||||
Widget bodyWidget = Container(
|
||||
decoration: BoxDecoration(
|
||||
@@ -58,11 +56,8 @@ class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
||||
),
|
||||
],
|
||||
),
|
||||
width: math.min(
|
||||
currentScreenWidth - 32,
|
||||
554,
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: currentScreenHeight - 100),
|
||||
width: width,
|
||||
height: height,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Stack(
|
||||
children: [
|
||||
@@ -73,23 +68,21 @@ class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
||||
_buildTitle(appLocalizations),
|
||||
const Divider(height: 1, color: Colors.black12),
|
||||
_buildSubtitle(appLocalizations),
|
||||
Flexible(child: _buildLabelListView()),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _addLabelStateNotifier,
|
||||
builder: (_, value, __) {
|
||||
return ModalListActionButtonWidget(
|
||||
positiveLabel: appLocalizations.addLabel,
|
||||
negativeLabel: appLocalizations.cancel,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 25,
|
||||
horizontal: 25,
|
||||
),
|
||||
isPositiveActionEnabled: value,
|
||||
onPositiveAction: _onAddLabel,
|
||||
onNegativeAction: _onCloseModal,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (widget.labels.isNotEmpty)
|
||||
Expanded(
|
||||
child: ListLabelWithActionModal(
|
||||
labels: widget.labels,
|
||||
imagePaths: widget.imagePaths,
|
||||
onLabelAsToEmailsAction: widget.onLabelAsToEmailsAction,
|
||||
onCloseModal: _onCloseModal,
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: NoLabelYetWidget(imagePaths: widget.imagePaths),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
DefaultCloseButtonWidget(
|
||||
@@ -137,63 +130,7 @@ class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLabelListView() {
|
||||
final labelList = widget.labels;
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: labelList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final label = labelList[index];
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _selectedLabelStateNotifier,
|
||||
builder: (_, selectedLabels, __) {
|
||||
final isSelected = selectedLabels.contains(label);
|
||||
return LabelListItem(
|
||||
label: label,
|
||||
imagePaths: widget.imagePaths,
|
||||
padding: const EdgeInsetsDirectional.only(
|
||||
start: 4,
|
||||
end: 16,
|
||||
),
|
||||
enableSelectedIcon: true,
|
||||
isSelected: isSelected,
|
||||
onOpenLabelCallback: _onToggleLabel,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _onToggleLabel(Label selectedLabel) {
|
||||
final currentLabels = _selectedLabelStateNotifier.value;
|
||||
|
||||
final isAlreadySelected = currentLabels.any((label) => label.id == selectedLabel.id);
|
||||
if (isAlreadySelected) {
|
||||
_selectedLabelStateNotifier.value =
|
||||
currentLabels.where((label) => label.id != selectedLabel.id).toList();
|
||||
} else {
|
||||
_selectedLabelStateNotifier.value = [...currentLabels, selectedLabel];
|
||||
}
|
||||
|
||||
_addLabelStateNotifier.value = _selectedLabelStateNotifier.value.isNotEmpty;
|
||||
}
|
||||
|
||||
void _onAddLabel() {
|
||||
widget.onLabelAsToEmailsAction(_selectedLabelStateNotifier.value);
|
||||
|
||||
popBack();
|
||||
}
|
||||
|
||||
void _onCloseModal() {
|
||||
popBack();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_addLabelStateNotifier.dispose();
|
||||
_selectedLabelStateNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/views/dialog/modal_list_action_button_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/choose_label_modal.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
class ListLabelWithActionModal extends StatefulWidget {
|
||||
final List<Label> labels;
|
||||
final ImagePaths imagePaths;
|
||||
final OnLabelAsToEmailsAction onLabelAsToEmailsAction;
|
||||
final VoidCallback onCloseModal;
|
||||
|
||||
const ListLabelWithActionModal({
|
||||
super.key,
|
||||
required this.labels,
|
||||
required this.imagePaths,
|
||||
required this.onLabelAsToEmailsAction,
|
||||
required this.onCloseModal,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ListLabelWithActionModal> createState() =>
|
||||
_ListLabelWithActionModalState();
|
||||
}
|
||||
|
||||
class _ListLabelWithActionModalState extends State<ListLabelWithActionModal> {
|
||||
final ValueNotifier<bool> _addLabelStateNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<List<Label>> _selectedLabelStateNotifier =
|
||||
ValueNotifier([]);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final labelList = widget.labels;
|
||||
final appLocalizations = AppLocalizations.of(context);
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: labelList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final label = labelList[index];
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _selectedLabelStateNotifier,
|
||||
builder: (_, selectedLabels, __) {
|
||||
final isSelected = selectedLabels.contains(label);
|
||||
return LabelListItem(
|
||||
label: label,
|
||||
imagePaths: widget.imagePaths,
|
||||
padding: const EdgeInsetsDirectional.only(
|
||||
start: 4,
|
||||
end: 16,
|
||||
),
|
||||
enableSelectedIcon: true,
|
||||
isSelected: isSelected,
|
||||
onOpenLabelCallback: _onToggleLabel,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _addLabelStateNotifier,
|
||||
builder: (_, value, __) {
|
||||
return ModalListActionButtonWidget(
|
||||
positiveLabel: appLocalizations.addLabel,
|
||||
negativeLabel: appLocalizations.cancel,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 25,
|
||||
horizontal: 25,
|
||||
),
|
||||
isPositiveActionEnabled: value,
|
||||
onPositiveAction: _onAddLabel,
|
||||
onNegativeAction: widget.onCloseModal,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _onToggleLabel(Label selectedLabel) {
|
||||
final currentLabels = _selectedLabelStateNotifier.value;
|
||||
|
||||
if (currentLabels.contains(selectedLabel)) {
|
||||
_selectedLabelStateNotifier.value =
|
||||
currentLabels.where((label) => label.id != selectedLabel.id).toList();
|
||||
} else {
|
||||
_selectedLabelStateNotifier.value = [...currentLabels, selectedLabel];
|
||||
}
|
||||
|
||||
_addLabelStateNotifier.value = _selectedLabelStateNotifier.value.isNotEmpty;
|
||||
}
|
||||
|
||||
void _onAddLabel() {
|
||||
widget.onLabelAsToEmailsAction(_selectedLabelStateNotifier.value);
|
||||
widget.onCloseModal();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_addLabelStateNotifier.dispose();
|
||||
_selectedLabelStateNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/theme_utils.dart';
|
||||
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
class NoLabelYetWidget extends StatelessWidget {
|
||||
final ImagePaths imagePaths;
|
||||
final VoidCallback? onCreateLabel;
|
||||
|
||||
const NoLabelYetWidget({
|
||||
super.key,
|
||||
required this.imagePaths,
|
||||
this.onCreateLabel,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appLocalizations = AppLocalizations.of(context);
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
constraints: const BoxConstraints(maxWidth: 352),
|
||||
child: SingleChildScrollView(
|
||||
physics: const ClampingScrollPhysics(),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_IconNoTag(icon: imagePaths.icNoTag),
|
||||
_LabelNoTag(
|
||||
text: appLocalizations.noLabelsYet,
|
||||
textStyle: ThemeUtils.textStyleInter600(),
|
||||
padding: const EdgeInsetsDirectional.only(top: 16),
|
||||
),
|
||||
Flexible(
|
||||
child: _LabelNoTag(
|
||||
text: appLocalizations.noLabelsYetMessageDescriptions,
|
||||
textStyle: ThemeUtils.textStyleInter400.copyWith(
|
||||
fontSize: 16,
|
||||
height: 21.01 / 16,
|
||||
letterSpacing: -0.15,
|
||||
color: AppColor.gray424244.withValues(alpha: 0.64),
|
||||
),
|
||||
padding: const EdgeInsetsDirectional.only(top: 36),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
constraints: const BoxConstraints(minWidth: 186),
|
||||
height: 48,
|
||||
margin: const EdgeInsetsDirectional.only(top: 16),
|
||||
child: ConfirmDialogButton(
|
||||
label: appLocalizations.createALabel,
|
||||
backgroundColor: Colors.white,
|
||||
textColor: AppColor.primaryMain,
|
||||
borderColor: AppColor.primaryMain,
|
||||
icon: imagePaths.icAddIdentity,
|
||||
onTapAction: onCreateLabel ?? () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _IconNoTag extends StatelessWidget {
|
||||
final String icon;
|
||||
|
||||
const _IconNoTag({required this.icon});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SvgPicture.asset(
|
||||
icon,
|
||||
width: 98,
|
||||
height: 98,
|
||||
fit: BoxFit.fill,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LabelNoTag extends StatelessWidget {
|
||||
final String text;
|
||||
final TextStyle textStyle;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
const _LabelNoTag({
|
||||
required this.text,
|
||||
required this.textStyle,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textWidget = Text(
|
||||
text,
|
||||
style: textStyle,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
if (padding != null) {
|
||||
return Padding(padding: padding!, child: textWidget);
|
||||
} else {
|
||||
return textWidget;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,7 +396,6 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
|
||||
controller.mapMailboxById,
|
||||
controller.imagePaths,
|
||||
isLabelAvailable: controller.isLabelAvailable,
|
||||
labels: controller.labelController.labels,
|
||||
onCancelSelection: () =>
|
||||
controller.dispatchAction(CancelSelectionAllEmailAction()),
|
||||
onEmailActionTypeAction: (listEmails, actionType) =>
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'package:core/presentation/utils/theme_utils.dart';
|
||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:model/email/presentation_email.dart';
|
||||
import 'package:model/extensions/list_presentation_email_extension.dart';
|
||||
@@ -22,7 +21,6 @@ class TopBarThreadSelection extends StatelessWidget{
|
||||
final VoidCallback? onCancelSelection;
|
||||
final ImagePaths imagePaths;
|
||||
final bool isLabelAvailable;
|
||||
final List<Label> labels;
|
||||
|
||||
const TopBarThreadSelection (
|
||||
this.listEmail,
|
||||
@@ -31,7 +29,6 @@ class TopBarThreadSelection extends StatelessWidget{
|
||||
{
|
||||
super.key,
|
||||
required this.isLabelAvailable,
|
||||
required this.labels,
|
||||
this.onEmailActionTypeAction,
|
||||
this.onCancelSelection,
|
||||
}
|
||||
@@ -109,7 +106,7 @@ class TopBarThreadSelection extends StatelessWidget{
|
||||
EmailActionType.moveToMailbox,
|
||||
),
|
||||
),
|
||||
if (isLabelAvailable && labels.isNotEmpty)
|
||||
if (isLabelAvailable)
|
||||
TMailButtonWidget.fromIcon(
|
||||
icon: imagePaths.icTag,
|
||||
iconSize: TopBarThreadSelectionStyle.iconSize,
|
||||
|
||||
@@ -5513,5 +5513,23 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"noLabelsYet": "No Labels yet",
|
||||
"@noLabelsYet": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"noLabelsYetMessageDescriptions": "Create your first label to organize your emails more efficiently.",
|
||||
"@noLabelsYetMessageDescriptions": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"createALabel": "Create a label",
|
||||
"@createALabel": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
@@ -5839,4 +5839,25 @@ class AppLocalizations {
|
||||
name: 'addListLabelToListEmailFailureMessage',
|
||||
);
|
||||
}
|
||||
|
||||
String get noLabelsYet {
|
||||
return Intl.message(
|
||||
'No Labels yet',
|
||||
name: 'noLabelsYet',
|
||||
);
|
||||
}
|
||||
|
||||
String get noLabelsYetMessageDescriptions {
|
||||
return Intl.message(
|
||||
'Create your first label to organize your emails more efficiently.',
|
||||
name: 'noLabelsYetMessageDescriptions',
|
||||
);
|
||||
}
|
||||
|
||||
String get createALabel {
|
||||
return Intl.message(
|
||||
'Create a label',
|
||||
name: 'createALabel',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user