mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-12 12:33:07 +00:00
bootstrap style notification
This commit is contained in:
@@ -14,6 +14,6 @@
|
|||||||
|
|
||||||
|
|
||||||
<ng-template #example>
|
<ng-template #example>
|
||||||
<p>Simple example</p>
|
<p>Simple Error example</p>
|
||||||
<button [routerLink]="['/mydspace']">Go to mydspace</button>
|
<button [routerLink]="['/mydspace']">Go to mydspace</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@@ -11,9 +11,10 @@ export class HomePageComponent {
|
|||||||
public notificationOptions: Options = {
|
public notificationOptions: Options = {
|
||||||
position: ['top', 'right'],
|
position: ['top', 'right'],
|
||||||
timeOut: 0,
|
timeOut: 0,
|
||||||
lastOnBottom: true,
|
animate: 'fromLeft'
|
||||||
clickIconToClose: false,
|
// lastOnBottom: true,
|
||||||
showProgressBar: true,
|
// clickIconToClose: false,
|
||||||
|
// showProgressBar: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ViewChild('example') example: TemplateRef<any>;
|
@ViewChild('example') example: TemplateRef<any>;
|
||||||
@@ -24,11 +25,11 @@ export class HomePageComponent {
|
|||||||
createNotification() {
|
createNotification() {
|
||||||
const n1 = this.notificationsService.success('Welcome in DSpace', 'Good choice!',
|
const n1 = this.notificationsService.success('Welcome in DSpace', 'Good choice!',
|
||||||
{
|
{
|
||||||
showProgressBar: false,
|
|
||||||
animate: 'rotate',
|
animate: 'rotate',
|
||||||
timeout: 2000});
|
timeout: 2000});
|
||||||
const n2 = this.notificationsService.error('Error in DSpace', 'This is a fake error!');
|
const n2 = this.notificationsService.info('Info in DSpace', 'For your info...!');
|
||||||
const n3 = this.notificationsService.info(this.example);
|
const n3 = this.notificationsService.warn('Warning in DSpace', 'This is a fake alert!');
|
||||||
|
const n4 = this.notificationsService.error(this.example);
|
||||||
console.log('Notifications pushed');
|
console.log('Notifications pushed');
|
||||||
console.log(n1);
|
console.log(n1);
|
||||||
console.log(n2);
|
console.log(n2);
|
||||||
|
@@ -2,13 +2,11 @@
|
|||||||
<div class="inner-wrapper">
|
<div class="inner-wrapper">
|
||||||
<ds-header></ds-header>
|
<ds-header></ds-header>
|
||||||
|
|
||||||
<p>Inizio notifiche</p>
|
|
||||||
<ds-notifications-board
|
<ds-notifications-board
|
||||||
[options]="notificationOptions"
|
[options]="notificationOptions"
|
||||||
(onCreate)="notificationCreated($event)"
|
(onCreate)="notificationCreated($event)"
|
||||||
(onDestroy)="notificationDestroyed($event)">
|
(onDestroy)="notificationDestroyed($event)">
|
||||||
</ds-notifications-board>
|
</ds-notifications-board>
|
||||||
<p>Fine notifiche</p>
|
|
||||||
|
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
@@ -30,10 +30,11 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
public notificationOptions: Options = {
|
public notificationOptions: Options = {
|
||||||
position: ['top', 'right'],
|
position: ['top', 'right'],
|
||||||
timeOut: 5000,
|
timeOut: 0,
|
||||||
lastOnBottom: true,
|
animate: 'fromLeft'
|
||||||
clickIconToClose: true,
|
// lastOnBottom: true,
|
||||||
showProgressBar: true
|
// clickIconToClose: false,
|
||||||
|
// showProgressBar: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
notificationCreated(event) {
|
notificationCreated(event) {
|
||||||
|
@@ -2,15 +2,15 @@ import {Icons} from './icons';
|
|||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
timeOut?: number;
|
timeOut?: number;
|
||||||
showProgressBar?: boolean;
|
// showProgressBar?: boolean;
|
||||||
pauseOnHover?: boolean;
|
// pauseOnHover?: boolean;
|
||||||
lastOnBottom?: boolean;
|
// lastOnBottom?: boolean;
|
||||||
clickToClose?: boolean;
|
// clickToClose?: boolean;
|
||||||
clickIconToClose?: boolean;
|
// clickIconToClose?: boolean;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
maxStack?: number;
|
maxStack?: number;
|
||||||
preventDuplicates?: boolean;
|
// preventDuplicates?: boolean;
|
||||||
preventLastDuplicates?: boolean | string;
|
// preventLastDuplicates?: boolean | string;
|
||||||
theClass?: string;
|
theClass?: string;
|
||||||
rtl?: boolean;
|
rtl?: boolean;
|
||||||
animate?: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale';
|
animate?: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale';
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
<div class="simple-notification"
|
<div class="alert" role="alert"
|
||||||
[@enterLeave]="item.state"
|
[@enterLeave]="item.state"
|
||||||
(click)="onClick($event)"
|
(click)="onClick($event)"
|
||||||
[class]="theClass"
|
[class]="theClass"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'alert': item.type === 'alert',
|
'alert-info': item.type === 'info',
|
||||||
'error': item.type === 'error',
|
'alert-danger': item.type === 'error',
|
||||||
'warn': item.type === 'warn',
|
'alert-warning': item.type === 'warn',
|
||||||
'success': item.type === 'success',
|
'alert-success': item.type === 'success',
|
||||||
'info': item.type === 'info',
|
|
||||||
'bare': item.type === 'bare',
|
'bare': item.type === 'bare',
|
||||||
'rtl-mode': rtl,
|
'rtl-mode': rtl,
|
||||||
'has-icon': item.icon !== 'bare'
|
'has-icon': item.icon !== 'bare'
|
||||||
@@ -33,7 +32,16 @@
|
|||||||
<div class="sn-content" [innerHTML]="content"></div>
|
<div class="sn-content" [innerHTML]="content"></div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="icon" *ngIf="item.icon !== 'bare'" [innerHTML]="safeSvg"></div>
|
<!--<div class="icon" *ngIf="item.icon !== 'bare'" [innerHTML]="safeSvg"></div>-->
|
||||||
|
{{icon}}
|
||||||
|
<div class="icon"><i
|
||||||
|
[ngClass]="{'fa': true,
|
||||||
|
'fa-check': icon == 'success',
|
||||||
|
'fa-times-circle': icon == 'error',
|
||||||
|
'fa-exclamation-triangle': icon == 'warn',
|
||||||
|
'fa-info': icon == 'info'
|
||||||
|
}"></i></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="item.html">
|
<div *ngIf="item.html">
|
||||||
<div class="sn-html" *ngIf="htmlIsTemplate; else regularHtml">
|
<div class="sn-html" *ngIf="htmlIsTemplate; else regularHtml">
|
||||||
@@ -44,11 +52,21 @@
|
|||||||
<div class="sn-content" [innerHTML]="item.html"></div>
|
<div class="sn-content" [innerHTML]="item.html"></div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="icon" [class.icon-hover]="clickIconToClose" *ngIf="item.icon" [innerHTML]="safeSvg" (click)="onClickIcon($event)"></div>
|
<!--<div class="icon" [class.icon-hover]="clickIconToClose" *ngIf="item.icon" [innerHTML]="safeSvg" (click)="onClickIcon($event)"></div>-->
|
||||||
|
{{icon}}
|
||||||
|
<div class="icon"><i
|
||||||
|
[ngClass]="{'fa': true,
|
||||||
|
'fa-check': icon == 'success',
|
||||||
|
'fa-times-circle': icon == 'error',
|
||||||
|
'fa-exclamation-triangle': icon == 'warning',
|
||||||
|
'fa-info': icon == 'info'
|
||||||
|
}"></i></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sn-progress-loader" *ngIf="showProgressBar">
|
<div class="sn-progress-loader" *ngIf="showProgressBar">
|
||||||
<span [ngStyle]="{'width': progressWidth + '%'}"></span>
|
<span [ngStyle]="{'width': progressWidth + '%'}"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="close"><i class="fa fa-times" (click)="remove()"></i></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,80 +1,80 @@
|
|||||||
.simple-notification {
|
//.simple-notification {
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
padding: 10px 20px;
|
// padding: 10px 20px;
|
||||||
box-sizing: border-box;
|
// box-sizing: border-box;
|
||||||
position: relative;
|
// position: relative;
|
||||||
float: left;
|
// float: left;
|
||||||
margin-bottom: 10px;
|
// margin-bottom: 10px;
|
||||||
color: #fff;
|
// color: #fff;
|
||||||
cursor: pointer;
|
// cursor: pointer;
|
||||||
transition: all 0.5s;
|
// transition: all 0.5s;
|
||||||
min-height: 70px;
|
// min-height: 70px;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .sn-title,
|
//.simple-notification .sn-title,
|
||||||
.simple-notification .sn-content,
|
//.simple-notification .sn-content,
|
||||||
.simple-notification .sn-html {
|
//.simple-notification .sn-html {
|
||||||
margin: 0;
|
// margin: 0;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .sn-title {
|
//.simple-notification .sn-title {
|
||||||
line-height: 30px;
|
// line-height: 30px;
|
||||||
font-size: 20px;
|
// font-size: 20px;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .sn-content {
|
//.simple-notification .sn-content {
|
||||||
font-size: 16px;
|
// font-size: 16px;
|
||||||
line-height: 20px;
|
// line-height: 20px;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification.has-icon .sn-title,
|
//.simple-notification.has-icon .sn-title,
|
||||||
.simple-notification.has-icon .sn-content,
|
//.simple-notification.has-icon .sn-content,
|
||||||
.simple-notification.has-icon .sn-html {
|
//.simple-notification.has-icon .sn-html {
|
||||||
padding: 0 50px 0 0;
|
// padding: 0 50px 0 0;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .icon {
|
//.simple-notification {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
box-sizing: border-box;
|
// box-sizing: border-box;
|
||||||
top: 0;
|
// top: 0;
|
||||||
right: 0;
|
// right: 0;
|
||||||
width: 70px;
|
// width: 70px;
|
||||||
height: 70px;
|
// height: 70px;
|
||||||
padding: 10px;
|
// padding: 10px;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .icon.icon-hover:hover {
|
//.simple-notification .icon.icon-hover:hover {
|
||||||
opacity: 0.5;
|
// opacity: 0.5;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .icon svg {
|
//.simple-notification .icon svg {
|
||||||
fill: #fff;
|
// fill: #fff;
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .icon svg g {
|
//.simple-notification .icon svg g {
|
||||||
fill: #fff;
|
// fill: #fff;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification.rtl-mode.has-icon .sn-title,
|
//.simple-notification.rtl-mode.has-icon .sn-title,
|
||||||
.simple-notification.rtl-mode.has-icon .sn-content,
|
//.simple-notification.rtl-mode.has-icon .sn-content,
|
||||||
.simple-notification.rtl-mode.has-icon .sn-html {
|
//.simple-notification.rtl-mode.has-icon .sn-html {
|
||||||
padding: 0 0 0 50px;
|
// padding: 0 0 0 50px;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification.rtl-mode {
|
//.simple-notification.rtl-mode {
|
||||||
direction: rtl;
|
// direction: rtl;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification.rtl-mode .sn-content {
|
//.simple-notification.rtl-mode .sn-content {
|
||||||
padding: 0 0 0 50px;
|
// padding: 0 0 0 50px;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification.rtl-mode svg {
|
//.simple-notification.rtl-mode svg {
|
||||||
left: 0;
|
// left: 0;
|
||||||
right: auto;
|
// right: auto;
|
||||||
}
|
//}
|
||||||
|
|
||||||
.simple-notification.error { background: #F44336; }
|
.simple-notification.error { background: #F44336; }
|
||||||
.simple-notification.success { background: #8BC34A; }
|
.simple-notification.success { background: #8BC34A; }
|
||||||
@@ -82,18 +82,18 @@
|
|||||||
.simple-notification.info { background: #03A9F4; }
|
.simple-notification.info { background: #03A9F4; }
|
||||||
.simple-notification.warn { background: #ffdb5b; }
|
.simple-notification.warn { background: #ffdb5b; }
|
||||||
|
|
||||||
.simple-notification .sn-progress-loader {
|
//.simple-notification .sn-progress-loader {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 0;
|
// top: 0;
|
||||||
left: 0;
|
// left: 0;
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
height: 5px;
|
// height: 5px;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.simple-notification .sn-progress-loader span {
|
//.simple-notification .sn-progress-loader span {
|
||||||
float: left;
|
// float: left;
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
}
|
//}
|
||||||
|
|
||||||
.simple-notification.success .sn-progress-loader span { background: #689F38; }
|
.simple-notification.success .sn-progress-loader span { background: #689F38; }
|
||||||
.simple-notification.error .sn-progress-loader span { background: #D32F2F; }
|
.simple-notification.error .sn-progress-loader span { background: #D32F2F; }
|
||||||
@@ -102,6 +102,42 @@
|
|||||||
.simple-notification.warn .sn-progress-loader span { background: #edc242; }
|
.simple-notification.warn .sn-progress-loader span { background: #edc242; }
|
||||||
.simple-notification.bare .sn-progress-loader span { background: #ccc; }
|
.simple-notification.bare .sn-progress-loader span { background: #ccc; }
|
||||||
|
|
||||||
.simple-notification.warn div .sn-title,
|
//.simple-notification.warn div .sn-title,
|
||||||
.simple-notification.warn div .sn-content,
|
//.simple-notification.warn div .sn-content,
|
||||||
.simple-notification.warn div .sn-html { color: #444; }
|
//.simple-notification.warn div .sn-html { color: #444; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
//.icon > fa-times-circle {
|
||||||
|
// color: red;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//.icon > fa-exclamation-triangle {
|
||||||
|
// color: orange;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//.icon > fa-info {
|
||||||
|
// color: white;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//.icon > fa-check {
|
||||||
|
// color: forestgreen;
|
||||||
|
//}
|
||||||
|
|
||||||
|
.sn-title, .sn-content, .sn-html {
|
||||||
|
position: relative;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
@@ -113,12 +113,12 @@ import { NotificationsService } from '../notifications.service';
|
|||||||
export class NotificationComponent implements OnInit, OnDestroy {
|
export class NotificationComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@Input() public timeOut: number;
|
@Input() public timeOut: number;
|
||||||
@Input() public showProgressBar: boolean;
|
// @Input() public showProgressBar: boolean;
|
||||||
@Input() public pauseOnHover: boolean;
|
// @Input() public pauseOnHover: boolean;
|
||||||
@Input() public clickToClose: boolean;
|
// @Input() public clickToClose: boolean;
|
||||||
@Input() public clickIconToClose: boolean;
|
// @Input() public clickIconToClose: boolean;
|
||||||
@Input() public maxLength: number;
|
// @Input() public maxLength: number;
|
||||||
@Input() public theClass: string;
|
// @Input() public theClass: string;
|
||||||
@Input() public rtl: boolean;
|
@Input() public rtl: boolean;
|
||||||
@Input() public animate: string;
|
@Input() public animate: string;
|
||||||
@Input() public position: number;
|
@Input() public position: number;
|
||||||
@@ -179,32 +179,32 @@ export class NotificationComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEnter(): void {
|
onEnter(): void {
|
||||||
if (this.pauseOnHover) {
|
// if (this.pauseOnHover) {
|
||||||
this.stopTime = true;
|
// this.stopTime = true;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
onLeave(): void {
|
onLeave(): void {
|
||||||
if (this.pauseOnHover) {
|
// if (this.pauseOnHover) {
|
||||||
this.stopTime = false;
|
// this.stopTime = false;
|
||||||
this.zone.runOutsideAngular(() => setTimeout(this.instance, (this.speed - this.diff)));
|
// this.zone.runOutsideAngular(() => setTimeout(this.instance, (this.speed - this.diff)));
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick($e: MouseEvent): void {
|
onClick($e: MouseEvent): void {
|
||||||
this.item.click!.emit($e);
|
this.item.click!.emit($e);
|
||||||
|
|
||||||
if (this.clickToClose) {
|
// if (this.clickToClose) {
|
||||||
this.remove();
|
// this.remove();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickIcon($e: MouseEvent): void {
|
onClickIcon($e: MouseEvent): void {
|
||||||
this.item.clickIcon!.emit($e);
|
this.item.clickIcon!.emit($e);
|
||||||
|
|
||||||
if (this.clickIconToClose) {
|
// if (this.clickIconToClose) {
|
||||||
this.remove();
|
// this.remove();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach all the overrides
|
// Attach all the overrides
|
||||||
@@ -227,9 +227,9 @@ export class NotificationComponent implements OnInit, OnDestroy {
|
|||||||
this.remove();
|
this.remove();
|
||||||
this.item.timeoutEnd!.emit();
|
this.item.timeoutEnd!.emit();
|
||||||
} else if (!this.stopTime) {
|
} else if (!this.stopTime) {
|
||||||
if (this.showProgressBar) {
|
// if (this.showProgressBar) {
|
||||||
this.progressWidth += 100 / this.steps;
|
// this.progressWidth += 100 / this.steps;
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.timer = setTimeout(this.instance, (this.speed - this.diff));
|
this.timer = setTimeout(this.instance, (this.speed - this.diff));
|
||||||
}
|
}
|
||||||
|
@@ -3,14 +3,15 @@
|
|||||||
*ngFor="let a of notifications; let i = index"
|
*ngFor="let a of notifications; let i = index"
|
||||||
[item]="a"
|
[item]="a"
|
||||||
[timeOut]="timeOut"
|
[timeOut]="timeOut"
|
||||||
[clickToClose]="clickToClose"
|
|
||||||
[clickIconToClose]="clickIconToClose"
|
|
||||||
[maxLength]="maxLength"
|
|
||||||
[showProgressBar]="showProgressBar"
|
|
||||||
[pauseOnHover]="pauseOnHover"
|
|
||||||
[theClass]="theClass"
|
|
||||||
[rtl]="rtl"
|
[rtl]="rtl"
|
||||||
[animate]="animate"
|
[animate]="animate"
|
||||||
[position]="i">
|
[position]="i">
|
||||||
|
|
||||||
|
<!--[clickToClose]="clickToClose"-->
|
||||||
|
<!--[clickIconToClose]="clickIconToClose"-->
|
||||||
|
<!--[maxLength]="maxLength"-->
|
||||||
|
<!--[showProgressBar]="showProgressBar"-->
|
||||||
|
<!--[pauseOnHover]="pauseOnHover"-->
|
||||||
|
<!--[theClass]="theClass"-->
|
||||||
</ds-notification>
|
</ds-notification>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -37,12 +37,12 @@ export class NotificationsBoardComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Sent values
|
// Sent values
|
||||||
public timeOut = 0;
|
public timeOut = 0;
|
||||||
public maxLength = 0;
|
// public maxLength = 0;
|
||||||
public clickToClose = true;
|
// public clickToClose = true;
|
||||||
public clickIconToClose = false;
|
// public clickIconToClose = false;
|
||||||
public showProgressBar = true;
|
// public showProgressBar = true;
|
||||||
public pauseOnHover = true;
|
// public pauseOnHover = true;
|
||||||
public theClass = '';
|
// public theClass = '';
|
||||||
public rtl = false;
|
public rtl = false;
|
||||||
public animate: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' = 'fromRight';
|
public animate: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' = 'fromRight';
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ export class NotificationsBoardComponent implements OnInit, OnDestroy {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Listen for changes in the service
|
// Listen for changes in the service
|
||||||
this.listener = this.service.emitter
|
this.listener = this.service.emitter
|
||||||
.subscribe((item) => {
|
.subscribe((item) => { // Subscribe a stato di redux
|
||||||
switch (item.command) {
|
switch (item.command) {
|
||||||
case 'cleanAll':
|
case 'cleanAll':
|
||||||
this.notifications = [];
|
this.notifications = [];
|
||||||
|
Reference in New Issue
Block a user