mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(angular): ion-nav no routing
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { ComponentFactoryResolver, Directive, ElementRef, Injector} from '@angular/core';
|
||||
import { NavigationEnd, NavigationStart, Router } from '@angular/router';
|
||||
|
||||
import { FrameworkDelegate, NavOptions, NavParams, TransitionDoneFn, ViewController } from '@ionic/core';
|
||||
import { NavOptions, NavParams, TransitionDoneFn, ViewController } from '@ionic/core';
|
||||
import { proxyEl } from '../util/util';
|
||||
import { AngularDelegate } from '..';
|
||||
|
||||
@@ -11,50 +10,37 @@ import { AngularDelegate } from '..';
|
||||
})
|
||||
export class IonNav {
|
||||
|
||||
private delegate: FrameworkDelegate;
|
||||
|
||||
constructor(
|
||||
private ref: ElementRef,
|
||||
router: Router,
|
||||
cfr: ComponentFactoryResolver,
|
||||
injector: Injector,
|
||||
angularDelegate: AngularDelegate,
|
||||
) {
|
||||
this.delegate = angularDelegate.create(cfr, injector);
|
||||
console.log('ion-nav');
|
||||
|
||||
router.events.subscribe(ev => {
|
||||
if (ev instanceof NavigationStart) {
|
||||
console.log('NavigationStart', ev.url);
|
||||
|
||||
} else if (ev instanceof NavigationEnd) {
|
||||
console.log('NavigationEnd', ev.url);
|
||||
}
|
||||
});
|
||||
this.ref.nativeElement.delegate = angularDelegate.create(cfr, injector);
|
||||
}
|
||||
|
||||
push(page: any, params?: NavParams, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'push', page, params, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'push', page, params, opts, done);
|
||||
}
|
||||
|
||||
insert(insertIndex: number, page: any, params?: NavParams, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'insert', insertIndex, page, params, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'insert', insertIndex, page, params, opts, done);
|
||||
}
|
||||
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'insertPages', insertIndex, insertPages, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'insertPages', insertIndex, insertPages, opts, done);
|
||||
}
|
||||
|
||||
pop(opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'pop', this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'pop', opts, done);
|
||||
}
|
||||
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'popTo', indexOrViewCtrl, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'popTo', indexOrViewCtrl, opts, done);
|
||||
}
|
||||
|
||||
popToRoot(opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'popToRoot', this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'popToRoot', opts, done);
|
||||
}
|
||||
|
||||
popAll(): Promise<boolean[]> {
|
||||
@@ -62,19 +48,19 @@ export class IonNav {
|
||||
}
|
||||
|
||||
removeIndex(startIndex: number, removeCount = 1, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'removeIndex', startIndex, removeCount, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'removeIndex', startIndex, removeCount, opts, done);
|
||||
}
|
||||
|
||||
removeView(viewController: ViewController, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'removeView', viewController, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'removeView', viewController, opts, done);
|
||||
}
|
||||
|
||||
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'setRoot', pageOrViewCtrl, params, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'setRoot', pageOrViewCtrl, params, opts, done);
|
||||
}
|
||||
|
||||
setPages(pages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return proxyEl(this.ref, 'setPages', pages, this.wrap(opts), done);
|
||||
return proxyEl(this.ref, 'setPages', pages, opts, done);
|
||||
}
|
||||
|
||||
getAllChildNavs(): any[] {
|
||||
@@ -114,12 +100,4 @@ export class IonNav {
|
||||
length() {
|
||||
return proxyEl(this.ref, 'length');
|
||||
}
|
||||
|
||||
private wrap(opts?: NavOptions): NavOptions {
|
||||
return {
|
||||
...this.wrap(opts),
|
||||
delegate: this.delegate
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,9 +53,9 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
}
|
||||
|
||||
removeViewFromDom(_container: any, component: any): Promise<void> {
|
||||
const mountingData = this.elRefMap.get(component);
|
||||
if (mountingData) {
|
||||
mountingData.componentRef.destroy();
|
||||
const componentRef = this.elRefMap.get(component);
|
||||
if (componentRef) {
|
||||
componentRef.destroy();
|
||||
this.elRefMap.delete(component);
|
||||
}
|
||||
return Promise.resolve();
|
||||
|
||||
4
core/src/components.d.ts
vendored
4
core/src/components.d.ts
vendored
@@ -42,6 +42,9 @@ import {
|
||||
import {
|
||||
Side,
|
||||
} from './utils/helpers';
|
||||
import {
|
||||
FrameworkDelegate as FrameworkDelegate2,
|
||||
} from '.';
|
||||
import {
|
||||
PickerButton,
|
||||
PickerColumn as PickerColumn2,
|
||||
@@ -2010,6 +2013,7 @@ declare global {
|
||||
namespace JSXElements {
|
||||
export interface IonNavAttributes extends HTMLAttributes {
|
||||
animated?: boolean;
|
||||
delegate?: FrameworkDelegate;
|
||||
root?: any;
|
||||
rootParams?: any;
|
||||
swipeBackEnabled?: boolean;
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from './nav-util';
|
||||
|
||||
import { ViewController, isViewController } from './view-controller';
|
||||
import { Animation, Config, DomController, GestureDetail, NavOutlet } from '../..';
|
||||
import { Animation, Config, DomController, FrameworkDelegate, GestureDetail, NavOutlet } from '../..';
|
||||
import { RouteID, RouteWrite } from '../router/utils/interfaces';
|
||||
import { assert } from '../../utils/helpers';
|
||||
|
||||
@@ -51,6 +51,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: HTMLIonAnimationControllerElement;
|
||||
@Prop({ mutable: true }) swipeBackEnabled: boolean;
|
||||
@Prop({ mutable: true }) animated: boolean;
|
||||
@Prop() delegate: FrameworkDelegate;
|
||||
@Prop() rootParams: any;
|
||||
@Prop() root: any;
|
||||
@Watch('root')
|
||||
@@ -342,16 +343,11 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
this._init = true;
|
||||
|
||||
// ensure we're not transitioning here
|
||||
this.isTransitioning = false;
|
||||
// let's see if there's another to kick off
|
||||
this._nextTrns();
|
||||
const isPop = result.direction === NavDirection.back;
|
||||
if (this.useRouter) {
|
||||
const router = document.querySelector('ion-router');
|
||||
router && router.navChanged(isPop);
|
||||
}
|
||||
|
||||
this.ionNavChanged.emit({isPop});
|
||||
|
||||
if (ti.done) {
|
||||
@@ -372,11 +368,6 @@ export class NavControllerBase implements NavOutlet {
|
||||
return;
|
||||
}
|
||||
this._queue.length = 0;
|
||||
|
||||
// let's see if there's another to kick off
|
||||
this.isTransitioning = false;
|
||||
this._nextTrns();
|
||||
|
||||
this._fireError(rejectReason, ti);
|
||||
}
|
||||
|
||||
@@ -405,11 +396,16 @@ export class NavControllerBase implements NavOutlet {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set that this nav is actively transitioning
|
||||
this.isTransitioning = true;
|
||||
this.runTransition(ti);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async runTransition(ti: TransitionInstruction) {
|
||||
try {
|
||||
// set that this nav is actively transitioning
|
||||
this.isTransitioning = true;
|
||||
this._prepareTI(ti);
|
||||
|
||||
const leavingView = this.getActive();
|
||||
const enteringView = this._getEnteringView(ti, leavingView);
|
||||
|
||||
@@ -421,23 +417,28 @@ export class NavControllerBase implements NavOutlet {
|
||||
ti.requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView;
|
||||
|
||||
if (enteringView && enteringView._state === ViewState.New) {
|
||||
enteringView.init(this.el);
|
||||
await enteringView.init(this.el);
|
||||
}
|
||||
this._postViewInit(enteringView, leavingView, ti);
|
||||
|
||||
this._transition(enteringView, leavingView, ti)
|
||||
.then((result) => this._success(result, ti))
|
||||
.catch((rejectReason) => this._failed(rejectReason, ti));
|
||||
const result = await this._transition(enteringView, leavingView, ti);
|
||||
|
||||
this._success(result, ti);
|
||||
} catch (rejectReason) {
|
||||
this._failed(rejectReason, ti);
|
||||
}
|
||||
return true;
|
||||
this.isTransitioning = false;
|
||||
this._nextTrns();
|
||||
}
|
||||
|
||||
private _prepareTI(ti: TransitionInstruction) {
|
||||
const viewsLength = this._views.length;
|
||||
|
||||
ti.opts = ti.opts || {};
|
||||
|
||||
if (ti.opts.delegate === undefined) {
|
||||
ti.opts.delegate = this.delegate;
|
||||
}
|
||||
if (ti.removeView != null) {
|
||||
assert(isPresent(ti.removeStart), 'removeView needs removeStart');
|
||||
assert(isPresent(ti.removeCount), 'removeView needs removeCount');
|
||||
@@ -481,6 +482,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
// Check all the inserted view are correct
|
||||
for (let i = 0; i < viewControllers.length; i++) {
|
||||
const view = viewControllers[i];
|
||||
view.delegate = ti.opts.delegate;
|
||||
const nav = view._nav;
|
||||
if (nav && nav !== this) {
|
||||
throw new Error('inserted view was already inserted');
|
||||
@@ -519,7 +521,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
assert(ti.resolve, 'resolve must be valid');
|
||||
assert(ti.reject, 'reject must be valid');
|
||||
|
||||
const opts = ti.opts = ti.opts || {};
|
||||
const opts = ti.opts;
|
||||
const insertViews = ti.insertViews;
|
||||
const removeStart = ti.removeStart;
|
||||
const removeCount = ti.removeCount;
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
boolean
|
||||
|
||||
|
||||
#### delegate
|
||||
|
||||
|
||||
|
||||
|
||||
#### root
|
||||
|
||||
any
|
||||
@@ -34,6 +39,11 @@ boolean
|
||||
boolean
|
||||
|
||||
|
||||
#### delegate
|
||||
|
||||
|
||||
|
||||
|
||||
#### root
|
||||
|
||||
any
|
||||
|
||||
@@ -32,11 +32,11 @@ export class ViewController {
|
||||
/** @hidden */
|
||||
id: string;
|
||||
element: HTMLElement;
|
||||
delegate: FrameworkDelegate;
|
||||
|
||||
constructor(
|
||||
public component: any,
|
||||
public data: any,
|
||||
private delegate?: FrameworkDelegate,
|
||||
public data: any
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,9 @@ const routes: Routes = [
|
||||
{ path: 'popover', loadChildren: 'app/popover/popover.module#PopoverModule' },
|
||||
{ path: 'segment', loadChildren: 'app/segment/segment.module#SegmentModule' },
|
||||
{ path: 'virtual-scroll', loadChildren: 'app/virtual-scroll/virtual-scroll.module#VirtualScrollModule' },
|
||||
|
||||
{ path: 'no-routing-nav', loadChildren: 'app/no-routing-nav/no-routing-nav.module#NoRoutingNavModule' },
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
14
demos/angular/src/app/no-routing-nav/no-routing-nav-routing.module.ts
Executable file
14
demos/angular/src/app/no-routing-nav/no-routing-nav-routing.module.ts
Executable file
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { NoRoutingNavPageComponent } from './no-routing-nav.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: NoRoutingNavPageComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class NoRoutingNavRoutingModule { }
|
||||
20
demos/angular/src/app/no-routing-nav/no-routing-nav.component.ts
Executable file
20
demos/angular/src/app/no-routing-nav/no-routing-nav.component.ts
Executable file
@@ -0,0 +1,20 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { PageOne } from './pages/page-one';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nav-page',
|
||||
template: `
|
||||
<ion-app>
|
||||
<ion-nav [root]="pageOne"></ion-nav>
|
||||
</ion-app>
|
||||
`
|
||||
})
|
||||
export class NoRoutingNavPageComponent {
|
||||
|
||||
pageOne: any = PageOne;
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
31
demos/angular/src/app/no-routing-nav/no-routing-nav.module.ts
Executable file
31
demos/angular/src/app/no-routing-nav/no-routing-nav.module.ts
Executable file
@@ -0,0 +1,31 @@
|
||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { NoRoutingNavPageComponent } from './no-routing-nav.component';
|
||||
import { NoRoutingNavRoutingModule } from './no-routing-nav-routing.module';
|
||||
import { IonicAngularModule } from '@ionic/angular';
|
||||
|
||||
import { PageOne } from './pages/page-one';
|
||||
import { PageTwo } from './pages/page-two';
|
||||
import { PageThree } from './pages/page-three';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
NoRoutingNavRoutingModule,
|
||||
IonicAngularModule,
|
||||
],
|
||||
declarations: [
|
||||
NoRoutingNavPageComponent,
|
||||
PageOne,
|
||||
PageTwo,
|
||||
PageThree
|
||||
],
|
||||
entryComponents: [
|
||||
PageOne,
|
||||
PageTwo,
|
||||
PageThree
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class NoRoutingNavModule { }
|
||||
62
demos/angular/src/app/no-routing-nav/pages/page-one.ts
Executable file
62
demos/angular/src/app/no-routing-nav/pages/page-one.ts
Executable file
@@ -0,0 +1,62 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { PageTwo } from './page-two';
|
||||
|
||||
@Component({
|
||||
selector: 'page-one',
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Page One</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
Page One
|
||||
<div>
|
||||
<ion-button (click)="goToPageTwo()">Go to Page Two</ion-button>
|
||||
</div>
|
||||
<ul>
|
||||
<li>ngOnInit - {{ngOnInitDetection}}</li>
|
||||
<li>ionViewWillEnter - {{ionViewWillEnterDetection}}</li>
|
||||
<li>ionViewDidEnter - {{ionViewDidEnterDetection}}</li>
|
||||
</ul>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class PageOne {
|
||||
|
||||
ngOnInitDetection = 'initial';
|
||||
ionViewWillEnterDetection = 'initial';
|
||||
ionViewDidEnterDetection = 'initial';
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
console.log('page one ngOnInit');
|
||||
setInterval(() => {
|
||||
this.ngOnInitDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
console.log('page one ionViewWillEnter');
|
||||
setInterval(() => {
|
||||
this.ionViewWillEnterDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
ionViewDidEnter() {
|
||||
console.log('page one ionViewDidEnter');
|
||||
setInterval(() => {
|
||||
this.ionViewDidEnterDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
goToPageTwo() {
|
||||
const nav = document.querySelector('ion-nav') as any;
|
||||
nav.push(PageTwo).then(() => console.log('push complete'));
|
||||
}
|
||||
}
|
||||
63
demos/angular/src/app/no-routing-nav/pages/page-three.ts
Executable file
63
demos/angular/src/app/no-routing-nav/pages/page-three.ts
Executable file
@@ -0,0 +1,63 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'page-three',
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Page Three</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
Page Three
|
||||
<div>
|
||||
<ion-button (click)="goBack()">Go Back</ion-button>
|
||||
</div>
|
||||
<ul>
|
||||
<li>ngOnInit - {{ngOnInitDetection}}</li>
|
||||
<li>ionViewWillEnter - {{ionViewWillEnterDetection}}</li>
|
||||
<li>ionViewDidEnter - {{ionViewDidEnterDetection}}</li>
|
||||
</ul>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class PageThree {
|
||||
|
||||
ngOnInitDetection = 'initial';
|
||||
ionViewWillEnterDetection = 'initial';
|
||||
ionViewDidEnterDetection = 'initial';
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
console.log('page two ngOnInit');
|
||||
setInterval(() => {
|
||||
this.ngOnInitDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
console.log('page two ionViewWillEnter');
|
||||
setInterval(() => {
|
||||
this.ionViewWillEnterDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
ionViewDidEnter() {
|
||||
console.log('page two ionViewDidEnter');
|
||||
setInterval(() => {
|
||||
this.ionViewDidEnterDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
goBack() {
|
||||
const nav = document.querySelector('ion-nav') as any;
|
||||
nav.pop().then(() => console.log('pop complete'));
|
||||
}
|
||||
}
|
||||
73
demos/angular/src/app/no-routing-nav/pages/page-two.ts
Executable file
73
demos/angular/src/app/no-routing-nav/pages/page-two.ts
Executable file
@@ -0,0 +1,73 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { PageThree } from './page-three';
|
||||
|
||||
@Component({
|
||||
selector: 'page-two',
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Page Two</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
Page Two
|
||||
<div>
|
||||
<ion-button (click)="goNext()">Go to Page Three</ion-button>
|
||||
</div>
|
||||
<div>
|
||||
<ion-button (click)="goBack()">Go Back</ion-button>
|
||||
</div>
|
||||
<ul>
|
||||
<li>ngOnInit - {{ngOnInitDetection}}</li>
|
||||
<li>ionViewWillEnter - {{ionViewWillEnterDetection}}</li>
|
||||
<li>ionViewDidEnter - {{ionViewDidEnterDetection}}</li>
|
||||
</ul>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class PageTwo {
|
||||
|
||||
ngOnInitDetection = 'initial';
|
||||
ionViewWillEnterDetection = 'initial';
|
||||
ionViewDidEnterDetection = 'initial';
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
console.log('page two ngOnInit');
|
||||
setInterval(() => {
|
||||
this.ngOnInitDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
console.log('page two ionViewWillEnter');
|
||||
setInterval(() => {
|
||||
this.ionViewWillEnterDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
ionViewDidEnter() {
|
||||
console.log('page two ionViewDidEnter');
|
||||
setInterval(() => {
|
||||
this.ionViewDidEnterDetection = '' + Date.now();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
goNext() {
|
||||
const nav = document.querySelector('ion-nav') as any;
|
||||
nav.push(PageThree).then(() => console.log('push complete'));
|
||||
}
|
||||
|
||||
goBack() {
|
||||
const nav = document.querySelector('ion-nav') as any;
|
||||
nav.pop().then(() => console.log('pop complete'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user