motion projection mode is optional and sensitive for the config
This commit is contained in:
parent
a9de755047
commit
384597afb9
@ -19,6 +19,7 @@ import { BaseModel } from 'app/shared/models/base/base-model';
|
|||||||
import { ViewModelStoreService } from './view-model-store.service';
|
import { ViewModelStoreService } from './view-model-store.service';
|
||||||
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ConfigService } from '../ui-services/config.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service cares about Projectables being projected and manage all projection-related
|
* This service cares about Projectables being projected and manage all projection-related
|
||||||
@ -41,7 +42,8 @@ export class ProjectorService {
|
|||||||
private http: HttpService,
|
private http: HttpService,
|
||||||
private slideManager: SlideManager,
|
private slideManager: SlideManager,
|
||||||
private viewModelStore: ViewModelStoreService,
|
private viewModelStore: ViewModelStoreService,
|
||||||
private translate: TranslateService
|
private translate: TranslateService,
|
||||||
|
private configService: ConfigService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +56,7 @@ export class ProjectorService {
|
|||||||
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
|
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
|
||||||
): IdentifiableProjectorElement {
|
): IdentifiableProjectorElement {
|
||||||
if (isProjectable(obj)) {
|
if (isProjectable(obj)) {
|
||||||
return obj.getSlide().getBasicProjectorElement({});
|
return obj.getSlide(this.configService).getBasicProjectorElement({});
|
||||||
} else if (isProjectorElementBuildDeskriptor(obj)) {
|
} else if (isProjectorElementBuildDeskriptor(obj)) {
|
||||||
return obj.getBasicProjectorElement({});
|
return obj.getBasicProjectorElement({});
|
||||||
} else {
|
} else {
|
||||||
|
@ -69,8 +69,11 @@ export class ViewModelStoreService {
|
|||||||
* @param callback The function to check
|
* @param callback The function to check
|
||||||
* @returns all matched view models of the collection
|
* @returns all matched view models of the collection
|
||||||
*/
|
*/
|
||||||
public filter<T extends BaseViewModel>(collectionString: string, callback: (model: T) => boolean): T[] {
|
public filter<T extends BaseViewModel>(
|
||||||
return this.getAll<T>(collectionString).filter(callback);
|
collectionType: ViewModelConstructor<T> | string,
|
||||||
|
callback: (model: T) => boolean
|
||||||
|
): T[] {
|
||||||
|
return this.getAll<T>(collectionType).filter(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,7 +83,10 @@ export class ViewModelStoreService {
|
|||||||
* @param callback THe callback to satisfy
|
* @param callback THe callback to satisfy
|
||||||
* @returns a found view model or null, if nothing was found.
|
* @returns a found view model or null, if nothing was found.
|
||||||
*/
|
*/
|
||||||
public find<T extends BaseViewModel>(collectionString: string, callback: (model: T) => boolean): T {
|
public find<T extends BaseViewModel>(
|
||||||
return this.getAll<T>(collectionString).find(callback);
|
collectionType: ViewModelConstructor<T> | string,
|
||||||
|
callback: (model: T) => boolean
|
||||||
|
): T {
|
||||||
|
return this.getAll<T>(collectionType).find(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,7 @@ _('Amendment to');
|
|||||||
_('Statute amendment for');
|
_('Statute amendment for');
|
||||||
_('Creation date');
|
_('Creation date');
|
||||||
_('Last modified');
|
_('Last modified');
|
||||||
|
_('Which version?');
|
||||||
|
|
||||||
// motion workflow 1
|
// motion workflow 1
|
||||||
_('Simple Workflow');
|
_('Simple Workflow');
|
||||||
|
@ -88,7 +88,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
Item,
|
Item,
|
||||||
MotionBlock,
|
MotionBlock,
|
||||||
Mediafile,
|
Mediafile,
|
||||||
Tag
|
Tag,
|
||||||
|
MotionChangeRecommendation
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,6 +149,10 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
const attachments = this.viewModelStoreService.getMany(ViewMediafile, motion.attachments_id);
|
const attachments = this.viewModelStoreService.getMany(ViewMediafile, motion.attachments_id);
|
||||||
const tags = this.viewModelStoreService.getMany(ViewTag, motion.tags_id);
|
const tags = this.viewModelStoreService.getMany(ViewTag, motion.tags_id);
|
||||||
const parent = this.viewModelStoreService.get(ViewMotion, motion.parent_id);
|
const parent = this.viewModelStoreService.get(ViewMotion, motion.parent_id);
|
||||||
|
const changeRecommendations = this.viewModelStoreService.filter(
|
||||||
|
ViewMotionChangeRecommendation,
|
||||||
|
cr => cr.motion_id === motion.id
|
||||||
|
);
|
||||||
let state: WorkflowState = null;
|
let state: WorkflowState = null;
|
||||||
if (workflow) {
|
if (workflow) {
|
||||||
state = workflow.getStateById(motion.state_id);
|
state = workflow.getStateById(motion.state_id);
|
||||||
@ -163,7 +168,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
|||||||
block,
|
block,
|
||||||
attachments,
|
attachments,
|
||||||
tags,
|
tags,
|
||||||
parent
|
parent,
|
||||||
|
changeRecommendations
|
||||||
);
|
);
|
||||||
viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion);
|
viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion);
|
||||||
viewMotion.getTitle = () => this.getTitle(viewMotion);
|
viewMotion.getTitle = () => this.getTitle(viewMotion);
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
ProjectionDialogReturnType
|
ProjectionDialogReturnType
|
||||||
} from 'app/shared/components/projection-dialog/projection-dialog.component';
|
} from 'app/shared/components/projection-dialog/projection-dialog.component';
|
||||||
import { ProjectorService } from '../core-services/projector.service';
|
import { ProjectorService } from '../core-services/projector.service';
|
||||||
|
import { ConfigService } from './config.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the projection dialog. Projects the result of the user's choice.
|
* Manages the projection dialog. Projects the result of the user's choice.
|
||||||
@ -21,7 +22,11 @@ export class ProjectionDialogService {
|
|||||||
* @param dialog
|
* @param dialog
|
||||||
* @param projectorService
|
* @param projectorService
|
||||||
*/
|
*/
|
||||||
public constructor(private dialog: MatDialog, private projectorService: ProjectorService) {}
|
public constructor(
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private projectorService: ProjectorService,
|
||||||
|
private configService: ConfigService
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the projection dialog for the given projectable. After the user's choice,
|
* Opens the projection dialog for the given projectable. After the user's choice,
|
||||||
@ -32,7 +37,7 @@ export class ProjectionDialogService {
|
|||||||
public async openProjectDialogFor(obj: Projectable | ProjectorElementBuildDeskriptor): Promise<void> {
|
public async openProjectDialogFor(obj: Projectable | ProjectorElementBuildDeskriptor): Promise<void> {
|
||||||
let descriptor: ProjectorElementBuildDeskriptor;
|
let descriptor: ProjectorElementBuildDeskriptor;
|
||||||
if (isProjectable(obj)) {
|
if (isProjectable(obj)) {
|
||||||
descriptor = obj.getSlide();
|
descriptor = obj.getSlide(this.configService);
|
||||||
} else {
|
} else {
|
||||||
descriptor = obj;
|
descriptor = obj;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
h2 {
|
h2 {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
.element-name {
|
.element-name {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { Projectable, ProjectorElementBuildDeskriptor } from './projectable';
|
import { Projectable, ProjectorElementBuildDeskriptor } from './projectable';
|
||||||
import { BaseViewModel } from './base-view-model';
|
import { BaseViewModel } from './base-view-model';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base view class for projectable models.
|
* Base view class for projectable models.
|
||||||
*/
|
*/
|
||||||
export abstract class BaseProjectableViewModel extends BaseViewModel implements Projectable {
|
export abstract class BaseProjectableViewModel extends BaseViewModel implements Projectable {
|
||||||
public abstract getSlide(): ProjectorElementBuildDeskriptor;
|
public abstract getSlide(configService?: ConfigService): ProjectorElementBuildDeskriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the projector title used for managing projector elements.
|
* @returns the projector title used for managing projector elements.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Displayable } from 'app/site/base/displayable';
|
import { Displayable } from 'app/site/base/displayable';
|
||||||
import { IdentifiableProjectorElement, ProjectorElementOptions } from 'app/shared/models/core/projector';
|
import { IdentifiableProjectorElement, ProjectorElementOptions } from 'app/shared/models/core/projector';
|
||||||
import { SlideOptions } from './slide-options';
|
import { SlideOptions } from './slide-options';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
|
||||||
export function isProjectorElementBuildDeskriptor(obj: any): obj is ProjectorElementBuildDeskriptor {
|
export function isProjectorElementBuildDeskriptor(obj: any): obj is ProjectorElementBuildDeskriptor {
|
||||||
const deskriptor = <ProjectorElementBuildDeskriptor>obj;
|
const deskriptor = <ProjectorElementBuildDeskriptor>obj;
|
||||||
@ -35,5 +36,5 @@ export function isProjectable(obj: any): obj is Projectable {
|
|||||||
* Interface for every model, that should be projectable.
|
* Interface for every model, that should be projectable.
|
||||||
*/
|
*/
|
||||||
export interface Projectable extends Displayable {
|
export interface Projectable extends Displayable {
|
||||||
getSlide(): ProjectorElementBuildDeskriptor;
|
getSlide(configSerice?: ConfigService): ProjectorElementBuildDeskriptor;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ import { ViewWorkflow } from './view-workflow';
|
|||||||
import { ViewCategory } from './view-category';
|
import { ViewCategory } from './view-category';
|
||||||
import { ViewMotionBlock } from './view-motion-block';
|
import { ViewMotionBlock } from './view-motion-block';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { ViewMotionChangeRecommendation } from './view-change-recommendation';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The line numbering mode for the motion detail view.
|
* The line numbering mode for the motion detail view.
|
||||||
@ -58,6 +60,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
protected _attachments: ViewMediafile[];
|
protected _attachments: ViewMediafile[];
|
||||||
protected _tags: ViewTag[];
|
protected _tags: ViewTag[];
|
||||||
protected _parent: ViewMotion;
|
protected _parent: ViewMotion;
|
||||||
|
protected _changeRecommendations: ViewMotionChangeRecommendation[];
|
||||||
public personalNote: PersonalNoteContent;
|
public personalNote: PersonalNoteContent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,6 +160,10 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get changeRecommendations(): ViewMotionChangeRecommendation[] {
|
||||||
|
return this._changeRecommendations;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the current state of thw workflow is final
|
* Checks if the current state of thw workflow is final
|
||||||
*
|
*
|
||||||
@ -356,7 +363,8 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
block?: ViewMotionBlock,
|
block?: ViewMotionBlock,
|
||||||
attachments?: ViewMediafile[],
|
attachments?: ViewMediafile[],
|
||||||
tags?: ViewTag[],
|
tags?: ViewTag[],
|
||||||
parent?: ViewMotion
|
parent?: ViewMotion,
|
||||||
|
changeRecommendations?: ViewMotionChangeRecommendation[]
|
||||||
) {
|
) {
|
||||||
super(Motion.COLLECTIONSTRING);
|
super(Motion.COLLECTIONSTRING);
|
||||||
this._motion = motion;
|
this._motion = motion;
|
||||||
@ -370,6 +378,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
this._attachments = attachments;
|
this._attachments = attachments;
|
||||||
this._tags = tags;
|
this._tags = tags;
|
||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
|
this._changeRecommendations = changeRecommendations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAgendaItem(): ViewItem {
|
public getAgendaItem(): ViewItem {
|
||||||
@ -429,6 +438,8 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
this.updateTags(update);
|
this.updateTags(update);
|
||||||
} else if (update instanceof ViewMotion && update.id !== this.id) {
|
} else if (update instanceof ViewMotion && update.id !== this.id) {
|
||||||
this.updateParent(update);
|
this.updateParent(update);
|
||||||
|
} else if (update instanceof ViewMotionChangeRecommendation) {
|
||||||
|
this.updateChangeRecommendation(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +448,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
*
|
*
|
||||||
* @param workflow potentially the (changed workflow (state). Needs manual verification
|
* @param workflow potentially the (changed workflow (state). Needs manual verification
|
||||||
*/
|
*/
|
||||||
public updateWorkflow(workflow: ViewWorkflow): void {
|
private updateWorkflow(workflow: ViewWorkflow): void {
|
||||||
if (workflow.id === this.motion.workflow_id) {
|
if (workflow.id === this.motion.workflow_id) {
|
||||||
this._workflow = workflow;
|
this._workflow = workflow;
|
||||||
this._state = workflow.getStateById(this.state_id);
|
this._state = workflow.getStateById(this.state_id);
|
||||||
@ -449,7 +460,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
*
|
*
|
||||||
* @param category potentially the changed category. Needs manual verification
|
* @param category potentially the changed category. Needs manual verification
|
||||||
*/
|
*/
|
||||||
public updateCategory(category: ViewCategory): void {
|
private updateCategory(category: ViewCategory): void {
|
||||||
if (this.category_id && category.id === this.motion.category_id) {
|
if (this.category_id && category.id === this.motion.category_id) {
|
||||||
this._category = category;
|
this._category = category;
|
||||||
}
|
}
|
||||||
@ -460,7 +471,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
*
|
*
|
||||||
* @param item potentially the changed agenda Item. Needs manual verification
|
* @param item potentially the changed agenda Item. Needs manual verification
|
||||||
*/
|
*/
|
||||||
public updateItem(item: ViewItem): void {
|
private updateItem(item: ViewItem): void {
|
||||||
if (item.id === this.motion.agenda_item_id) {
|
if (item.id === this.motion.agenda_item_id) {
|
||||||
this._item = item;
|
this._item = item;
|
||||||
}
|
}
|
||||||
@ -471,7 +482,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
*
|
*
|
||||||
* @param block potentially the changed motion block. Needs manual verification
|
* @param block potentially the changed motion block. Needs manual verification
|
||||||
*/
|
*/
|
||||||
public updateMotionBlock(block: ViewMotionBlock): void {
|
private updateMotionBlock(block: ViewMotionBlock): void {
|
||||||
if (this.motion_block_id && block.id === this.motion.motion_block_id) {
|
if (this.motion_block_id && block.id === this.motion.motion_block_id) {
|
||||||
this._block = block;
|
this._block = block;
|
||||||
}
|
}
|
||||||
@ -482,7 +493,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
*
|
*
|
||||||
* @param update potentially the changed agenda Item. Needs manual verification
|
* @param update potentially the changed agenda Item. Needs manual verification
|
||||||
*/
|
*/
|
||||||
public updateUser(update: ViewUser): void {
|
private updateUser(update: ViewUser): void {
|
||||||
if (this.motion.submitters && this.motion.submitters.find(user => user.user_id === update.id)) {
|
if (this.motion.submitters && this.motion.submitters.find(user => user.user_id === update.id)) {
|
||||||
const userIndex = this.motion.submitters.findIndex(submitter => submitter.user_id === update.id);
|
const userIndex = this.motion.submitters.findIndex(submitter => submitter.user_id === update.id);
|
||||||
this.submitters[userIndex] = update;
|
this.submitters[userIndex] = update;
|
||||||
@ -502,7 +513,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
*
|
*
|
||||||
* @param mediafile
|
* @param mediafile
|
||||||
*/
|
*/
|
||||||
public updateAttachments(mediafile: ViewMediafile): void {
|
private updateAttachments(mediafile: ViewMediafile): void {
|
||||||
if (this.attachments_id && this.attachments_id.includes(mediafile.id)) {
|
if (this.attachments_id && this.attachments_id.includes(mediafile.id)) {
|
||||||
const attachmentIndex = this.attachments.findIndex(_mediafile => _mediafile.id === mediafile.id);
|
const attachmentIndex = this.attachments.findIndex(_mediafile => _mediafile.id === mediafile.id);
|
||||||
if (attachmentIndex < 0) {
|
if (attachmentIndex < 0) {
|
||||||
@ -513,7 +524,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateTags(tag: ViewTag): void {
|
private updateTags(tag: ViewTag): void {
|
||||||
if (this.tags_id && this.tags_id.includes(tag.id)) {
|
if (this.tags_id && this.tags_id.includes(tag.id)) {
|
||||||
const tagIndex = this.tags.findIndex(_tag => _tag.id === tag.id);
|
const tagIndex = this.tags.findIndex(_tag => _tag.id === tag.id);
|
||||||
if (tagIndex < 0) {
|
if (tagIndex < 0) {
|
||||||
@ -524,12 +535,23 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateParent(parent: ViewMotion): void {
|
private updateParent(parent: ViewMotion): void {
|
||||||
if (this.parent_id && this.parent_id === parent.id) {
|
if (this.parent_id && this.parent_id === parent.id) {
|
||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateChangeRecommendation(cr: ViewMotionChangeRecommendation): void {
|
||||||
|
if (cr.motion_id === this.id) {
|
||||||
|
const index = this.changeRecommendations.findIndex(_cr => _cr.id === cr.id);
|
||||||
|
if (index < 0) {
|
||||||
|
this.changeRecommendations.push(cr);
|
||||||
|
} else {
|
||||||
|
this.changeRecommendations[index] = cr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public hasSupporters(): boolean {
|
public hasSupporters(): boolean {
|
||||||
return !!(this.supporters && this.supporters.length > 0);
|
return !!(this.supporters && this.supporters.length > 0);
|
||||||
}
|
}
|
||||||
@ -561,26 +583,30 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
return this.amendment_paragraphs.length > 0;
|
return this.amendment_paragraphs.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSlide(): ProjectorElementBuildDeskriptor {
|
public getSlide(configService: ConfigService): ProjectorElementBuildDeskriptor {
|
||||||
return {
|
const slideOptions = [];
|
||||||
getBasicProjectorElement: options => ({
|
|
||||||
name: Motion.COLLECTIONSTRING,
|
if (this.changeRecommendations && this.changeRecommendations.length) {
|
||||||
id: this.id,
|
slideOptions.push({
|
||||||
getIdentifiers: () => ['name', 'id']
|
|
||||||
}),
|
|
||||||
slideOptions: [
|
|
||||||
{
|
|
||||||
key: 'mode',
|
key: 'mode',
|
||||||
displayName: 'Change recommendations',
|
displayName: 'Which version?',
|
||||||
default: 'original',
|
default: configService.instant('motions_recommendation_text_mode'),
|
||||||
choices: [
|
choices: [
|
||||||
{ value: 'original', displayName: 'Original version' },
|
{ value: 'original', displayName: 'Original version' },
|
||||||
{ value: 'changed', displayName: 'Changed version' },
|
{ value: 'changed', displayName: 'Changed version' },
|
||||||
{ value: 'diff', displayName: 'Diff version' },
|
{ value: 'diff', displayName: 'Diff version' },
|
||||||
{ value: 'agreed', displayName: 'Final version' }
|
{ value: 'agreed', displayName: 'Final version' }
|
||||||
]
|
]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
],
|
|
||||||
|
return {
|
||||||
|
getBasicProjectorElement: options => ({
|
||||||
|
name: Motion.COLLECTIONSTRING,
|
||||||
|
id: this.id,
|
||||||
|
getIdentifiers: () => ['name', 'id']
|
||||||
|
}),
|
||||||
|
slideOptions: slideOptions,
|
||||||
projectionDefaultName: 'motions',
|
projectionDefaultName: 'motions',
|
||||||
getDialogTitle: this.getAgendaTitle
|
getDialogTitle: this.getAgendaTitle
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user