TF-802 Show message when enable/disable vacation responder
This commit is contained in:
@@ -376,7 +376,7 @@ class ComposerController extends BaseController {
|
|||||||
final sentDate = presentationEmail.sentAt;
|
final sentDate = presentationEmail.sentAt;
|
||||||
final emailAddress = presentationEmail.from.listEmailAddressToString(isFullEmailAddress: true);
|
final emailAddress = presentationEmail.from.listEmailAddressToString(isFullEmailAddress: true);
|
||||||
return AppLocalizations.of(context).header_email_quoted(
|
return AppLocalizations.of(context).header_email_quoted(
|
||||||
sentDate.formatDate(pattern: 'MMM d, y h:mm a', locale: locale),
|
sentDate.formatDateToLocal(pattern: 'MMM d, y h:mm a', locale: locale),
|
||||||
emailAddress);
|
emailAddress);
|
||||||
case EmailActionType.forward:
|
case EmailActionType.forward:
|
||||||
var headerQuoted = '------- ${AppLocalizations.of(context).forwarded_message} -------'.addNewLineTag();
|
var headerQuoted = '------- ${AppLocalizations.of(context).forwarded_message} -------'.addNewLineTag();
|
||||||
@@ -397,7 +397,7 @@ class ComposerController extends BaseController {
|
|||||||
if (sentDate != null) {
|
if (sentDate != null) {
|
||||||
headerQuoted = headerQuoted
|
headerQuoted = headerQuoted
|
||||||
.append('${AppLocalizations.of(context).date}: ')
|
.append('${AppLocalizations.of(context).date}: ')
|
||||||
.append(sentDate.formatDate(pattern: 'MMM d, y h:mm a', locale: locale))
|
.append(sentDate.formatDateToLocal(pattern: 'MMM d, y h:mm a', locale: locale))
|
||||||
.addNewLineTag();
|
.addNewLineTag();
|
||||||
}
|
}
|
||||||
if (fromEmailAddress.isNotEmpty) {
|
if (fromEmailAddress.isNotEmpty) {
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class EmailView extends GetWidget<EmailController> with NetworkConnectionMixin {
|
|||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
_buildAppBar(context),
|
_buildAppBar(context),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.mailboxDashBoardController.vacationResponse.value?.vacationResponderIsReady == true &&
|
if (controller.mailboxDashBoardController.vacationResponse.value?.vacationResponderIsValid == true &&
|
||||||
(responsiveUtils.isMobile(context) ||
|
(responsiveUtils.isMobile(context) ||
|
||||||
responsiveUtils.isTablet(context) ||
|
responsiveUtils.isTablet(context) ||
|
||||||
responsiveUtils.isLandscapeMobile(context))) {
|
responsiveUtils.isLandscapeMobile(context))) {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
|
|||||||
SizedBox(child: MailboxView(), width: responsiveUtils.defaultSizeMenu),
|
SizedBox(child: MailboxView(), width: responsiveUtils.defaultSizeMenu),
|
||||||
Expanded(child: Column(children: [
|
Expanded(child: Column(children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.vacationResponse.value?.vacationResponderIsReady == true) {
|
if (controller.vacationResponse.value?.vacationResponderIsValid == true) {
|
||||||
return VacationNotificationMessageWidget(
|
return VacationNotificationMessageWidget(
|
||||||
margin: const EdgeInsets.only(top: 16, right: 16),
|
margin: const EdgeInsets.only(top: 16, right: 16),
|
||||||
vacationResponse: controller.vacationResponse.value!,
|
vacationResponse: controller.vacationResponse.value!,
|
||||||
|
|||||||
+56
-2
@@ -3,8 +3,10 @@ import 'package:core/utils/app_logger.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/utc_date.dart';
|
import 'package:jmap_dart_client/jmap/core/utc_date.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
||||||
|
import 'package:model/model.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_presentation.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_presentation.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_responder_status.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_responder_status.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
extension VacationResponseExtension on VacationResponse {
|
extension VacationResponseExtension on VacationResponse {
|
||||||
|
|
||||||
@@ -26,14 +28,48 @@ extension VacationResponseExtension on VacationResponse {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get vacationResponderIsValid {
|
||||||
|
return vacationResponderIsReady && !vacationResponderIsStopped;
|
||||||
|
}
|
||||||
|
|
||||||
bool get vacationResponderIsReady {
|
bool get vacationResponderIsReady {
|
||||||
if (isEnabled == true) {
|
if (isEnabled == true) {
|
||||||
final currentDate = DateTime.now().toUtc();
|
final currentDate = DateTime.now().toUtc();
|
||||||
log('VacationResponseExtension::vacationResponderEnabled(): currentDate: $currentDate');
|
log('VacationResponseExtension::vacationResponderEnabled(): currentDate: $currentDate');
|
||||||
final startDate = fromDate?.value.toUtc();
|
final startDate = fromDate?.value.toUtc();
|
||||||
log('VacationResponseExtension::vacationResponderEnabled(): startDate: $startDate');
|
log('VacationResponseExtension::vacationResponderEnabled(): startDate: $startDate');
|
||||||
if (startDate?.isBefore(currentDate) == true ||
|
if (startDate != null && (startDate.isBefore(currentDate) ||
|
||||||
startDate?.isAtSameMomentAs(currentDate) == true) {
|
startDate.isAtSameMomentAs(currentDate))) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get vacationResponderIsWaiting {
|
||||||
|
if (isEnabled == true) {
|
||||||
|
final currentDate = DateTime.now().toUtc();
|
||||||
|
log('VacationResponseExtension::vacationResponderIsWaiting(): currentDate: $currentDate');
|
||||||
|
final startDate = fromDate?.value.toUtc();
|
||||||
|
log('VacationResponseExtension::vacationResponderIsWaiting(): startDate: $startDate');
|
||||||
|
if (startDate != null && startDate.isAfter(currentDate)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get vacationResponderIsStopped {
|
||||||
|
if (isEnabled == true) {
|
||||||
|
final currentDate = DateTime.now().toUtc();
|
||||||
|
log('VacationResponseExtension::vacationResponderIsStopped(): currentDate: $currentDate');
|
||||||
|
final endDate = toDate?.value.toUtc();
|
||||||
|
log('VacationResponseExtension::vacationResponderIsStopped(): endDate: $endDate');
|
||||||
|
if (endDate != null && endDate.isBefore(currentDate)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -59,4 +95,22 @@ extension VacationResponseExtension on VacationResponse {
|
|||||||
htmlBody: htmlBody ?? this.htmlBody
|
htmlBody: htmlBody ?? this.htmlBody
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getNotificationMessage(BuildContext context) {
|
||||||
|
if (vacationResponderIsValid) {
|
||||||
|
return AppLocalizations.of(context).yourVacationResponderIsEnabled;
|
||||||
|
} else if (vacationResponderIsWaiting) {
|
||||||
|
return AppLocalizations.of(context).messageEnableVacationResponderAutomatically(
|
||||||
|
fromDate.formatDate(
|
||||||
|
pattern: 'MMM d, y h:mm a',
|
||||||
|
locale: Localizations.localeOf(context).toLanguageTag()));
|
||||||
|
} else if (vacationResponderIsStopped) {
|
||||||
|
return AppLocalizations.of(context).messageDisableVacationResponderAutomatically(
|
||||||
|
toDate.formatDate(
|
||||||
|
pattern: 'MMM d, y h:mm a',
|
||||||
|
locale: Localizations.localeOf(context).toLanguageTag()));
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ class ManageAccountDashBoardView extends GetWidget<ManageAccountDashBoardControl
|
|||||||
color: AppColor.colorBgDesktop,
|
color: AppColor.colorBgDesktop,
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.vacationResponse.value?.vacationResponderIsReady == true) {
|
if (controller.vacationResponse.value?.vacationResponderIsValid == true) {
|
||||||
return VacationNotificationMessageWidget(
|
return VacationNotificationMessageWidget(
|
||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
top: 16,
|
top: 16,
|
||||||
@@ -93,6 +93,22 @@ class ManageAccountDashBoardView extends GetWidget<ManageAccountDashBoardControl
|
|||||||
right: BuildUtils.isWeb ? 24 : 16),
|
right: BuildUtils.isWeb ? 24 : 16),
|
||||||
vacationResponse: controller.vacationResponse.value!,
|
vacationResponse: controller.vacationResponse.value!,
|
||||||
action: () => controller.disableVacationResponder());
|
action: () => controller.disableVacationResponder());
|
||||||
|
} else if ((controller.vacationResponse.value?.vacationResponderIsWaiting == true
|
||||||
|
|| controller.vacationResponse.value?.vacationResponderIsStopped == true)
|
||||||
|
&& controller.accountMenuItemSelected.value == AccountMenuItem.vacation) {
|
||||||
|
return VacationNotificationMessageWidget(
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
top: 16,
|
||||||
|
left: BuildUtils.isWeb ? 24 : 16,
|
||||||
|
right: BuildUtils.isWeb ? 24 : 16),
|
||||||
|
vacationResponse: controller.vacationResponse.value!,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
||||||
|
backgroundColor: Colors.yellow,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
leadingIcon: const Padding(
|
||||||
|
padding: EdgeInsets.only(right: 16),
|
||||||
|
child: Icon(Icons.timer, size: 20),
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
@@ -111,7 +127,7 @@ class ManageAccountDashBoardView extends GetWidget<ManageAccountDashBoardControl
|
|||||||
: const EdgeInsets.only(top: 16, left: 24, right: 32),
|
: const EdgeInsets.only(top: 16, left: 24, right: 32),
|
||||||
child: _buildAppbar(context)),
|
child: _buildAppbar(context)),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.vacationResponse.value?.vacationResponderIsReady == true) {
|
if (controller.vacationResponse.value?.vacationResponderIsValid == true) {
|
||||||
return VacationNotificationMessageWidget(
|
return VacationNotificationMessageWidget(
|
||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
left: BuildUtils.isWeb ? 24 : 16,
|
left: BuildUtils.isWeb ? 24 : 16,
|
||||||
@@ -119,6 +135,22 @@ class ManageAccountDashBoardView extends GetWidget<ManageAccountDashBoardControl
|
|||||||
top: BuildUtils.isWeb ? 16 : 0),
|
top: BuildUtils.isWeb ? 16 : 0),
|
||||||
vacationResponse: controller.vacationResponse.value!,
|
vacationResponse: controller.vacationResponse.value!,
|
||||||
action: () => controller.disableVacationResponder());
|
action: () => controller.disableVacationResponder());
|
||||||
|
} else if ((controller.vacationResponse.value?.vacationResponderIsWaiting == true
|
||||||
|
|| controller.vacationResponse.value?.vacationResponderIsStopped == true)
|
||||||
|
&& controller.accountMenuItemSelected.value == AccountMenuItem.vacation) {
|
||||||
|
return VacationNotificationMessageWidget(
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
left: BuildUtils.isWeb ? 24 : 16,
|
||||||
|
right: BuildUtils.isWeb ? 24 : 16,
|
||||||
|
top: BuildUtils.isWeb ? 16 : 0),
|
||||||
|
vacationResponse: controller.vacationResponse.value!,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
backgroundColor: Colors.yellow,
|
||||||
|
leadingIcon: const Padding(
|
||||||
|
padding: EdgeInsets.only(right: 16),
|
||||||
|
child: Icon(Icons.timer, size: 20),
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-17
@@ -2,6 +2,7 @@
|
|||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
||||||
|
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
typedef DisableVacationResponderAction = Function();
|
typedef DisableVacationResponderAction = Function();
|
||||||
@@ -13,6 +14,9 @@ class VacationNotificationMessageWidget extends StatelessWidget {
|
|||||||
final EdgeInsets? margin;
|
final EdgeInsets? margin;
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
final double? radius;
|
final double? radius;
|
||||||
|
final Widget? leadingIcon;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final FontWeight? fontWeight;
|
||||||
|
|
||||||
const VacationNotificationMessageWidget({
|
const VacationNotificationMessageWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -21,6 +25,9 @@ class VacationNotificationMessageWidget extends StatelessWidget {
|
|||||||
this.radius,
|
this.radius,
|
||||||
this.margin,
|
this.margin,
|
||||||
this.padding,
|
this.padding,
|
||||||
|
this.leadingIcon,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.fontWeight,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -28,31 +35,33 @@ class VacationNotificationMessageWidget extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
margin: margin ?? const EdgeInsets.symmetric(horizontal: 16),
|
margin: margin ?? const EdgeInsets.symmetric(horizontal: 16),
|
||||||
padding: padding ?? const EdgeInsets.only(top: 5, bottom: 5, left: 16),
|
padding: padding ?? const EdgeInsets.only(left: 16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(radius ?? 5),
|
borderRadius: BorderRadius.circular(radius ?? 5),
|
||||||
color: AppColor.colorVacationNotificationMessageBackground,
|
color: backgroundColor ?? AppColor.colorVacationNotificationMessageBackground,
|
||||||
),
|
),
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
|
if (leadingIcon != null) leadingIcon!,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context).yourVacationResponderIsEnabled,
|
vacationResponse.getNotificationMessage(context),
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: 16,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: fontWeight ?? FontWeight.w500,
|
||||||
))),
|
))),
|
||||||
buildTextButton(
|
if (action != null)
|
||||||
AppLocalizations.of(context).disable.allInCaps,
|
buildTextButton(
|
||||||
textStyle: const TextStyle(
|
AppLocalizations.of(context).disable.allInCaps,
|
||||||
fontWeight: FontWeight.w500,
|
textStyle: const TextStyle(
|
||||||
fontSize: 16,
|
fontWeight: FontWeight.w500,
|
||||||
color: AppColor.colorTextButton),
|
fontSize: 13,
|
||||||
backgroundColor: Colors.transparent,
|
color: AppColor.colorTextButton),
|
||||||
width: 128,
|
backgroundColor: Colors.transparent,
|
||||||
height: 44,
|
width: 128,
|
||||||
radius: 10,
|
height: 44,
|
||||||
onTap: () => action?.call())
|
radius: 10,
|
||||||
|
onTap: () => action!.call())
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
|
|||||||
... [
|
... [
|
||||||
_buildAppBarNormal(context),
|
_buildAppBarNormal(context),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.mailboxDashBoardController.vacationResponse.value?.vacationResponderIsReady == true) {
|
if (controller.mailboxDashBoardController.vacationResponse.value?.vacationResponderIsValid == true) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
child: VacationNotificationMessageWidget(
|
child: VacationNotificationMessageWidget(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@@last_modified": "2022-08-18T16:16:05.548805",
|
"@@last_modified": "2022-08-18T17:02:08.322535",
|
||||||
"initializing_data": "Initializing data...",
|
"initializing_data": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -2017,5 +2017,25 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"messageEnableVacationResponderAutomatically": "Your vacation responder will be activated on {startDate}",
|
||||||
|
"@messageEnableVacationResponderAutomatically": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [
|
||||||
|
"startDate"
|
||||||
|
],
|
||||||
|
"placeholders": {
|
||||||
|
"startDate": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"messageDisableVacationResponderAutomatically": "Your vacation responder stopped on {endDate}",
|
||||||
|
"@messageDisableVacationResponderAutomatically": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [
|
||||||
|
"endDate"
|
||||||
|
],
|
||||||
|
"placeholders": {
|
||||||
|
"endDate": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2092,4 +2092,18 @@ class AppLocalizations {
|
|||||||
name: 'yourVacationResponderIsDisabledSuccessfully',
|
name: 'yourVacationResponderIsDisabledSuccessfully',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String messageEnableVacationResponderAutomatically(String startDate) {
|
||||||
|
return Intl.message(
|
||||||
|
'Your vacation responder will be activated on $startDate',
|
||||||
|
name: 'messageEnableVacationResponderAutomatically',
|
||||||
|
args: [startDate]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String messageDisableVacationResponderAutomatically(String endDate) {
|
||||||
|
return Intl.message(
|
||||||
|
'Your vacation responder stopped on $endDate',
|
||||||
|
name: 'messageDisableVacationResponderAutomatically',
|
||||||
|
args: [endDate]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ extension PresentationEmailExtension on PresentationEmail {
|
|||||||
String getReceivedAt(String newLocale, {String? pattern}) {
|
String getReceivedAt(String newLocale, {String? pattern}) {
|
||||||
final emailTime = receivedAt;
|
final emailTime = receivedAt;
|
||||||
if (emailTime != null) {
|
if (emailTime != null) {
|
||||||
return emailTime.formatDate(
|
return emailTime.formatDateToLocal(
|
||||||
pattern: pattern ?? emailTime.value.toLocal().toPattern(),
|
pattern: pattern ?? emailTime.value.toLocal().toPattern(),
|
||||||
locale: newLocale);
|
locale: newLocale);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,19 @@ import 'package:jmap_dart_client/jmap/core/utc_date.dart';
|
|||||||
|
|
||||||
extension UTCDateExtension on UTCDate? {
|
extension UTCDateExtension on UTCDate? {
|
||||||
|
|
||||||
String formatDate({String pattern = 'yMMMd', String locale = 'en_US'}) {
|
String formatDateToLocal({String pattern = 'yMMMd', String locale = 'en_US'}) {
|
||||||
if (this != null) {
|
if (this != null) {
|
||||||
return DateFormat(pattern, locale).format(this!.value.toLocal());
|
return DateFormat(pattern, locale).format(this!.value.toLocal());
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String formatDate({String pattern = 'yMMMd', String locale = 'en_US'}) {
|
||||||
|
if (this != null) {
|
||||||
|
return DateFormat(pattern, locale).format(this!.value);
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user