mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-01-10 12:35:07 +00:00
mail..
This commit is contained in:
parent
2e4d040139
commit
b9569a5ba8
|
@ -3,16 +3,4 @@
|
||||||
#main-navigation {
|
#main-navigation {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
background-color: map-get($background, raised-button);
|
|
||||||
color: map_get($foreground, text);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: map-get($background, hover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import {MaterialModule} from './material.module';
|
|
||||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { MaterialModule } from './material.module';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FuseMdSidenavHelperDirective,
|
FuseMdSidenavHelperDirective,
|
||||||
FuseMdSidenavTogglerDirective
|
FuseMdSidenavTogglerDirective
|
||||||
} from '../directives/md-sidenav-helper/md-sidenav-helper.directive';
|
} from '../directives/md-sidenav-helper/md-sidenav-helper.directive';
|
||||||
import {PerfectScrollbarModule} from 'ngx-perfect-scrollbar';
|
import { FusePipesModule } from '../pipes/pipes.module';
|
||||||
import {KeysPipe} from '../pipes/keys';
|
|
||||||
import {HtmlToPlaintextPipe} from '../pipes//htmlToPlaintext';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
FuseMdSidenavHelperDirective,
|
FuseMdSidenavHelperDirective,
|
||||||
FuseMdSidenavTogglerDirective,
|
FuseMdSidenavTogglerDirective
|
||||||
KeysPipe,
|
|
||||||
HtmlToPlaintextPipe
|
|
||||||
],
|
],
|
||||||
imports : [
|
imports : [
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
MaterialModule,
|
MaterialModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
FusePipesModule,
|
||||||
PerfectScrollbarModule
|
PerfectScrollbarModule
|
||||||
],
|
],
|
||||||
exports : [
|
exports : [
|
||||||
|
@ -32,9 +32,8 @@ import {HtmlToPlaintextPipe} from '../pipes//htmlToPlaintext';
|
||||||
FormsModule,
|
FormsModule,
|
||||||
FuseMdSidenavHelperDirective,
|
FuseMdSidenavHelperDirective,
|
||||||
FuseMdSidenavTogglerDirective,
|
FuseMdSidenavTogglerDirective,
|
||||||
PerfectScrollbarModule,
|
FusePipesModule,
|
||||||
KeysPipe,
|
PerfectScrollbarModule
|
||||||
HtmlToPlaintextPipe
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
22
src/app/core/pipes/getById.pipe.ts
Normal file
22
src/app/core/pipes/getById.pipe.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({name: 'getById'})
|
||||||
|
export class GetByIdPipe implements PipeTransform
|
||||||
|
{
|
||||||
|
transform(value: any[], id: number, property: string ): any
|
||||||
|
{
|
||||||
|
const foundItem = value.find(item => {
|
||||||
|
if ( item.id )
|
||||||
|
{
|
||||||
|
return item.id === id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( foundItem )
|
||||||
|
{
|
||||||
|
return foundItem[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/app/core/pipes/keys.pipe.ts
Normal file
23
src/app/core/pipes/keys.pipe.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({name: 'keys'})
|
||||||
|
export class KeysPipe implements PipeTransform
|
||||||
|
{
|
||||||
|
transform(value: any, args: string[]): any
|
||||||
|
{
|
||||||
|
const keys: any[] = [];
|
||||||
|
|
||||||
|
for ( const key in value )
|
||||||
|
{
|
||||||
|
if ( value.hasOwnProperty(key) )
|
||||||
|
{
|
||||||
|
keys.push({
|
||||||
|
key : key,
|
||||||
|
value: value[key]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
|
||||||
|
|
||||||
@Pipe({name: 'keys'})
|
|
||||||
export class KeysPipe implements PipeTransform
|
|
||||||
{
|
|
||||||
transform(value: any, args: string[]): any
|
|
||||||
{
|
|
||||||
let keys: any[] = [];
|
|
||||||
for ( let key in value )
|
|
||||||
{
|
|
||||||
keys.push({key: key, value: value[key]});
|
|
||||||
}
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
}
|
|
27
src/app/core/pipes/pipes.module.ts
Normal file
27
src/app/core/pipes/pipes.module.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { KeysPipe } from './keys.pipe';
|
||||||
|
import { GetByIdPipe } from './getById.pipe';
|
||||||
|
import { HtmlToPlaintextPipe } from './htmlToPlaintext.pipe';
|
||||||
|
import { SearchPipe } from './search.pipe';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
KeysPipe,
|
||||||
|
GetByIdPipe,
|
||||||
|
HtmlToPlaintextPipe,
|
||||||
|
SearchPipe
|
||||||
|
],
|
||||||
|
imports : [],
|
||||||
|
exports : [
|
||||||
|
KeysPipe,
|
||||||
|
GetByIdPipe,
|
||||||
|
HtmlToPlaintextPipe,
|
||||||
|
SearchPipe
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class FusePipesModule
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
49
src/app/core/pipes/search.pipe.ts
Normal file
49
src/app/core/pipes/search.pipe.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'filter'
|
||||||
|
})
|
||||||
|
export class SearchPipe implements PipeTransform
|
||||||
|
{
|
||||||
|
transform(items: any, term: string): any
|
||||||
|
{
|
||||||
|
if ( term === undefined )
|
||||||
|
{
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.filter(items, term);
|
||||||
|
}
|
||||||
|
|
||||||
|
filter(items: any, term: string)
|
||||||
|
{
|
||||||
|
return items.filter(item => {
|
||||||
|
|
||||||
|
for ( const property in item )
|
||||||
|
{
|
||||||
|
if ( !item.hasOwnProperty(property) || !item[property] )
|
||||||
|
{
|
||||||
|
console.log('continueing...');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(item[property]) )
|
||||||
|
{
|
||||||
|
console.log('is Array');
|
||||||
|
return this.filter(item[property], term);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( item[property].toString().toLowerCase().includes(term.toLowerCase()) )
|
||||||
|
{
|
||||||
|
console.log('found!');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
<div *ngIf="!mail" fxLayout="column" fxLayoutAlign="center center" fxFlex>
|
||||||
|
<md-icon>email</md-icon>
|
||||||
|
<span class="hint-text">Select a message to read</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="mail">
|
<div *ngIf="mail">
|
||||||
|
|
||||||
<div class="mail-header" fxLayout="row" fxLayoutAlign="space-between center">
|
<div class="mail-header" fxLayout="row" fxLayoutAlign="space-between center">
|
||||||
|
@ -6,8 +11,8 @@
|
||||||
<div class="subject" flex>{{mail.subject}}</div>
|
<div class="subject" flex>{{mail.subject}}</div>
|
||||||
|
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div class="label" *ngFor="let labelId of mail.labels">
|
<div class="label" *ngFor="let labelId of mail.labels"
|
||||||
{{labelId}}
|
[ngStyle]="{background: labels | getById:labelId:'color'}">{{labels | getById:labelId:'title'}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -128,8 +133,8 @@
|
||||||
<img class="preview" src="{{attachment.preview}}">
|
<img class="preview" src="{{attachment.preview}}">
|
||||||
|
|
||||||
<div fxLayout="column">
|
<div fxLayout="column">
|
||||||
<a href="#" class="md-accent-color link">View</a>
|
<a href="#" class="md-accent-color link" onclick="event.preventDefault()">View</a>
|
||||||
<a href="#" class="md-accent-color link">Download</a>
|
<a href="#" class="md-accent-color link" onclick="event.preventDefault()">Download</a>
|
||||||
<div class="size">({{attachment.size}})</div>
|
<div class="size">({{attachment.size}})</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
@import 'src/app/core/scss/fuse';
|
@import 'src/app/core/scss/fuse';
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
background: #FFFFFF;
|
background: #FFFFFF;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
|
||||||
|
@ -10,12 +13,6 @@
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
min-width: 88px;
|
min-width: 88px;
|
||||||
|
|
||||||
.mat-icon-button {
|
|
||||||
padding: 0;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.subject {
|
.subject {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Mail } from '../mail.model';
|
import { Mail } from '../mail.model';
|
||||||
import { MailService } from '../mail.service';
|
import { MailService } from '../mail.service';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector : 'fuse-mail-details',
|
selector : 'fuse-mail-details',
|
||||||
|
@ -12,9 +11,9 @@ export class MailDetailsComponent implements OnInit
|
||||||
{
|
{
|
||||||
@Input('selectedMail') public mail: Mail;
|
@Input('selectedMail') public mail: Mail;
|
||||||
showDetails: boolean;
|
showDetails: boolean;
|
||||||
|
labels: any[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
|
||||||
private mailService: MailService
|
private mailService: MailService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -23,7 +22,7 @@ export class MailDetailsComponent implements OnInit
|
||||||
|
|
||||||
ngOnInit()
|
ngOnInit()
|
||||||
{
|
{
|
||||||
|
this.labels = this.mailService.labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleStar(event)
|
toggleStar(event)
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
{{mail.message | htmlToPlaintext | slice:0:180}}{{mail.message.length > 180 ? '...' : ''}}
|
{{mail.message | htmlToPlaintext | slice:0:180}}{{mail.message.length > 180 ? '...' : ''}}
|
||||||
|
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div class="label" *ngFor="let labelId of mail.labels">{{labelId}}</div>
|
<div class="label" *ngFor="let labelId of mail.labels"
|
||||||
|
[ngStyle]="{background: labels | getById:labelId:'color'}">{{labels | getById:labelId:'title'}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Mail } from '../../mail.model';
|
import { Mail } from '../../mail.model';
|
||||||
import { MailService } from '../../mail.service';
|
import { MailService } from '../../mail.service';
|
||||||
import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector : 'fuse-mail-list-item',
|
selector : 'fuse-mail-list-item',
|
||||||
|
@ -11,6 +11,7 @@ import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router';
|
||||||
export class MailListItemComponent implements OnInit
|
export class MailListItemComponent implements OnInit
|
||||||
{
|
{
|
||||||
@Input() mail: Mail;
|
@Input() mail: Mail;
|
||||||
|
labels: any[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
@ -23,6 +24,7 @@ export class MailListItemComponent implements OnInit
|
||||||
ngOnInit()
|
ngOnInit()
|
||||||
{
|
{
|
||||||
this.mail = new Mail(this.mail);
|
this.mail = new Mail(this.mail);
|
||||||
|
this.labels = this.mailService.labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleStar(event)
|
toggleStar(event)
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
<fuse-mail-list-item *ngFor="let mail of mails" [mail]="mail" md-ripple (click)="selectMail(mail.id)"
|
<div *ngIf="mails.length === 0" fxLayout="column" fxLayoutAlign="center center" fxFlex>
|
||||||
|
<span class="hint-text">There are no messages!</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="text" [(ngModel)]="search">
|
||||||
|
|
||||||
|
{{search}}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<fuse-mail-list-item *ngFor="let mail of mails | filter:search" [mail]="mail" md-ripple (click)="selectMail(mail.id)"
|
||||||
[ngClass]="{'selected-mail':mail?.id === selectedMail?.id}">
|
[ngClass]="{'selected-mail':mail?.id === selectedMail?.id}">
|
||||||
</fuse-mail-list-item>
|
</fuse-mail-list-item>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Mail } from '../mail.model';
|
import { Mail } from '../mail.model';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { MailService } from '../mail.service';
|
import { MailService } from '../mail.service';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector : 'fuse-mail-list',
|
selector : 'fuse-mail-list',
|
||||||
|
@ -14,10 +15,12 @@ export class MailListComponent implements OnInit
|
||||||
|
|
||||||
@Input('selectedMail') public selectedMail: Mail;
|
@Input('selectedMail') public selectedMail: Mail;
|
||||||
|
|
||||||
|
search: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private mailService: MailService,
|
||||||
private mailService: MailService
|
private location: Location
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -30,9 +33,26 @@ export class MailListComponent implements OnInit
|
||||||
// Subscribe to update mails on changes
|
// Subscribe to update mails on changes
|
||||||
this.mailService.onMailsUpdated
|
this.mailService.onMailsUpdated
|
||||||
.subscribe(mails => {
|
.subscribe(mails => {
|
||||||
console.log('mailsUpdated');
|
|
||||||
this.mails = mails;
|
this.mails = mails;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.mailService.onSelectedMailUpdated
|
||||||
|
.subscribe(selectedMail => {
|
||||||
|
if ( !selectedMail )
|
||||||
|
{
|
||||||
|
const labelHandle = this.route.snapshot.params.labelHandle,
|
||||||
|
folderHandle = this.route.snapshot.params.folderHandle;
|
||||||
|
|
||||||
|
if ( labelHandle )
|
||||||
|
{
|
||||||
|
this.location.go('apps/mail/label/' + labelHandle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.location.go('apps/mail/' + folderHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
selectMail(mailId)
|
selectMail(mailId)
|
||||||
|
@ -42,12 +62,15 @@ export class MailListComponent implements OnInit
|
||||||
|
|
||||||
if ( labelHandle )
|
if ( labelHandle )
|
||||||
{
|
{
|
||||||
this.router.navigate(['apps/mail/label', labelHandle, mailId]);
|
this.location.go('apps/mail/label/' + labelHandle + '/' + mailId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.router.navigate(['apps/mail', folderHandle, mailId]);
|
this.location.go('apps/mail/' + folderHandle + '/' + mailId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set selected mail
|
||||||
|
this.mailService.setSelectedMail(mailId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,13 +47,13 @@
|
||||||
<!-- / CONTENT TOOLBAR -->
|
<!-- / CONTENT TOOLBAR -->
|
||||||
|
|
||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<div class="content" perfect-scrollbar>
|
<div class="content">
|
||||||
|
|
||||||
<div fxLayout="row" fxFill>
|
<div fxLayout="row" fxFill>
|
||||||
|
|
||||||
<fuse-mail-list fxFlex [selectedMail]="selectedMail"></fuse-mail-list>
|
<fuse-mail-list fxFlex [selectedMail]="selectedMail" perfect-scrollbar></fuse-mail-list>
|
||||||
|
|
||||||
<fuse-mail-details fxFlex [selectedMail]="selectedMail"></fuse-mail-details>
|
<fuse-mail-details fxFlex [selectedMail]="selectedMail" perfect-scrollbar></fuse-mail-details>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -177,9 +177,8 @@ export class MailService implements Resolve<any>
|
||||||
update(mail)
|
update(mail)
|
||||||
{
|
{
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.http.post('api/mail-mails/' + mail.id, {...mail}).subscribe(response => {
|
|
||||||
|
|
||||||
console.log(response);
|
this.http.post('api/mail-mails/' + mail.id, {...mail}).subscribe(response => {
|
||||||
|
|
||||||
this.getMails().then(mails => {
|
this.getMails().then(mails => {
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<div class="nav-subheader">FOLDERS</div>
|
<div class="nav-subheader">FOLDERS</div>
|
||||||
|
|
||||||
<div class="nav-item" *ngFor="let item of folders">
|
<div class="nav-item" *ngFor="let item of folders">
|
||||||
<a class="nav-link" md-ripple [routerLink]="'/apps/mail/' + item.handle">
|
<a class="nav-link" md-ripple [routerLink]="'/apps/mail/' + item.handle" routerLinkActive="active">
|
||||||
<md-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</md-icon>
|
<md-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</md-icon>
|
||||||
<span>{{item.title}}</span>
|
<span>{{item.title}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
<div class="nav-subheader">LABELS</div>
|
<div class="nav-subheader">LABELS</div>
|
||||||
|
|
||||||
<div class="nav-item" *ngFor="let item of labels">
|
<div class="nav-item" *ngFor="let item of labels">
|
||||||
<a class="nav-link" md-ripple [routerLink]="'/apps/mail/label/' + item.handle">
|
<a class="nav-link" md-ripple [routerLink]="'/apps/mail/label/' + item.handle" routerLinkActive="active">
|
||||||
<md-icon class="nav-link-icon" [ngStyle]="{'color':item.color}">label</md-icon>
|
<md-icon class="nav-link-icon" [ngStyle]="{'color':item.color}">label</md-icon>
|
||||||
<span>{{item.title}}</span>
|
<span>{{item.title}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user